Merge "Bump CTS and CTS Verifier to 12_R1" into sc-dev
diff --git a/apps/CameraITS/CameraITS.pdf b/apps/CameraITS/CameraITS.pdf
index 8572f8b..431755a 100644
--- a/apps/CameraITS/CameraITS.pdf
+++ b/apps/CameraITS/CameraITS.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index 97d2e78..f71ca0a 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -63,6 +63,8 @@
_NAME = os.path.splitext(os.path.basename(__file__))[0]
_NUM_ROTATIONS = 10
+_START_FRAME = 1
+_FRAME_DELTA_TOL = 1.5 # 50% margin over nominal FPS of captures
# Constants to convert between different units (for clarity).
_SEC_TO_MSEC = 1000.0
@@ -215,7 +217,7 @@
'%s_gyro_events.png' % (os.path.join(log_path, _NAME)))
-def _get_cam_times(cam_events):
+def _get_cam_times(cam_events, fps):
"""Get the camera frame times.
Assign a time to each frame. Assumes the image is instantly captured in the
@@ -224,14 +226,26 @@
Args:
cam_events: List of (start_exposure, exposure_time, readout_duration)
tuples, one per captured frame, with times in nanoseconds.
+ fps: float of frames per second value
Returns:
frame_times: Array of N times, one corresponding to the 'middle' of the
exposure of each frame.
"""
starts = np.array([start for start, exptime, readout in cam_events])
+ max_frame_delta_ms = (np.amax(np.subtract(starts[1:], starts[0:-1])) /
+ _MSEC_TO_NSEC)
+ logging.debug('Maximum frame delta: %.3f ms', max_frame_delta_ms)
+ frame_delta_tol_ms = _FRAME_DELTA_TOL * (1 / fps) * _SEC_TO_MSEC
+ if max_frame_delta_ms > frame_delta_tol_ms:
+ raise AssertionError(f'Frame drop! Max delta: {max_frame_delta_ms:.3f}ms, '
+ f'ATOL: {frame_delta_tol_ms}ms')
exptimes = np.array([exptime for start, exptime, readout in cam_events])
+ if not np.all(exptimes == exptimes[0]):
+ raise AssertionError(f'Exposure times vary in frames! {exptimes}')
readouts = np.array([readout for start, exptime, readout in cam_events])
+ if not np.all(readouts == readouts[0]):
+ raise AssertionError(f'Rolling shutter not always equal! {readouts}')
frame_times = starts + (exptimes + readouts) / 2.0
return frame_times
@@ -283,6 +297,7 @@
frame = (frame * 255.0).astype(np.uint8) # cv2 uses [0, 255]
gframes.append(cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY))
num_frames = len(gframes)
+ logging.debug('num_frames: %d', num_frames)
# create mask
ymin = int(h * (1 - _FEATURE_MARGIN) / 2)
@@ -294,7 +309,8 @@
logging.debug('Using %s masking method', masking)
rots = []
for i in range(1, num_frames):
- gframe0 = gframes[i-1]
+ j = i - 1
+ gframe0 = gframes[j]
gframe1 = gframes[i]
if masking == 'post':
p0 = cv2.goodFeaturesToTrack(
@@ -308,10 +324,10 @@
if num_features < _FEATURE_PTS_MIN:
for pt in p0_filtered:
x, y = pt[0][0], pt[0][1]
- cv2.circle(frames[i-1], (x, y), 3, (100, 255, 255), -1)
+ cv2.circle(frames[j], (x, y), 3, (100, 255, 255), -1)
image_processing_utils.write_image(
- frames[i-1], f'{file_name_stem}_features{i-1:03d}.png')
- msg = (f'Not enough features in frame {i-1}. Need at least '
+ frames[j], f'{file_name_stem}_features{j+_START_FRAME:03d}.png')
+ msg = (f'Not enough features in frame {j+_START_FRAME}. Need at least '
f'{_FEATURE_PTS_MIN} features, got {num_features}.')
if masking == 'pre':
raise AssertionError(msg)
@@ -320,7 +336,7 @@
break
else:
logging.debug('Number of features in frame %s is %d',
- str(i - 1).zfill(3), num_features)
+ str(j+_START_FRAME).zfill(3), num_features)
p1, st, _ = cv2.calcOpticalFlowPyrLK(gframe0, gframe1, p0_filtered, None,
**_CV2_LK_PARAMS)
tform = _procrustes_rotation(p0_filtered[st == 1], p1[st == 1])
@@ -334,13 +350,16 @@
if i == 1:
# Save debug visualization of features that are being
# tracked in the first frame.
- frame = frames[i-1]
+ frame = frames[j]
for x, y in p0_filtered[st == 1]:
- cv2.circle(frame, (x, y), 3, (100, 100, 255), -1)
+ cv2.circle(frame, (x, y), 3, (100, 255, 255), -1)
image_processing_utils.write_image(
- frame, f'{file_name_stem}_features000.png')
- if i == len(rots):
+ frame, f'{file_name_stem}_features{j+_START_FRAME:03d}.png')
+ if i == num_frames-1:
+ logging.debug('Correct num of frames found: %d', i)
break # exit if enough features in all frames
+ if i != num_frames-1:
+ raise AssertionError('Neither method found enough features in all frames')
rots = np.array(rots)
rot_per_frame_max = max(abs(rots))
@@ -500,16 +519,19 @@
rot_rig['ch'] = self.rotator_ch
events, frames = _collect_data(cam, fps, img_w, img_h, test_length,
rot_rig, chart_distance, log_path)
+ logging.debug('Start frame: %d', _START_FRAME)
_plot_gyro_events(events['gyro'], log_path)
# Validity check on gyro/camera timestamps
- cam_times = _get_cam_times(events['cam'])
+ cam_times = _get_cam_times(
+ events['cam'][_START_FRAME:len(events['cam'])], fps)
gyro_times = [e['time'] for e in events['gyro']]
self._assert_gyro_encompasses_camera(cam_times, gyro_times)
# Compute cam rotation displacement(rads) between pairs of adjacent frames.
- cam_rots = _get_cam_rotations(frames, events['facing'], img_h, log_path)
+ cam_rots = _get_cam_rotations(
+ frames[_START_FRAME:len(frames)], events['facing'], img_h, log_path)
logging.debug('cam_rots: %s', str(cam_rots))
gyro_rots = sensor_fusion_utils.get_gyro_rotations(
events['gyro'], cam_times)
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 0fa27ea..a01a8e2 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -4557,34 +4557,6 @@
<meta-data android:name="test_required_configs"
android:value="config_hdmi_source" />
</activity>
- <activity android:name=".tv.display.DisplayHdrCapabilitiesTestActivity"
- android:label="@string/tv_hdr_capabilities_test"
- android:exported="true"
- 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" />
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- </activity>
- <activity android:name=".tv.display.DisplayModesTestActivity"
- android:label="@string/tv_display_modes_test"
- android:exported="true"
- 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"/>
- <meta-data android:name="display_mode"
- android:value="multi_display_mode" />
- </activity>
<activity android:name=".screenpinning.ScreenPinningTestActivity"
android:exported="true"
diff --git a/apps/CtsVerifier/res/layout/dialog_test_list_activity.xml b/apps/CtsVerifier/res/layout/dialog_test_list_activity.xml
new file mode 100644
index 0000000..6630d2d
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/dialog_test_list_activity.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2011 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="2">
+ <TextView
+ android:id="@+id/test_instructions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:textSize="18dip"/>
+ </ScrollView>
+ <ListView
+ android:id="@+id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="3"/>
+ <include layout="@layout/pass_fail_buttons"/>
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 732f57a..aa5e08a 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -4086,6 +4086,7 @@
Use the Back button to return to this page.
</string>
<string name="device_owner_disallow_remove_user_create_user">Create uninitialized user</string>
+ <string name="device_owner_remove_secondary_user">Remove uninitialized user</string>
<string name="disallow_remove_managed_profile">Disallow remove managed profile</string>
<string name="disallow_remove_managed_profile_action">Removing the work profile. It shouldn\'t be possible neither from the Accounts screen nor the Device Administrators screen (after selecting the Device Administrator that corresponds to the badged version of \"CTS Verifier\")</string>
<string name="disallow_share_location">Disallow share location</string>
@@ -4116,17 +4117,6 @@
</string>
<string name="set_maximum_time_to_lock_action">Selecting maximum time to lock greater than the set value below</string>
<string name="set_maximum_time_to_lock_widget_label">Set maximum time to lock (in Sec):</string>
- <string name="set_password_quality">Set password quality</string>
- <string name="password_quality_set_step">Set minimum password quality by selecting an option in the spinner below.</string>
- <string name="set_password_quality_action">Setting a password which does not meet the requirements of the password quality selected</string>
- <string name="set_password_quality_widget_label">@string/set_password_quality</string>
- <string name="password_quality_unspecified">Unspecified</string>
- <string name="password_quality_something">Something</string>
- <string name="password_quality_numeric">Numeric</string>
- <string name="password_quality_numeric_complex">Numeric (Complex)</string>
- <string name="password_quality_alphabetic">Alphabetic</string>
- <string name="password_quality_alphanumeric">Alphanumeric</string>
- <string name="password_quality_complex">Complex</string>
<string name="set_permitted_accessibility_services">Set permitted accessibility services</string>
<string name="permitted_accessibility_services_set_step">
Check that \'Test Accessibility service\' is not enabled in Settings and disallow \'Test Accessibility service\' from permitted accessibility services by turning on
@@ -4465,6 +4455,32 @@
</string>
<string name="device_owner_enable_usb_data_signaling_test">Enable USB data signaling</string>
+ <string name="device_owner_required_password_complexity_test">Set required password complexity</string>
+ <string name="device_owner_required_password_complexity_test_info">
+ This test verifies that required password complexity can be set by the device owner.\n
+ 1. Press the \"Set low required password complexity\" button.\n
+ 2. Press the \"Go\" button to go to Settings > Security > Screen lock. Verify that
+ you have to set a pattern, PIN or password.\n
+ 3. Press the \"Set medium required password complexity\" button.\n
+ 4. Press the \"Go\" button to go to Settings > Security > Screen lock. Verify that
+ you have to set a PIN without repeating (4444) or ordered (1234, 4321, 2468) sequences
+ with a length of at least 4; or you have to set an alphabetic or alphanumeric password with
+ a length of at least 4.\n
+ 5. Press the \"Set high required password complexity\" button.\n
+ 6. Press the \"Go\" button to go to Settings > Security > Screen lock. Verify that
+ you have to set a PIN without repeating (4444) or ordered (1234, 4321, 2468) sequences
+ with a length of at least 8; or you have to set an alphabetic or alphanumeric password with
+ a length of at least 6.\n
+ 7. Press the \"Remove required password complexity\" button.\n
+ 8. Press the \"Go\" button to go to Settings > Security > Screen lock. Verify that
+ you don\'t have to set a pattern, PIN or password.\n
+ Please mark the test accordingly.
+ </string>
+ <string name="set_low_required_password_complexity">Set low required password complexity</string>
+ <string name="set_medium_required_password_complexity">Set medium required password complexity</string>
+ <string name="set_high_required_password_complexity">Set high required password complexity</string>
+ <string name="remove_required_password_complexity">Remove required password complexity</string>
+
<string name="managed_user_test">Managed User</string>
<string name="managed_user_positive_tests">Managed User positive tests</string>
<string name="managed_user_positive_tests_instructions">
@@ -4761,58 +4777,9 @@
soundbars which are connected. </string>
<string name="tv_audio_capabilities_receiver_connected">Connect a receiver or
soundbar which can play Dolby Atmos. </string>
- <string name="tv_audio_capabilities_atmos_supported">Does your Android TV device support Dolby Atmos? </string>
+ <string name="tv_audio_capabilities_atmos_supported">Does your Android TV device support Dolby
+ Atmos (either passthrough or decode)? </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>
- <string name="tv_hdr_capabilities_test_step_no_display">No Display</string>
- <string name="tv_hdr_capabilities_test_step_non_hdr_display">Non HDR Display</string>
- <string name="tv_hdr_capabilities_test_info">This test checks if
- Display.getHdrCapabilities correctly reports the HDR capabilities of the display.
- </string>
- <string name="tv_hdr_connect_no_hdr_display">Connect a non-HDR display and then
- press the "%s" button, below.
- </string>
- <string name="tv_hdr_connect_hdr_display">Connect an HDR display and press
- the "%s" button, below.
- </string>
- <string name="tv_hdr_disconnect_display">Press the "%1$s" button
- and disconnect the display within %2$d seconds. Wait at least %3$d seconds and then
- reconnect the display.
- </string>
- <string name="tv_panel_hdr_types_reported_are_supported">
- The supported HDR types are: %s\nAre all of them supported by the hardware?
- </string>
- <string name="tv_panel_hdr_types_supported_are_reported">
- Are there other HDR types which are supported by the hardware, but are not listed above?
- </string>
-
- <!-- Display Modes Test -->
- <string name="tv_display_modes_test">Display Modes Test</string>
- <string name="tv_display_modes_test_info">This test checks if Display.getSupportedModes()
- and Display.getMode() are correctly reporting the supported screen modes.
- </string>
- <string name="tv_display_modes_disconnect_display">
- Press the "%1$s" button and disconnect the display within %2$d seconds. Wait at least %3$d
- seconds and then reconnect the display.
- </string>
- <string name="tv_display_modes_test_step_no_display">No Display</string>
- <string name="tv_display_modes_test_step_1080p">1080p Display</string>
- <string name="tv_display_modes_test_step_2160p">2160p Display</string>
- <string name="tv_display_modes_start_test_button">Start Test</string>
- <string name="tv_display_modes_connect_2160p_display">
- Connect a 2160p display and press the "%s" button, below.
- </string>
- <string name="tv_display_modes_connect_1080p_display">
- Connect a 1080p display and press the "%s" button, below.
- </string>
- <string name="tv_panel_display_modes_reported_are_supported">
- The supported display modes are:\n%s\n\nAre all of the above display modes supported by the hardware?
- </string>
- <string name="tv_panel_display_modes_supported_are_reported">
- Are there other modes which are supported by the hardware, but are not listed above?
- </string>
<string name="overlay_view_text">Overlay View Dummy Text</string>
<string name="custom_rating">Example of input app specific custom rating.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
index bed5a77..9e8f8d7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
@@ -32,15 +32,17 @@
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
+import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
-import com.android.cts.verifier.R;
+import androidx.annotation.Nullable;
/**
* Test list activity that supports showing dialogs with pass/fail buttons instead of
* starting new activities.
- * In addition to that dialogs have a 'go' button that can be configured to launch an intent.
+ *
+ * <p>In addition to that dialogs have a 'go' button that can be configured to launch an intent.
* Instructions are shown on top of the screen and a test preparation button is provided.
*/
public abstract class DialogTestListActivity extends PassFailButtons.TestListActivity {
@@ -50,11 +52,29 @@
private final int mInfoStringId;
private final int mInstructionsStringId;
- protected Button mPrepareTestButton;
+ /**
+ * The button to prepare the test, populated if the layout ID used to construct the object
+ * contains a {@code prepare_test_button} {@link Button}.
+ */
+ @Nullable protected Button mPrepareTestButton;
+
protected ListView mTestFeaturesList;
protected int mCurrentTestPosition;
+ /**
+ * Constructs the activity with the given resources.
+ *
+ * @param layoutId The layout to inflate, which must contain a {@link TextView} with ID {@code
+ * test_instructions} for the test instructions within a {@link ScrollView}, a {@link ListView}
+ * with ID {@code android:list} for the tests, and must include the {@code pass_fail_buttons}
+ * layout. If a custom layout is not required, {@code dialog_test_list_activity} can be used.
+ * @param titleStringId The string resource to use for the title at the top of the screen.
+ * @param infoStringId The string resource to use for the info button between the pass and fail
+ * buttons.
+ * @param instructionsStringId The string resource to use for the explanation that populates the
+ * {@link TextView} with ID {@code test_instructions} in the given layout.
+ */
protected DialogTestListActivity(int layoutId, int titleStringId, int infoStringId,
int instructionsStringId) {
mLayoutId = layoutId;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 967eb03..168ba1e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -75,7 +75,6 @@
public static final String COMMAND_SET_GLOBAL_SETTING =
"set-global-setting";
public static final String COMMAND_SET_MAXIMUM_TO_LOCK = "set-maximum-time-to-lock";
- public static final String COMMAND_SET_PASSWORD_QUALITY = "set-password-quality";
public static final String COMMAND_SET_KEYGUARD_DISABLED = "set-keyguard-disabled";
public static final String COMMAND_SET_LOCK_SCREEN_INFO = "set-lock-screen-info";
public static final String COMMAND_SET_STATUSBAR_DISABLED = "set-statusbar-disabled";
@@ -117,12 +116,15 @@
public static final String COMMAND_CREATE_MANAGED_USER = "create-managed-user";
public static final String COMMAND_CREATE_MANAGED_USER_WITHOUT_SETUP =
"create-managed-user-without-setup";
+ public static final String COMMAND_REMOVE_SECONDARY_USERS = "remove-secondary-users";
public static final String COMMAND_WITH_USER_SWITCHER_MESSAGE = "with-user-switcher-message";
public static final String COMMAND_WITHOUT_USER_SWITCHER_MESSAGE =
"without-user-switcher-message";
public static final String COMMAND_ENABLE_LOGOUT = "enable-logout";
public static final String COMMAND_DISABLE_USB_DATA_SIGNALING = "disable-usb-data-signaling";
public static final String COMMAND_ENABLE_USB_DATA_SIGNALING = "enable-usb-data-signaling";
+ public static final String COMMAND_SET_REQUIRED_PASSWORD_COMPLEXITY =
+ "set-required-password-complexity";
public static final String EXTRA_USER_RESTRICTION =
"com.android.cts.verifier.managedprovisioning.extra.USER_RESTRICTION";
@@ -237,10 +239,6 @@
mDpm.setMaximumTimeToLock(mAdmin,
TimeUnit.SECONDS.toMillis(timeInSeconds) /* in milliseconds */);
} break;
- case COMMAND_SET_PASSWORD_QUALITY: {
- int quality = intent.getIntExtra(EXTRA_VALUE, 0);
- mDpm.setPasswordQuality(mAdmin, quality);
- } break;
case COMMAND_SET_KEYGUARD_DISABLED: {
boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
KeyguardManager km = this.getSystemService(KeyguardManager.class);
@@ -513,6 +511,14 @@
extras.putBoolean(DeviceAdminTestReceiver.EXTRA_MANAGED_USER_TEST, true);
mDpm.createAndManageUser(mAdmin, "managed user", mAdmin, extras, /* flags */ 0);
} break;
+ case COMMAND_REMOVE_SECONDARY_USERS: {
+ if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+ return;
+ }
+ for (UserHandle secondaryUser : mDpm.getSecondaryUsers(mAdmin)) {
+ mDpm.removeUser(mAdmin, secondaryUser);
+ }
+ } break;
case COMMAND_WITH_USER_SWITCHER_MESSAGE: {
createAndSwitchUserWithMessage("Start user session", "End user session");
} break;
@@ -537,6 +543,11 @@
mDpm.setUsbDataSignalingEnabled(true);
break;
}
+ case COMMAND_SET_REQUIRED_PASSWORD_COMPLEXITY: {
+ int complexity = intent.getIntExtra(EXTRA_VALUE,
+ DevicePolicyManager.PASSWORD_COMPLEXITY_NONE);
+ mDpm.setRequiredPasswordComplexity(complexity);
+ }
}
} catch (Exception e) {
Log.e(TAG, "Failed to execute command: " + intent, e);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index c6359eb..3b912e1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -88,6 +88,8 @@
private static final String DISALLOW_AMBIENT_DISPLAY_ID = "DISALLOW_AMBIENT_DISPLAY";
private static final String DISALLOW_REMOVE_USER_TEST_ID = "DISALLOW_REMOVE_USER";
private static final String DISABLE_USB_DATA_SIGNALING_TEST_ID = "DISABLE_USB_DATA_SIGNALING";
+ private static final String SET_REQUIRED_PASSWORD_COMPLEXITY_ID =
+ "SET_REQUIRED_PASSWORD_COMPLEXITY";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -525,7 +527,10 @@
new ButtonInfo(
R.string.device_owner_user_restriction_unset,
CommandReceiverActivity.createSetCurrentUserRestrictionIntent(
- UserManager.DISALLOW_REMOVE_USER, false))
+ UserManager.DISALLOW_REMOVE_USER, false)),
+ new ButtonInfo(
+ R.string.device_owner_remove_secondary_user,
+ createRemoveSecondaryUsersIntent())
}));
}
@@ -571,6 +576,31 @@
}));
}
+ // setRequiredPasswordComplexity
+ adapter.add(createInteractiveTestItem(this, SET_REQUIRED_PASSWORD_COMPLEXITY_ID,
+ R.string.device_owner_required_password_complexity_test,
+ R.string.device_owner_required_password_complexity_test_info,
+ new ButtonInfo[] {
+ new ButtonInfo(
+ R.string.set_low_required_password_complexity,
+ createSetRequiredPasswordComplexityIntent(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_LOW)),
+ new ButtonInfo(
+ R.string.set_medium_required_password_complexity,
+ createSetRequiredPasswordComplexityIntent(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM)),
+ new ButtonInfo(
+ R.string.set_high_required_password_complexity,
+ createSetRequiredPasswordComplexityIntent(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH)),
+ new ButtonInfo(
+ R.string.remove_required_password_complexity,
+ createSetRequiredPasswordComplexityIntent(
+ DevicePolicyManager.PASSWORD_COMPLEXITY_NONE)),
+ new ButtonInfo(
+ R.string.device_owner_settings_go,
+ new Intent(Settings.ACTION_SECURITY_SETTINGS))}));
+
// removeDeviceOwner
adapter.add(createInteractiveTestItem(this, REMOVE_DEVICE_OWNER_TEST_ID,
R.string.device_owner_remove_device_owner_test,
@@ -649,6 +679,12 @@
CommandReceiverActivity.COMMAND_CREATE_MANAGED_USER_WITHOUT_SETUP);
}
+ private Intent createRemoveSecondaryUsersIntent() {
+ return new Intent(this, CommandReceiverActivity.class)
+ .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+ CommandReceiverActivity.COMMAND_REMOVE_SECONDARY_USERS);
+ }
+
private Intent createEnableUsbDataSignalingIntent() {
return new Intent(this, CommandReceiverActivity.class)
.putExtra(CommandReceiverActivity.EXTRA_COMMAND,
@@ -661,6 +697,13 @@
CommandReceiverActivity.COMMAND_DISABLE_USB_DATA_SIGNALING);
}
+ private Intent createSetRequiredPasswordComplexityIntent(int complexity) {
+ return new Intent(this, CommandReceiverActivity.class)
+ .putExtra(CommandReceiverActivity.EXTRA_COMMAND,
+ CommandReceiverActivity.COMMAND_SET_REQUIRED_PASSWORD_COMPLEXITY)
+ .putExtra(CommandReceiverActivity.EXTRA_VALUE, complexity);
+ }
+
private boolean isStatusBarEnabled() {
// Watches don't support the status bar so this is an ok proxy, but this is not the most
// general test for that. TODO: add a test API to do a real check for status bar support.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
index 690bf58..de511b4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
@@ -17,26 +17,24 @@
package com.android.cts.verifier.managedprovisioning;
import android.accessibilityservice.AccessibilityService;
-import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
+import android.provider.Settings;
import android.util.ArrayMap;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
-import android.widget.Spinner;
import android.widget.Switch;
import android.widget.TextView;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-import java.util.ArrayList;
import java.util.Map;
public class PolicyTransparencyTestActivity extends PassFailButtons.Activity implements
@@ -57,7 +55,6 @@
"check-keyguard-unredacted-notification";
public static final String TEST_CHECK_LOCK_SCREEN_INFO = "check-lock-screen-info";
public static final String TEST_CHECK_MAXIMUM_TIME_TO_LOCK = "check-maximum-time-to-lock";
- public static final String TEST_CHECK_PASSWORD_QUALITY = "check-password-quality";
public static final String TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE =
"check-permitted-accessibility-service";
public static final String TEST_CHECK_PERMITTED_INPUT_METHOD = "check-permitted-input-method";
@@ -97,12 +94,6 @@
R.string.set_maximum_time_to_lock_widget_label,
R.id.edit_text_widget,
CommandReceiverActivity.COMMAND_SET_MAXIMUM_TO_LOCK));
- POLICY_TEST_ITEMS.put(TEST_CHECK_PASSWORD_QUALITY, new PolicyTestItem(
- R.string.password_quality_set_step,
- R.string.set_password_quality_action,
- R.string.set_password_quality_widget_label,
- R.id.spinner_widget,
- CommandReceiverActivity.COMMAND_SET_PASSWORD_QUALITY));
POLICY_TEST_ITEMS.put(TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE, new PolicyTestItem(
R.string.permitted_accessibility_services_set_step,
R.string.set_permitted_accessibility_services_action,
@@ -117,27 +108,6 @@
CommandReceiverActivity.COMMAND_ALLOW_ONLY_SYSTEM_INPUT_METHODS));
}
- // IDs of settings for {@link DevicePolicyManager#setPasswordQuality(ComponentName, int)}.
- private final int[] passwordQualityIds = new int[] {
- DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED,
- DevicePolicyManager.PASSWORD_QUALITY_SOMETHING,
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC,
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX,
- DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC,
- DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC,
- DevicePolicyManager.PASSWORD_QUALITY_COMPLEX
- };
- // Strings to show for each password quality setting.
- private final int[] passwordQualityLabelResIds = new int[] {
- R.string.password_quality_unspecified,
- R.string.password_quality_something,
- R.string.password_quality_numeric,
- R.string.password_quality_numeric_complex,
- R.string.password_quality_alphabetic,
- R.string.password_quality_alphanumeric,
- R.string.password_quality_complex
- };
-
private boolean mForceCurrentUserDpm;
private String mSettingsIntentAction;
private String mTestId;
@@ -201,21 +171,6 @@
updateButton.setOnClickListener(this);
updateButton.setVisibility(View.VISIBLE);
} break;
- case R.id.spinner_widget: {
- if (TEST_CHECK_PASSWORD_QUALITY.equals(mTest)) {
- Spinner spinner = (Spinner) findViewById(R.id.spinner_widget);
- spinner.setVisibility(View.VISIBLE);
- spinner.setOnItemSelectedListener(this);
- final ArrayList<String> passwordQualityLabels = new ArrayList<String>();
- for (int resId : passwordQualityLabelResIds) {
- passwordQualityLabels.add(getString(resId));
- }
- ArrayAdapter<String> adapter = new ArrayAdapter(this,
- android.R.layout.simple_spinner_item, passwordQualityLabels);
- adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- spinner.setAdapter(adapter);
- }
- } break;
default: {
throw new IllegalArgumentException("Unknown widgetId: " + widgetId);
}
@@ -225,7 +180,12 @@
@Override
public void onClick(View view) {
if (view.getId() == R.id.open_settings_button) {
- startActivity(new Intent(mSettingsIntentAction));
+ try {
+ startActivity(new Intent(mSettingsIntentAction));
+ } catch (ActivityNotFoundException e) {
+ // If the given settings intent is not handled, use the main settings intent
+ startActivity(new Intent(Settings.ACTION_SETTINGS));
+ }
} else if (view.getId() == R.id.update_button) {
final PolicyTestItem testItem = POLICY_TEST_ITEMS.get(mTest);
final Intent intent = new Intent(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
@@ -261,9 +221,6 @@
final PolicyTestItem testItem = POLICY_TEST_ITEMS.get(mTest);
final Intent intent = new Intent(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
intent.putExtra(CommandReceiverActivity.EXTRA_COMMAND, testItem.command);
- if (TEST_CHECK_PASSWORD_QUALITY.equals(mTest)) {
- intent.putExtra(CommandReceiverActivity.EXTRA_VALUE, passwordQualityIds[(int) id]);
- }
startActivity(intent);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index 0022c90..9a6d1f1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -62,7 +62,6 @@
PolicyTransparencyTestActivity.TEST_CHECK_KEYGURAD_UNREDACTED_NOTIFICATION,
PolicyTransparencyTestActivity.TEST_CHECK_LOCK_SCREEN_INFO,
PolicyTransparencyTestActivity.TEST_CHECK_MAXIMUM_TIME_TO_LOCK,
- PolicyTransparencyTestActivity.TEST_CHECK_PASSWORD_QUALITY,
PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE,
PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_INPUT_METHOD
};
@@ -71,7 +70,6 @@
Settings.ACTION_SETTINGS,
Settings.ACTION_DISPLAY_SETTINGS,
Settings.ACTION_DISPLAY_SETTINGS,
- Settings.ACTION_SETTINGS,
Settings.ACTION_ACCESSIBILITY_SETTINGS,
Settings.ACTION_SETTINGS
};
@@ -80,7 +78,6 @@
R.string.disallow_keyguard_unredacted_notifications,
R.string.set_lock_screen_info,
R.string.set_maximum_time_to_lock,
- R.string.set_password_quality,
R.string.set_permitted_accessibility_services,
R.string.set_permitted_input_methods
};
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
index 0127082..f9806fa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/AudioCapabilitiesTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/audio/AudioCapabilitiesTestActivity.java
@@ -136,21 +136,6 @@
.that(AudioTrack.isDirectPlaybackSupported(
makeAudioFormat(ENCODING_E_AC3, 44100, 6), audioAttributes))
.isFalse();
-
- ImmutableList.Builder<String> actualAtmosFormatStrings = ImmutableList.builder();
- for (AudioFormat audioFormat : ATMOS_FORMATS) {
- if (AudioTrack.isDirectPlaybackSupported(audioFormat, audioAttributes)) {
- actualAtmosFormatStrings.add(toStr(audioFormat));
- }
- }
-
- // check that Atmos should not be supported
- getAsserter()
- .withMessage(
- "AudioTrack.isDirectPlaybackSupported is expected to return false for"
- + " EAC3_JOC")
- .that(actualAtmosFormatStrings.build())
- .isEmpty();
}
}
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
deleted file mode 100644
index e6128d8..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.display;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.util.Log;
-import android.view.Display;
-
-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;
-
-import com.google.common.base.Throwables;
-import com.google.common.collect.Range;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Test to verify the HDR Capabilities API is correctly implemented.
- *
- * <p>This test checks if <a
- * href="https://developer.android.com/reference/android/view/Display.html#isHdr()">Display.isHdr()</a>
- * and <a
- * href="https://developer.android.com/reference/android/view/Display.html#getHdrCapabilities()">Display.getHdrCapabilities()</a>
- * return correct results when 1. HDR Display is connected, 2. non-HDR Display is connected and 3.
- * no display is connected.
- */
-public class DisplayHdrCapabilitiesTestActivity extends TvAppVerifierActivity {
- private static final String LOG_TAG = "HdrCapabilitiesTest";
- private static final float MAX_EXPECTED_LUMINANCE = 10_000f;
- private static final int DISPLAY_DISCONNECT_WAIT_TIME_SECONDS = 5;
-
- private TestSequence mTestSequence;
-
- @Override
- protected void setInfoResources() {
- setInfoResources(
- R.string.tv_hdr_capabilities_test, R.string.tv_hdr_capabilities_test_info, -1);
- }
-
- @Override
- public String getTestDetails() {
- return mTestSequence.getFailureDetails();
- }
-
- @Override
- protected void createTestItems() {
- List<TestStepBase> testSteps = new ArrayList<>();
- if (TvUtil.isHdmiSourceDevice()) {
- // The device is a set-top box or a TV dongle
- testSteps.add(new NonHdrDisplayTestStep(this));
- testSteps.add(new HdrDisplayTestStep(this));
- testSteps.add(new NoDisplayTestStep(this));
- } else {
- // The device is a TV Panel
- testSteps.add(new TvPanelReportedTypesAreSupportedTestStep(this));
- testSteps.add(new TvPanelSupportedTypesAreReportedTestStep(this));
- }
- mTestSequence = new TestSequence(this, testSteps);
- mTestSequence.init();
- }
-
- private static class NonHdrDisplayTestStep extends SyncTestStep {
-
- public NonHdrDisplayTestStep(TvAppVerifierActivity context) {
- super(
- context,
- R.string.tv_hdr_capabilities_test_step_non_hdr_display,
- getInstructionText(context),
- getButtonStringId());
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(
- R.string.tv_hdr_connect_no_hdr_display, context.getString(getButtonStringId()));
- }
-
- private static @StringRes int getButtonStringId() {
- return R.string.tv_start_test;
- }
-
- @Override
- public void runTest() {
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isFalse();
- getAsserter()
- .withMessage("Display.getHdrCapabilities()")
- .that(display.getHdrCapabilities().getSupportedHdrTypes())
- .isEmpty();
- }
- }
-
- private static class HdrDisplayTestStep extends SyncTestStep {
-
- public HdrDisplayTestStep(TvAppVerifierActivity context) {
- super(
- context,
- R.string.tv_hdr_capabilities_test_step_hdr_display,
- getInstructionText(context),
- getButtonStringId());
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(
- R.string.tv_hdr_connect_hdr_display, context.getString(getButtonStringId()));
- }
-
- private static @StringRes int getButtonStringId() {
- return R.string.tv_start_test;
- }
-
- @Override
- public void runTest() {
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
-
- getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isTrue();
-
- Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities();
-
- int[] supportedHdrTypes = hdrCapabilities.getSupportedHdrTypes();
- Arrays.sort(supportedHdrTypes);
-
- getAsserter()
- .withMessage("Display.getHdrCapabilities().getSupportedTypes()")
- .that(supportedHdrTypes)
- .isEqualTo(
- new int[] {
- Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION,
- Display.HdrCapabilities.HDR_TYPE_HDR10,
- Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS,
- Display.HdrCapabilities.HDR_TYPE_HLG
- });
-
- float maxLuminance = hdrCapabilities.getDesiredMaxLuminance();
- getAsserter()
- .withMessage("Display.getHdrCapabilities().getDesiredMaxLuminance()")
- .that(maxLuminance)
- .isIn(Range.openClosed(0f, MAX_EXPECTED_LUMINANCE));
-
- float minLuminance = hdrCapabilities.getDesiredMinLuminance();
- getAsserter()
- .withMessage("Display.getHdrCapabilities().getDesiredMinLuminance()")
- .that(minLuminance)
- .isIn(Range.closedOpen(0f, MAX_EXPECTED_LUMINANCE));
-
- getAsserter()
- .withMessage("Display.getHdrCapabilities().getDesiredMaxAverageLuminance()")
- .that(hdrCapabilities.getDesiredMaxAverageLuminance())
- .isIn(Range.openClosed(minLuminance, maxLuminance));
- }
- }
-
- private static class NoDisplayTestStep extends AsyncTestStep {
- public NoDisplayTestStep(TvAppVerifierActivity context) {
- super(
- context,
- R.string.tv_hdr_capabilities_test_step_no_display,
- getInstructionText(context),
- getButtonStringId());
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(
- R.string.tv_hdr_disconnect_display,
- context.getString(getButtonStringId()),
- DISPLAY_DISCONNECT_WAIT_TIME_SECONDS,
- DISPLAY_DISCONNECT_WAIT_TIME_SECONDS + 1);
- }
-
- private static @StringRes int getButtonStringId() {
- return R.string.tv_start_test;
- }
-
- @Override
- public void runTestAsync() {
- // Wait for the user to disconnect the display.
- final long delay = Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis();
- mContext.getPostTarget().postDelayed(this::runTest, delay);
- }
-
- private void runTest() {
- try {
- // Verify the display APIs do not crash when the display is disconnected
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- display.isHdr();
- display.getHdrCapabilities();
- } catch (Exception e) {
- getAsserter().withMessage(Throwables.getStackTraceAsString(e)).fail();
- }
- done();
- }
- }
-
- private static class TvPanelReportedTypesAreSupportedTestStep extends YesNoTestStep {
- public TvPanelReportedTypesAreSupportedTestStep(TvAppVerifierActivity context) {
- super(context, getInstructionText(context), R.string.tv_yes, R.string.tv_no);
- }
-
- private static String getInstructionText(Context context) {
- DisplayManager displayManager = context.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
-
- int[] hdrTypes = display.getHdrCapabilities().getSupportedHdrTypes();
- String hdrTypesString;
- if (hdrTypes.length == 0) {
- hdrTypesString = context.getString(R.string.tv_none);
- } else {
- hdrTypesString =
- Arrays.stream(hdrTypes)
- .mapToObj(DisplayHdrCapabilitiesTestActivity::hdrTypeToString)
- .collect(Collectors.joining(", "));
- }
-
- return context.getString(
- R.string.tv_panel_hdr_types_reported_are_supported, hdrTypesString);
- }
- }
-
- private static class TvPanelSupportedTypesAreReportedTestStep extends YesNoTestStep {
- public TvPanelSupportedTypesAreReportedTestStep(TvAppVerifierActivity context) {
- super(context, getInstructionText(context), R.string.tv_no, R.string.tv_yes);
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(R.string.tv_panel_hdr_types_supported_are_reported);
- }
- }
-
- private static String hdrTypeToString(@Display.HdrCapabilities.HdrType int type) {
- switch (type) {
- case Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION:
- return "DOLBY_VISION";
- case Display.HdrCapabilities.HDR_TYPE_HDR10:
- return "HDR10";
- case Display.HdrCapabilities.HDR_TYPE_HLG:
- return "HLG";
- case Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS:
- return "HDR10_PLUS";
- default:
- Log.e(LOG_TAG, "Unknown HDR type " + type);
- return "UNKNOWN";
- }
- }
-}
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
deleted file mode 100644
index cee5dac..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
+++ /dev/null
@@ -1,331 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.display;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Bundle;
-import android.view.Display;
-
-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;
-
-import com.google.common.base.Throwables;
-import com.google.common.truth.Correspondence;
-import com.google.common.truth.FailureMetadata;
-import com.google.common.truth.Subject;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import javax.annotation.Nullable;
-
-/**
- * Test for verifying that the platform correctly reports display resolution and refresh rate. More
- * specifically Display.getMode() and Display.getSupportedModes() APIs are tested. In the case for
- * set-top boxes and TV dongles they are tested against reference displays. For TV panels they are
- * tested against the hardware capabilities of the device.
- */
-public class DisplayModesTestActivity extends TvAppVerifierActivity {
- private static final int DISPLAY_DISCONNECT_WAIT_TIME_SECONDS = 5;
- private static final float REFRESH_RATE_PRECISION = 0.01f;
-
- private static final Subject.Factory<ModeSubject, Display.Mode> MODE_SUBJECT_FACTORY =
- (failureMetadata, mode) -> new ModeSubject(failureMetadata, mode);
-
- private static final Correspondence<Display.Mode, Mode> MODE_CORRESPONDENCE =
- Correspondence.from((Display.Mode displayMode, Mode mode) -> {
- return mode.isEquivalent(displayMode, REFRESH_RATE_PRECISION);
- }, "is equivalent to");
-
- private TestSequence mTestSequence;
-
- @Override
- protected void setInfoResources() {
- setInfoResources(R.string.tv_display_modes_test, R.string.tv_display_modes_test_info, -1);
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- }
-
- @Override
- protected void createTestItems() {
- List<TestStepBase> testSteps = new ArrayList<>();
- if (TvUtil.isHdmiSourceDevice()) {
- // The device is a set-top box or a TV dongle
- testSteps.add(new NoDisplayTestStep(this));
- testSteps.add(new Display2160pTestStep(this));
- testSteps.add(new Display1080pTestStep(this));
- } else {
- // The device is a TV Panel
- testSteps.add(new TvPanelReportedModesAreSupportedTestStep(this));
- testSteps.add(new TvPanelSupportedModesAreReportedTestStep(this));
- }
- mTestSequence = new TestSequence(this, testSteps);
- mTestSequence.init();
- }
-
- @Override
- public String getTestDetails() {
- return mTestSequence.getFailureDetails();
- }
-
- private static class NoDisplayTestStep extends AsyncTestStep {
- public NoDisplayTestStep(TvAppVerifierActivity context) {
- super(
- context,
- R.string.tv_display_modes_test_step_no_display,
- getInstructionText(context),
- getButtonStringId());
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(
- R.string.tv_display_modes_disconnect_display,
- context.getString(getButtonStringId()),
- DISPLAY_DISCONNECT_WAIT_TIME_SECONDS,
- DISPLAY_DISCONNECT_WAIT_TIME_SECONDS + 1);
- }
-
- private static @StringRes int getButtonStringId() {
- return R.string.tv_start_test;
- }
-
- @Override
- public void runTestAsync() {
- final long delay = Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis();
- mContext.getPostTarget().postDelayed(this::runTest, delay);
- }
-
- private void runTest() {
- try {
- // Verify the display APIs do not crash when the display is disconnected
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- display.getMode();
- display.getSupportedModes();
- } catch (Exception e) {
- getAsserter().withMessage(Throwables.getStackTraceAsString(e)).fail();
- }
- done();
- }
- }
-
- private static class Display2160pTestStep extends SyncTestStep {
- public Display2160pTestStep(TvAppVerifierActivity context) {
- super(
- context,
- R.string.tv_display_modes_test_step_2160p,
- getInstructionText(context),
- getButtonStringId());
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(
- R.string.tv_display_modes_connect_2160p_display,
- context.getString(getButtonStringId()));
- }
-
- private static @StringRes int getButtonStringId() {
- return R.string.tv_start_test;
- }
-
- @Override
- public void runTest() {
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- getAsserter()
- .withMessage("Display.getMode()")
- .about(MODE_SUBJECT_FACTORY)
- .that(display.getMode())
- .isEquivalentToAnyOf(
- REFRESH_RATE_PRECISION,
- new Mode(3840, 2160, 60f),
- new Mode(3840, 2160, 50f));
-
- Mode[] expected2160pSupportedModes =
- new Mode[] {
- new Mode(720, 480, 60f),
- new Mode(720, 576, 50f),
- // 720p modes
- new Mode(1280, 720, 50f),
- new Mode(1280, 720, 60f),
- // 1080p modes
- new Mode(1920, 1080, 24f),
- new Mode(1920, 1080, 25f),
- new Mode(1920, 1080, 30f),
- new Mode(1920, 1080, 50f),
- new Mode(1920, 1080, 60f),
- // 2160p modes
- new Mode(3840, 2160, 24f),
- new Mode(3840, 2160, 25f),
- new Mode(3840, 2160, 30f),
- new Mode(3840, 2160, 50f),
- new Mode(3840, 2160, 60f)
- };
- getAsserter()
- .withMessage("Display.getSupportedModes()")
- .that(Arrays.asList(display.getSupportedModes()))
- .comparingElementsUsing(MODE_CORRESPONDENCE)
- .containsAtLeastElementsIn(expected2160pSupportedModes);
- }
- }
-
- private static class Display1080pTestStep extends SyncTestStep {
- public Display1080pTestStep(TvAppVerifierActivity context) {
- super(
- context,
- R.string.tv_display_modes_test_step_1080p,
- getInstructionText(context),
- getButtonStringId());
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(
- R.string.tv_display_modes_connect_1080p_display,
- context.getString(getButtonStringId()));
- }
-
- private static @StringRes int getButtonStringId() {
- return R.string.tv_start_test;
- }
-
- @Override
- public void runTest() {
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
-
- getAsserter()
- .withMessage("Display.getMode()")
- .about(MODE_SUBJECT_FACTORY)
- .that(display.getMode())
- .isEquivalentToAnyOf(
- REFRESH_RATE_PRECISION,
- new Mode(1920, 1080, 60f),
- new Mode(1920, 1080, 50f));
-
- final Mode[] expected1080pSupportedModes =
- new Mode[] {
- new Mode(720, 480, 60f),
- new Mode(720, 576, 50f),
- // 720p modes
- new Mode(1280, 720, 50f),
- new Mode(1280, 720, 60f),
- // 1080p modes
- new Mode(1920, 1080, 24f),
- new Mode(1920, 1080, 25f),
- new Mode(1920, 1080, 30f),
- new Mode(1920, 1080, 50f),
- new Mode(1920, 1080, 60f),
- };
- getAsserter()
- .withMessage("Display.getSupportedModes()")
- .that(Arrays.asList(display.getSupportedModes()))
- .comparingElementsUsing(MODE_CORRESPONDENCE)
- .containsAtLeastElementsIn(expected1080pSupportedModes);
- }
- }
-
- private static class TvPanelReportedModesAreSupportedTestStep extends YesNoTestStep {
- public TvPanelReportedModesAreSupportedTestStep(TvAppVerifierActivity context) {
- super(context, getInstructionText(context), R.string.tv_yes, R.string.tv_no);
- }
-
- private static String getInstructionText(Context context) {
- DisplayManager displayManager = context.getSystemService(DisplayManager.class);
- Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- String supportedModes =
- Arrays.stream(display.getSupportedModes())
- .map(DisplayModesTestActivity::formatDisplayMode)
- .collect(Collectors.joining("\n"));
-
- return context.getString(
- R.string.tv_panel_display_modes_reported_are_supported, supportedModes);
- }
- }
-
- private static class TvPanelSupportedModesAreReportedTestStep extends YesNoTestStep {
- public TvPanelSupportedModesAreReportedTestStep(TvAppVerifierActivity context) {
- super(context, getInstructionText(context), R.string.tv_no, R.string.tv_yes);
- }
-
- private static String getInstructionText(Context context) {
- return context.getString(R.string.tv_panel_display_modes_supported_are_reported);
- }
- }
-
- // We use a custom Mode class since the constructors of Display.Mode are hidden. Additionally,
- // 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;
- public float mRefreshRate;
-
- public Mode(int width, int height, float refreshRate) {
- this.mWidth = width;
- this.mHeight = height;
- this.mRefreshRate = refreshRate;
- }
-
- public boolean isEquivalent(Display.Mode displayMode, float refreshRatePrecision) {
- return mHeight == displayMode.getPhysicalHeight()
- && mWidth == displayMode.getPhysicalWidth()
- && Math.abs(mRefreshRate - displayMode.getRefreshRate()) < refreshRatePrecision;
- }
-
- @Override
- public String toString() {
- return formatDisplayMode(mWidth, mHeight, mRefreshRate);
- }
- }
-
- private static class ModeSubject extends Subject {
- private final Display.Mode mActual;
-
- public ModeSubject(FailureMetadata failureMetadata, @Nullable Display.Mode subject) {
- super(failureMetadata, subject);
- mActual = subject;
- }
-
- public void isEquivalentToAnyOf(final float refreshRatePrecision, Mode... modes) {
- boolean found = Arrays.stream(modes)
- .anyMatch(mode -> mode.isEquivalent(mActual, refreshRatePrecision));
- if (!found) {
- failWithActual("expected any of", Arrays.toString(modes));
- }
- }
- }
-
- private static String formatDisplayMode(Display.Mode mode) {
- return formatDisplayMode(
- mode.getPhysicalWidth(), mode.getPhysicalHeight(), mode.getRefreshRate());
- }
-
- private static String formatDisplayMode(int width, int height, float refreshRate) {
- return String.format("%dx%d %.2f Hz", width, height, refreshRate);
- }
-}
diff --git a/common/device-side/bedstead/testapp/src/communication/main/java/com/android/bedstead/testapp/RemoteActivity.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/activities/NeneActivity.java
similarity index 72%
rename from common/device-side/bedstead/testapp/src/communication/main/java/com/android/bedstead/testapp/RemoteActivity.java
rename to common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/activities/NeneActivity.java
index c8b3b3f..7ec10e8 100644
--- a/common/device-side/bedstead/testapp/src/communication/main/java/com/android/bedstead/testapp/RemoteActivity.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/activities/NeneActivity.java
@@ -14,26 +14,25 @@
* limitations under the License.
*/
-package com.android.bedstead.testapp;
+package com.android.bedstead.nene.activities;
import android.app.Activity;
-import com.android.bedstead.testapp.processor.annotations.TestAppCommunication;
-
/**
- * Methods callable on remote activities.
+ * Interface for use by classes which are able to be used in Nene activity test apis.
*
- * <p>Each method listed in this activity must have a corresponding method in
- * {@link BaseTestAppActivity}.
+ * <p>Methods on this interface should match exactly methods on {@link Activity}.
*/
-@TestAppCommunication
-public interface RemoteActivity {
+public interface NeneActivity {
/** See {@link Activity#startLockTask}. */
void startLockTask();
/** See {@link Activity#stopLockTask}. */
void stopLockTask();
+ /** See {@link Activity#finish()}. */
+ void finish();
+
/** See {@link Activity#isFinishing}. */
boolean isFinishing();
}
diff --git a/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java b/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java
index 4b46d17..5229d2e 100644
--- a/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java
+++ b/common/device-side/bedstead/remotedpc/src/communication/main/java/com/android/bedstead/remotedpc/managers/RemoteDevicePolicyManager.java
@@ -18,6 +18,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
+import android.content.IntentFilter;
import android.os.Bundle;
import androidx.annotation.NonNull;
@@ -87,4 +88,23 @@
@NonNull Bundle getUserRestrictions(@NonNull ComponentName admin);
/** See {@link DevicePolicyManager#getUserRestrictions(ComponentName)}. */
@RemoteDpcAutomaticAdmin @NonNull Bundle getUserRestrictions();
+
+ /**
+ * See {@link DevicePolicyManager#addCrossProfileIntentFilter(ComponentName, IntentFilter,
+ * int)}.
+ */
+ void addCrossProfileIntentFilter(@NonNull ComponentName admin, @NonNull IntentFilter filter,
+ int flags);
+ /**
+ * See {@link DevicePolicyManager#addCrossProfileIntentFilter(ComponentName, IntentFilter,
+ * int)}.
+ */
+ @RemoteDpcAutomaticAdmin void addCrossProfileIntentFilter(@NonNull IntentFilter filter,
+ int flags);
+
+ /** See {@link DevicePolicyManager#clearCrossProfileIntentFilters(ComponentName)}. */
+ void clearCrossProfileIntentFilters(@NonNull ComponentName admin);
+ /** See {@link DevicePolicyManager#clearCrossProfileIntentFilters(ComponentName)}. */
+ @RemoteDpcAutomaticAdmin void clearCrossProfileIntentFilters();
+
}
diff --git a/common/device-side/bedstead/testapp/Android.bp b/common/device-side/bedstead/testapp/Android.bp
index 6c8582a..7e4611d 100644
--- a/common/device-side/bedstead/testapp/Android.bp
+++ b/common/device-side/bedstead/testapp/Android.bp
@@ -3,25 +3,6 @@
}
android_library {
- name: "TestApp_Communication",
- sdk_version: "test_current",
- srcs: [
- "src/communication/main/java/**/*.java"
- ],
- static_libs: [
- "Nene",
- "ConnectedAppsSDK",
- "ConnectedAppsSDK_Annotations",
- "androidx.annotation_annotation",
- "TestApp_TestApps",
- "TestApp_Annotations"
- ],
- manifest: "src/communication/main/AndroidManifest.xml",
- min_sdk_version: "28",
- plugins: ["ConnectedAppsSDK_Processor", "TestApp_Processor"],
-}
-
-android_library {
name: "TestApp_TestApps",
sdk_version: "test_current",
srcs: [
@@ -29,10 +10,15 @@
],
static_libs: [
"Nene",
- "EventLib"
+ "EventLib",
+ "TestApp_Annotations",
+ "ConnectedAppsSDK",
+ "ConnectedAppsSDK_Annotations",
+ "androidx.annotation_annotation",
],
manifest: "src/main/testapps/AndroidManifest.xml",
- min_sdk_version: "28"
+ min_sdk_version: "28",
+ plugins: ["ConnectedAppsSDK_Processor", "TestApp_Processor"],
}
android_library {
@@ -44,11 +30,16 @@
static_libs: [
"Nene",
"EventLib",
- "TestApp_Communication"
+ "TestApp_Annotations",
+ "ConnectedAppsSDK",
+ "ConnectedAppsSDK_Annotations",
+ "androidx.annotation_annotation",
+ "TestApp_TestApps"
],
manifest: "src/main/library/AndroidManifest.xml",
min_sdk_version: "28",
resource_zips: [":TestApp_Apps"],
+ plugins: ["ConnectedAppsSDK_Processor", "TestApp_Processor"],
}
android_test {
@@ -107,8 +98,7 @@
android_test_helper_app {
name: "EmptyTestApp",
static_libs: [
- "TestApp_TestApps",
- "TestApp_Communication"
+ "TestApp_TestApps"
],
manifest: "manifests/EmptyTestAppManifest.xml",
min_sdk_version: "28"
@@ -117,8 +107,7 @@
android_test_helper_app {
name: "EmptyTestApp2",
static_libs: [
- "TestApp_TestApps",
- "TestApp_Communication"
+ "TestApp_TestApps"
],
manifest: "manifests/EmptyTestApp2Manifest.xml",
min_sdk_version: "28"
@@ -128,7 +117,6 @@
name: "DeviceAdminTestApp",
static_libs: [
"TestApp_TestApps",
- "TestApp_Communication",
"DeviceAdminApp"
],
manifest: "manifests/DeviceAdminManifest.xml",
diff --git a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestApp.java b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestApp.java
index a7abab1..771625c 100644
--- a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestApp.java
+++ b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestApp.java
@@ -26,6 +26,7 @@
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.packages.PackageReference;
import com.android.bedstead.nene.users.UserReference;
+import com.android.bedstead.testapp.processor.annotations.TestAppSender;
import java.io.File;
import java.io.FileOutputStream;
@@ -33,6 +34,7 @@
import java.io.InputStream;
/** Represents a single test app which can be installed and interacted with. */
+@TestAppSender
public class TestApp {
private static final TestApis sTestApis = new TestApis();
diff --git a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivities.java b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivities.java
index 16f7293..9373d64 100644
--- a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivities.java
+++ b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivities.java
@@ -29,11 +29,11 @@
*
* <p>Currently, this will always return the same activity.
*/
- public TestAppActivityReference any() {
+ public UnresolvedTestAppActivity any() {
// TODO(scottjonathan): Currently we only have one pattern for testapps and they all have
// exactly one activity - so we will return it here. In future we should expose a query
// interface
- return new TestAppActivityReference(
+ return new UnresolvedTestAppActivity(
mInstance,
mInstance.testApp().reference().component("android.testapp.activity"));
}
diff --git a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivity.java b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivity.java
new file mode 100644
index 0000000..f804866
--- /dev/null
+++ b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivity.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bedstead.testapp;
+
+import android.app.Activity;
+
+import com.android.bedstead.nene.activities.NeneActivity;
+import com.android.bedstead.nene.packages.ComponentReference;
+
+/**
+ * A reference to an instance of an activity in a test app.
+ *
+ * <p>Many of the APIs exposed by this class perform a remote call and will make the call on the
+ * actual {@link Activity} instance inside the test app.
+ */
+public abstract class TestAppActivity extends TestAppActivityReference implements NeneActivity {
+ TestAppActivity(TestAppInstanceReference instance,
+ ComponentReference component) {
+ super(instance, component);
+ }
+}
diff --git a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivityReference.java b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivityReference.java
index 648d23c..9bc9160 100644
--- a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivityReference.java
+++ b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/TestAppActivityReference.java
@@ -16,26 +16,19 @@
package com.android.bedstead.testapp;
-import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
-import android.app.Activity;
-import android.content.Intent;
-
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.activities.ActivityReference;
import com.android.bedstead.nene.packages.ComponentReference;
-import com.android.eventlib.events.activities.ActivityCreatedEvent;
/**
- * A reference to an {@link Activity} in a {@link TestApp}.
+ * A reference to an activity in a test app for which there may or may not be an instance.
*/
-public final class TestAppActivityReference {
+public abstract class TestAppActivityReference {
- private static final TestApis sTestApis = new TestApis();
+ static final TestApis sTestApis = new TestApis();
- private final TestAppInstanceReference mInstance;
- private final ComponentReference mComponent;
+ final TestAppInstanceReference mInstance;
+ final ComponentReference mComponent;
TestAppActivityReference(
TestAppInstanceReference instance,
@@ -44,30 +37,6 @@
mComponent = component;
}
- /**
- * Starts the activity.
- */
- public TestAppActivityReference start() {
- Intent intent = new Intent();
- intent.setComponent(mComponent.componentName());
- intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
- sTestApis.context().instrumentedContext().startActivity(intent);
-
- ActivityCreatedEvent
- .queryPackage(mComponent.packageName().packageName())
- .whereActivity().className().isEqualTo(mComponent.className()).waitForEvent();
-
- return this;
- }
-
- /**
- * Makes calls on the remote activity.
- */
- public RemoteActivity remote() {
- return new RemoteActivityImpl(
- mComponent.className(), new TargetedRemoteActivityWrapper(mInstance.connector()));
- }
-
/** Gets the {@link TestAppInstanceReference} this activity exists in. */
public TestAppInstanceReference testAppInstance() {
return mInstance;
diff --git a/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/UnresolvedTestAppActivity.java b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/UnresolvedTestAppActivity.java
new file mode 100644
index 0000000..e6b272a
--- /dev/null
+++ b/common/device-side/bedstead/testapp/src/main/library/java/com/android/bedstead/testapp/UnresolvedTestAppActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bedstead.testapp;
+
+import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import android.app.Activity;
+import android.content.Intent;
+
+import com.android.bedstead.nene.packages.ComponentReference;
+import com.android.eventlib.events.activities.ActivityCreatedEvent;
+
+/**
+ * A reference to an {@link Activity} in a {@link TestApp}.
+ */
+public final class UnresolvedTestAppActivity extends TestAppActivityReference {
+
+ UnresolvedTestAppActivity(TestAppInstanceReference instance,
+ ComponentReference component) {
+ super(instance, component);
+ }
+
+ /**
+ * Starts the activity.
+ */
+ public TestAppActivity start() {
+ Intent intent = new Intent();
+ intent.setComponent(mComponent.componentName());
+ intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
+ sTestApis.context().instrumentedContext().startActivity(intent);
+
+ ActivityCreatedEvent
+ .queryPackage(mComponent.packageName().packageName())
+ .whereActivity().className().isEqualTo(mComponent.className()).waitForEvent();
+
+ return new TestAppActivityImpl(mInstance, mComponent);
+ }
+}
diff --git a/common/device-side/bedstead/testapp/src/main/testapps/AndroidManifest.xml b/common/device-side/bedstead/testapp/src/main/testapps/AndroidManifest.xml
index 06e0eab..78d2273 100644
--- a/common/device-side/bedstead/testapp/src/main/testapps/AndroidManifest.xml
+++ b/common/device-side/bedstead/testapp/src/main/testapps/AndroidManifest.xml
@@ -20,5 +20,6 @@
package="com.android.bedstead.testapp.testapps">
<uses-sdk android:minSdkVersion="28" />
<application>
+ <service android:name="com.google.android.enterprise.connectedapps.CrossProfileConnector_Service" android:exported="true" />
</application>
</manifest>
diff --git a/common/device-side/bedstead/testapp/src/main/testapps/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java b/common/device-side/bedstead/testapp/src/main/testapps/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
index 7e5497f..34b9ef2 100644
--- a/common/device-side/bedstead/testapp/src/main/testapps/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
+++ b/common/device-side/bedstead/testapp/src/main/testapps/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
@@ -22,9 +22,12 @@
import android.content.Intent;
import android.util.Log;
+import com.android.bedstead.testapp.processor.annotations.TestAppReceiver;
+
/**
* An {@link AppComponentFactory} which redirects invalid class names to premade TestApp classes.
*/
+@TestAppReceiver
public final class TestAppAppComponentFactory extends AppComponentFactory {
private static final String LOG_TAG = "TestAppACF";
diff --git a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java
index b2d30bf..4e9289f 100644
--- a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java
+++ b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/Processor.java
@@ -17,7 +17,8 @@
package com.android.bedstead.testapp.processor;
-import com.android.bedstead.testapp.processor.annotations.TestAppCommunication;
+import com.android.bedstead.testapp.processor.annotations.TestAppReceiver;
+import com.android.bedstead.testapp.processor.annotations.TestAppSender;
import com.google.android.enterprise.connectedapps.annotations.CrossProfile;
import com.google.android.enterprise.connectedapps.annotations.CrossProfileConfiguration;
@@ -51,21 +52,26 @@
/** Processor for generating TestApp API for remote execution. */
@SupportedAnnotationTypes({
- "com.android.bedstead.testapp.processor.annotations.TestAppCommunication",
+ "com.android.bedstead.testapp.processor.annotations.TestAppSender",
+ "com.android.bedstead.testapp.processor.annotations.TestAppReceiver",
})
@AutoService(javax.annotation.processing.Processor.class)
public final class Processor extends AbstractProcessor {
// TODO(scottjonathan): Add more verification before generating - and add processor tests
private static final ClassName CONTEXT_CLASSNAME =
ClassName.get("android.content", "Context");
- private static final ClassName REMOTE_ACTIVITY_CLASSNAME =
+ private static final ClassName NENE_ACTIVITY_CLASSNAME =
+ ClassName.get(
+ "com.android.bedstead.nene.activities",
+ "NeneActivity");
+ private static final ClassName TEST_APP_ACTIVITY_CLASSNAME =
ClassName.get(
"com.android.bedstead.testapp",
- "RemoteActivity");
- private static final ClassName REMOTE_ACTIVITY_IMPL_CLASSNAME =
+ "TestAppActivity");
+ private static final ClassName TEST_APP_ACTIVITY_IMPL_CLASSNAME =
ClassName.get(
"com.android.bedstead.testapp",
- "RemoteActivityImpl");
+ "TestAppActivityImpl");
private static final ClassName PROFILE_TARGETED_REMOTE_ACTIVITY_CLASSNAME =
ClassName.get(
"com.android.bedstead.testapp",
@@ -96,6 +102,10 @@
private static final ClassName NENE_EXCEPTION_CLASSNAME =
ClassName.get(
"com.android.bedstead.nene.exceptions", "NeneException");
+ private static final ClassName TEST_APP_INSTANCE_REFERENCE_CLASSNAME =
+ ClassName.get("com.android.bedstead.testapp", "TestAppInstanceReference");
+ private static final ClassName COMPONENT_REFERENCE_CLASSNAME =
+ ClassName.get("com.android.bedstead.nene.packages", "ComponentReference");
public static final String PACKAGE_NAME = "com.android.bedstead.testapp";
@Override
@@ -107,32 +117,35 @@
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
- if (roundEnv.getElementsAnnotatedWith(TestAppCommunication.class).isEmpty()) {
- return true;
+ TypeElement neneActivityInterface =
+ processingEnv.getElementUtils().getTypeElement(
+ NENE_ACTIVITY_CLASSNAME.canonicalName());
+
+ if (!roundEnv.getElementsAnnotatedWith(TestAppReceiver.class).isEmpty()
+ || !roundEnv.getElementsAnnotatedWith(TestAppSender.class).isEmpty()) {
+ generateTargetedRemoteActivityInterface(neneActivityInterface);
+ generateTargetedRemoteActivityImpl(neneActivityInterface);
+ generateTargetedRemoteActivityWrapper(neneActivityInterface);
+ generateProvider();
+ generateConfiguration();
+
}
- TypeElement remoteActivityInterface =
- processingEnv.getElementUtils().getTypeElement(
- REMOTE_ACTIVITY_CLASSNAME.canonicalName());
-
- generateRemoteActivityImpl(remoteActivityInterface);
- generateTargetedRemoteActivityInterface(remoteActivityInterface);
- generateTargetedRemoteActivityImpl(remoteActivityInterface);
- generateTargetedRemoteActivityWrapper(remoteActivityInterface);
- generateProvider();
- generateConfiguration();
+ if (!roundEnv.getElementsAnnotatedWith(TestAppSender.class).isEmpty()) {
+ generateRemoteActivityImpl(neneActivityInterface);
+ }
return true;
}
- private void generateTargetedRemoteActivityImpl(TypeElement remoteActivityInterface) {
+ private void generateTargetedRemoteActivityImpl(TypeElement neneActivityInterface) {
TypeSpec.Builder classBuilder =
TypeSpec.classBuilder(
TARGETED_REMOTE_ACTIVITY_IMPL_CLASSNAME)
.addSuperinterface(TARGETED_REMOTE_ACTIVITY_CLASSNAME)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
- for (ExecutableElement method : getMethods(remoteActivityInterface)) {
+ for (ExecutableElement method : getMethods(neneActivityInterface)) {
MethodSpec.Builder methodBuilder =
MethodSpec.methodBuilder(method.getSimpleName().toString())
.returns(ClassName.get(method.getReturnType()))
@@ -170,7 +183,7 @@
writeClassToFile(PACKAGE_NAME, classBuilder.build());
}
- private void generateTargetedRemoteActivityWrapper(TypeElement remoteActivityInterface) {
+ private void generateTargetedRemoteActivityWrapper(TypeElement neneActivityInterface) {
TypeSpec.Builder classBuilder =
TypeSpec.classBuilder(
TARGETED_REMOTE_ACTIVITY_WRAPPER_CLASSNAME)
@@ -195,7 +208,7 @@
PROFILE_TARGETED_REMOTE_ACTIVITY_CLASSNAME)
.build());
- for (ExecutableElement method : getMethods(remoteActivityInterface)) {
+ for (ExecutableElement method : getMethods(neneActivityInterface)) {
MethodSpec.Builder methodBuilder =
MethodSpec.methodBuilder(method.getSimpleName().toString())
.returns(ClassName.get(method.getReturnType()))
@@ -247,11 +260,11 @@
writeClassToFile(PACKAGE_NAME, classBuilder.build());
}
- private void generateRemoteActivityImpl(TypeElement remoteActivityInterface) {
+ private void generateRemoteActivityImpl(TypeElement neneActivityInterface) {
TypeSpec.Builder classBuilder =
TypeSpec.classBuilder(
- REMOTE_ACTIVITY_IMPL_CLASSNAME)
- .addSuperinterface(REMOTE_ACTIVITY_CLASSNAME)
+ TEST_APP_ACTIVITY_IMPL_CLASSNAME)
+ .superclass(TEST_APP_ACTIVITY_CLASSNAME)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL);
classBuilder.addField(FieldSpec.builder(String.class, "mActivityClassName")
@@ -262,15 +275,17 @@
classBuilder.addMethod(
MethodSpec.constructorBuilder()
- .addParameter(String.class, "activityClassName")
+ .addParameter(TEST_APP_INSTANCE_REFERENCE_CLASSNAME, "instance")
.addParameter(
- TARGETED_REMOTE_ACTIVITY_CLASSNAME, "targetedRemoteActivity")
- .addStatement("mActivityClassName = activityClassName")
- .addStatement("mTargetedRemoteActivity = targetedRemoteActivity")
+ COMPONENT_REFERENCE_CLASSNAME, "component")
+ .addStatement("super(instance, component)")
+ .addStatement("mActivityClassName = component.className()")
+ .addStatement("mTargetedRemoteActivity = new $T(mInstance.connector())",
+ TARGETED_REMOTE_ACTIVITY_WRAPPER_CLASSNAME)
.build());
- for (ExecutableElement method : getMethods(remoteActivityInterface)) {
+ for (ExecutableElement method : getMethods(neneActivityInterface)) {
MethodSpec.Builder methodBuilder =
MethodSpec.methodBuilder(method.getSimpleName().toString())
.returns(ClassName.get(method.getReturnType()))
@@ -303,13 +318,13 @@
writeClassToFile(PACKAGE_NAME, classBuilder.build());
}
- private void generateTargetedRemoteActivityInterface(TypeElement remoteActivityInterface) {
+ private void generateTargetedRemoteActivityInterface(TypeElement neneActivityInterface) {
TypeSpec.Builder classBuilder =
TypeSpec.interfaceBuilder(
TARGETED_REMOTE_ACTIVITY_CLASSNAME)
.addModifiers(Modifier.PUBLIC);
- for (ExecutableElement method : getMethods(remoteActivityInterface)) {
+ for (ExecutableElement method : getMethods(neneActivityInterface)) {
MethodSpec.Builder methodBuilder =
MethodSpec.methodBuilder(method.getSimpleName().toString())
.returns(ClassName.get(method.getReturnType()))
diff --git a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppReceiver.java
similarity index 93%
rename from common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
rename to common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppReceiver.java
index a64c3a0..ad35792 100644
--- a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
+++ b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppReceiver.java
@@ -16,5 +16,5 @@
package com.android.bedstead.testapp.processor.annotations;
-public @interface TestAppCommunication {
+public @interface TestAppReceiver {
}
diff --git a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppSender.java
similarity index 93%
copy from common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
copy to common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppSender.java
index a64c3a0..06ff1de 100644
--- a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
+++ b/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppSender.java
@@ -16,5 +16,5 @@
package com.android.bedstead.testapp.processor.annotations;
-public @interface TestAppCommunication {
+public @interface TestAppSender {
}
diff --git a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppActivityReferenceTest.java b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppActivityReferenceTest.java
index 0133427..0c14877 100644
--- a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppActivityReferenceTest.java
+++ b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppActivityReferenceTest.java
@@ -55,8 +55,7 @@
public void start_activityIsStarted() {
TestApp testApp = mTestAppProvider.any(); // TODO(scottjonathan): specify must have activity
try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
-
- TestAppActivityReference activity = testAppInstance.activities().any().start();
+ TestAppActivity activity = testAppInstance.activities().any().start();
assertThat(sTestApis.activities().foregroundActivity()).isEqualTo(activity.reference());
}
@@ -66,9 +65,9 @@
public void remote_executes() {
TestApp testApp = mTestAppProvider.any(); // TODO(scottjonathan): specify must have activity
try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
- TestAppActivityReference activity = testAppInstance.activities().any().start();
+ TestAppActivity activity = testAppInstance.activities().any().start();
- assertThat(activity.remote().isFinishing()).isFalse();
+ assertThat(activity.isFinishing()).isFalse();
}
}
}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/FreezeRotationRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/FreezeRotationRule.java
new file mode 100644
index 0000000..b07631f
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/FreezeRotationRule.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compatibility.common.util;
+
+import android.app.UiAutomation;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class FreezeRotationRule extends BeforeAfterRule {
+ private final UiAutomation mUiAutomation = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation();
+
+ private final int mRotation;
+
+ public FreezeRotationRule(int rotation) {
+ mRotation = rotation;
+ }
+
+ public FreezeRotationRule() {
+ this(UiAutomation.ROTATION_FREEZE_0);
+ }
+
+ @Override
+ protected void onBefore(Statement base, Description description) throws Throwable {
+ mUiAutomation.setRotation(mRotation);
+ }
+
+ @Override
+ protected void onAfter(Statement base, Description description) throws Throwable {
+ mUiAutomation.setRotation(UiAutomation.ROTATION_UNFREEZE);
+ }
+}
diff --git a/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java b/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java
index 16214ce..060c8ed 100644
--- a/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java
+++ b/hostsidetests/appcompat/compatchanges/app/src/com/android/cts/appcompat/compatchanges/CompatChangesTest.java
@@ -56,7 +56,8 @@
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
- Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD);
+ Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
@After
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/OWNERS b/hostsidetests/appsecurity/src/android/appsecurity/cts/OWNERS
new file mode 100644
index 0000000..de3a3b9
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/OWNERS
@@ -0,0 +1 @@
+per-file *ResumeOnReboot* = file:platform/frameworks/base:/core/java/android/service/resumeonreboot/OWNERS
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/OWNERS b/hostsidetests/appsecurity/test-apps/EncryptionApp/OWNERS
index 0c17955..11033f1 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/OWNERS
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 49763
include platform/frameworks/base:/core/java/android/os/storage/OWNERS
+include platform/frameworks/base:/core/java/android/service/resumeonreboot/OWNERS
diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
index 6ba3e78..36ad932 100644
--- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java
@@ -438,6 +438,10 @@
try (ParcelFileDescriptor pfd = mContentResolver.openFileDescriptor(red, "w")) {
}
+ // Wait for MediaStore to be idle to avoid flakiness due to race conditions between
+ // MediaStore.scanFile (which is called above in #openFileDescriptor) and rename (which is
+ // called below). This is a known issue: b/158982091
+ MediaStore.waitForIdle(mContentResolver);
// Check File API support
assertAccessFileAPISupport(file);
diff --git a/hostsidetests/car/app/Android.bp b/hostsidetests/car/app/Android.bp
index 3a36e0b..0ed1890 100644
--- a/hostsidetests/car/app/Android.bp
+++ b/hostsidetests/car/app/Android.bp
@@ -27,6 +27,7 @@
"android.frameworks.automotive.powerpolicy-V1-java",
"android.hardware.automotive.vehicle-V2.0-java",
"androidx.test.rules",
+ "compatibility-device-util-axt",
],
libs: ["android.car"],
diff --git a/hostsidetests/car/app/src/android/car/cts/app/PowerComponentUtil.java b/hostsidetests/car/app/src/android/car/cts/app/PowerComponentUtil.java
new file mode 100644
index 0000000..6e7c295
--- /dev/null
+++ b/hostsidetests/car/app/src/android/car/cts/app/PowerComponentUtil.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cts.app;
+
+import android.car.hardware.power.PowerComponent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+public final class PowerComponentUtil {
+ private static final String POWER_COMPONENT_AUDIO = "AUDIO";
+ private static final String POWER_COMPONENT_MEDIA = "MEDIA";
+ private static final String POWER_COMPONENT_DISPLAY = "DISPLAY";
+ private static final String POWER_COMPONENT_BLUETOOTH = "BLUETOOTH";
+ private static final String POWER_COMPONENT_WIFI = "WIFI";
+ private static final String POWER_COMPONENT_CELLULAR = "CELLULAR";
+ private static final String POWER_COMPONENT_ETHERNET = "ETHERNET";
+ private static final String POWER_COMPONENT_PROJECTION = "PROJECTION";
+ private static final String POWER_COMPONENT_NFC = "NFC";
+ private static final String POWER_COMPONENT_INPUT = "INPUT";
+ private static final String POWER_COMPONENT_VOICE_INTERACTION = "VOICE_INTERACTION";
+ private static final String POWER_COMPONENT_VISUAL_INTERACTION = "VISUAL_INTERACTION";
+ private static final String POWER_COMPONENT_TRUSTED_DEVICE_DETECTION =
+ "TRUSTED_DEVICE_DETECTION";
+ private static final String POWER_COMPONENT_LOCATION = "LOCATION";
+ private static final String POWER_COMPONENT_MICROPHONE = "MICROPHONE";
+ private static final String POWER_COMPONENT_CPU = "CPU";
+
+ public static final int INVALID_POWER_COMPONENT = -1;
+
+ private PowerComponentUtil() throws Exception {
+ throw new Exception("utility class is not instantiable");
+ }
+
+ public static int toPowerComponent(@Nullable String component) {
+ if (component == null) {
+ return INVALID_POWER_COMPONENT;
+ }
+ switch (component) {
+ case POWER_COMPONENT_AUDIO:
+ return PowerComponent.AUDIO;
+ case POWER_COMPONENT_MEDIA:
+ return PowerComponent.MEDIA;
+ case POWER_COMPONENT_DISPLAY:
+ return PowerComponent.DISPLAY;
+ case POWER_COMPONENT_BLUETOOTH:
+ return PowerComponent.BLUETOOTH;
+ case POWER_COMPONENT_WIFI:
+ return PowerComponent.WIFI;
+ case POWER_COMPONENT_CELLULAR:
+ return PowerComponent.CELLULAR;
+ case POWER_COMPONENT_ETHERNET:
+ return PowerComponent.ETHERNET;
+ case POWER_COMPONENT_PROJECTION:
+ return PowerComponent.PROJECTION;
+ case POWER_COMPONENT_NFC:
+ return PowerComponent.NFC;
+ case POWER_COMPONENT_INPUT:
+ return PowerComponent.INPUT;
+ case POWER_COMPONENT_VOICE_INTERACTION:
+ return PowerComponent.VOICE_INTERACTION;
+ case POWER_COMPONENT_VISUAL_INTERACTION:
+ return PowerComponent.VISUAL_INTERACTION;
+ case POWER_COMPONENT_TRUSTED_DEVICE_DETECTION:
+ return PowerComponent.TRUSTED_DEVICE_DETECTION;
+ case POWER_COMPONENT_LOCATION:
+ return PowerComponent.LOCATION;
+ case POWER_COMPONENT_MICROPHONE:
+ return PowerComponent.MICROPHONE;
+ case POWER_COMPONENT_CPU:
+ return PowerComponent.CPU;
+ default:
+ return INVALID_POWER_COMPONENT;
+ }
+ }
+
+ @NonNull
+ public static String componentToString(int component) {
+ switch (component) {
+ case PowerComponent.AUDIO:
+ return POWER_COMPONENT_AUDIO;
+ case PowerComponent.MEDIA:
+ return POWER_COMPONENT_MEDIA;
+ case PowerComponent.DISPLAY:
+ return POWER_COMPONENT_DISPLAY;
+ case PowerComponent.BLUETOOTH:
+ return POWER_COMPONENT_BLUETOOTH;
+ case PowerComponent.WIFI:
+ return POWER_COMPONENT_WIFI;
+ case PowerComponent.CELLULAR:
+ return POWER_COMPONENT_CELLULAR;
+ case PowerComponent.ETHERNET:
+ return POWER_COMPONENT_ETHERNET;
+ case PowerComponent.PROJECTION:
+ return POWER_COMPONENT_PROJECTION;
+ case PowerComponent.NFC:
+ return POWER_COMPONENT_NFC;
+ case PowerComponent.INPUT:
+ return POWER_COMPONENT_INPUT;
+ case PowerComponent.VOICE_INTERACTION:
+ return POWER_COMPONENT_VOICE_INTERACTION;
+ case PowerComponent.VISUAL_INTERACTION:
+ return POWER_COMPONENT_VISUAL_INTERACTION;
+ case PowerComponent.TRUSTED_DEVICE_DETECTION:
+ return POWER_COMPONENT_TRUSTED_DEVICE_DETECTION;
+ case PowerComponent.LOCATION:
+ return POWER_COMPONENT_LOCATION;
+ case PowerComponent.MICROPHONE:
+ return POWER_COMPONENT_MICROPHONE;
+ case PowerComponent.CPU:
+ return POWER_COMPONENT_CPU;
+ default:
+ return "unknown component";
+ }
+ }
+}
diff --git a/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyListenerImpl.java b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyListenerImpl.java
new file mode 100644
index 0000000..dc2e13e
--- /dev/null
+++ b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyListenerImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.cts.app;
+
+import android.car.hardware.power.CarPowerManager;
+import android.car.hardware.power.CarPowerPolicy;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.util.Arrays;
+
+public final class PowerPolicyListenerImpl implements
+ CarPowerManager.CarPowerPolicyListener {
+ private static final String TAG = PowerPolicyListenerImpl.class.getSimpleName();
+
+ private final PowerPolicyTestClient mTestClient;
+ private final String mComponentName;
+ private CarPowerPolicy mCurrentPolicy;
+
+ PowerPolicyListenerImpl(PowerPolicyTestClient testClient, String compName) {
+ mTestClient = testClient;
+ mComponentName = compName;
+ }
+
+ @Override
+ public void onPolicyChanged(CarPowerPolicy policy) {
+ Log.d(TAG, "a new policy has been received by component: " + mComponentName);
+ if (policy == null) {
+ Log.e(TAG, "onPolicyChanged: null policy");
+ }
+ mCurrentPolicy = policy;
+ mTestClient.printResultHeader("PowerPolicyListener " + mComponentName);
+ mTestClient.printlnResult(getPolicyString(policy));
+ }
+
+ @Nullable
+ public CarPowerPolicy getCurrentPolicy() {
+ return mCurrentPolicy;
+ }
+
+ public void reset() {
+ mCurrentPolicy = null;
+ }
+
+ public static String getPolicyString(CarPowerPolicy policy) {
+ String[] enables = Arrays.stream(policy.getEnabledComponents())
+ .mapToObj(PowerComponentUtil::componentToString).toArray(String[]::new);
+ String[] disables = Arrays.stream(policy.getDisabledComponents())
+ .mapToObj(PowerComponentUtil::componentToString).toArray(String[]::new);
+
+ StringBuilder policyStr = new StringBuilder();
+ policyStr.append(policy.getPolicyId()).append(" (enabledComponents: ");
+ if (enables.length == 0) {
+ policyStr.append("none");
+ } else {
+ policyStr.append(String.join(",", enables));
+ }
+
+ policyStr.append(" disabledComponents: ");
+ if (disables.length == 0) {
+ policyStr.append("none");
+ } else {
+ policyStr.append(String.join(",", disables));
+ }
+ policyStr.append(')');
+
+ return policyStr.toString();
+ }
+}
diff --git a/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestActivity.java b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestActivity.java
index b6d5a78..c7be415 100644
--- a/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestActivity.java
+++ b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestActivity.java
@@ -37,10 +37,16 @@
* <pre class="prettyprint">
* adb shell am force-stop android.car.cts.app
* adb shell am start -n android.car.cts.app/.PowerPolicyTestActivity \
- * --es "powerpolicy" "testcase,action[,data]"
- * - testcase: suite | 1 | 2 | 3 | 4 | 5 | 6
- * - action: start | end | dumpstate | dumppolicy | applypolicy | closefile
- * - data: policyId
+ * --es "powerpolicy" "action"
+ * action:
+ * settest [testcase_name]
+ * cleartest
+ * dumppolicy [policyId]
+ * addlistener [component_name]
+ * removelistener [component_name]
+ * dumplistener [component_name]
+ * resetlisteners
+ * waitlisteners
* </pre>
*/
public final class PowerPolicyTestActivity extends Activity {
@@ -48,13 +54,13 @@
private static final String TAG = PowerPolicyTestActivity.class.getSimpleName();
private final Object mLock = new Object();
- private final PowerPolicyTestClient mTestClient = new PowerPolicyTestClient();
// LocalLog is not available for cts. Use StringWriter instead.
// The host side test will kill and restart the app for each test case,
// therefore 4KB buffer size is sufficient.
private final StringWriter mResultBuf = new StringWriter(RESULT_LOG_SIZE);
private final PrintWriter mResultLog = new PrintWriter(mResultBuf);
+ private final PowerPolicyTestClient mTestClient = new PowerPolicyTestClient(mResultLog);
private Car mCarApi;
@GuardedBy("mLock")
@@ -82,12 +88,16 @@
return;
}
- PowerPolicyTestCommand cmd = mTestClient.parseCommand(extras);
- if (cmd == null) {
- Log.d(TAG, "onNewIntent(): null policy test command");
- return;
+ try {
+ PowerPolicyTestCommand cmd = mTestClient.parseCommand(extras);
+ if (cmd == null) {
+ Log.d(TAG, "onNewIntent(): null policy test command");
+ return;
+ }
+ cmd.execute();
+ } catch (Exception e) {
+ Log.e(TAG, "onNewIntent(): failed to handle cmd", e);
}
- mTestClient.handleCommand(cmd, mResultLog);
}
@Override
diff --git a/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestClient.java b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestClient.java
index 38f0821..bd34fbe 100644
--- a/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestClient.java
+++ b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestClient.java
@@ -17,93 +17,110 @@
package android.car.cts.app;
import android.car.hardware.power.CarPowerManager;
+import android.car.hardware.power.CarPowerPolicy;
+import android.car.hardware.power.CarPowerPolicyFilter;
import android.os.Bundle;
-import android.os.SystemClock;
import android.util.Log;
import androidx.annotation.Nullable;
import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
public final class PowerPolicyTestClient {
private static final String TAG = PowerPolicyTestClient.class.getSimpleName();
+ private static final String TEST_RESULT_HEADER = "PowerPolicyTestResult: ";
private static final String POWERPOLICY_TEST_CMD_IDENTIFIER = "powerpolicy";
- private static final String TEST_CMD_START = "start";
- private static final String TEST_CMD_END = "end";
- private static final String TEST_CMD_DUMP_STATE = "dumpstate";
+ private static final String TEST_CMD_SET_TEST = "settest";
+ private static final String TEST_CMD_CLEAR_TEST = "cleartest";
private static final String TEST_CMD_DUMP_POLICY = "dumppolicy";
- private static final String TEST_CMD_SET_POLICY_GROUP = "setpolicygroup";
- private static final String TEST_CMD_APPLY_POLICY = "applypolicy";
- private static final String TEST_CMD_CLOSE_DATAFILE = "closefile";
+ private static final String TEST_CMD_ADD_POLICY_LISTENER = "addlistener";
+ private static final String TEST_CMD_REMOVE_POLICY_LISTENER = "removelistener";
+ private static final String TEST_CMD_DUMP_POLICY_LISTENER = "dumplistener";
+ private static final String TEST_CMD_WAIT_POLICY_LISTENERS = "waitlisteners";
+ private static final String TEST_CMD_RESET_POLICY_LISTENERS = "resetlisteners";
+ private static final int MAX_THREAD_POOL_SIZE = 1;
+ private final HashMap<String, PowerPolicyListenerImpl> mListenerMap = new HashMap<>();
+ private final Executor mExecutor = Executors.newFixedThreadPool(MAX_THREAD_POOL_SIZE);
+ private final PrintWriter mResultLog;
+ private String mCurrentTestcase = "Unknown";
private CarPowerManager mPowerManager;
- private long mClientStartTime;
- // This method is not intended for multi-threaded calls.
- public void handleCommand(PowerPolicyTestCommand cmd, PrintWriter resultLog) {
- cmd.execute(this, resultLog);
+ PowerPolicyTestClient(PrintWriter resultLog) {
+ mResultLog = resultLog;
+ }
+
+ public void printResult(String msg) {
+ if (msg != null) {
+ mResultLog.print(msg);
+ }
+ }
+
+ public void printlnResult() {
+ mResultLog.println();
+ }
+
+ public void printlnResult(@Nullable String msg) {
+ if (msg == null) {
+ mResultLog.println();
+ return;
+ }
+ mResultLog.println(msg);
+ }
+
+ public void printfResult(String format, Object... args) {
+ mResultLog.printf(format, args);
+ }
+
+ public void printResultHeader(String msg) {
+ mResultLog.printf("%s%s: %s ## ", TEST_RESULT_HEADER, mCurrentTestcase, msg);
}
@Nullable
public PowerPolicyTestCommand parseCommand(Bundle intentExtras) {
- String testcase;
- String action;
- String data;
PowerPolicyTestCommand cmd = null;
String cmdStr = intentExtras.getString(POWERPOLICY_TEST_CMD_IDENTIFIER);
if (cmdStr == null) {
Log.d(TAG, "empty power test command");
return cmd;
}
+ Log.d(TAG, "parseCommand with: " + cmdStr);
String[] tokens = cmdStr.split(",");
- int paramCount = tokens.length;
- if (paramCount != 2 && paramCount != 3) {
- throw new IllegalArgumentException("invalid command syntax: " + cmdStr);
- }
-
- testcase = tokens[0];
- action = tokens[1];
- if (paramCount == 3) {
- data = tokens[2];
- } else {
- data = null;
- }
-
- switch (action) {
- case TEST_CMD_START:
- cmd = new PowerPolicyTestCommand.StartTestcaseCommand(testcase, mPowerManager);
+ assertNumberOfArgs(tokens);
+ switch (tokens[0]) {
+ case TEST_CMD_SET_TEST:
+ cmd = new PowerPolicyTestCommand.SetTestCommand(this, tokens[1]);
break;
- case TEST_CMD_END:
- cmd = new PowerPolicyTestCommand.EndTestcaseCommand(testcase, mPowerManager);
- break;
- case TEST_CMD_DUMP_STATE:
- cmd = new PowerPolicyTestCommand.DumpStateCommand(testcase, mPowerManager);
+ case TEST_CMD_CLEAR_TEST:
+ cmd = new PowerPolicyTestCommand.ClearTestCommand(this);
break;
case TEST_CMD_DUMP_POLICY:
- cmd = new PowerPolicyTestCommand.DumpPolicyCommand(testcase, mPowerManager);
+ cmd = new PowerPolicyTestCommand.DumpPolicyCommand(this);
break;
- case TEST_CMD_SET_POLICY_GROUP:
- if (paramCount != 3) {
- throw new IllegalArgumentException("invalid cmd syntax: " + cmdStr);
- }
- cmd = new PowerPolicyTestCommand.SetPolicyGroupCommand(testcase, mPowerManager);
- cmd.mPolicyData = data;
+ case TEST_CMD_ADD_POLICY_LISTENER:
+ cmd = new PowerPolicyTestCommand.AddListenerCommand(this, tokens[1]);
break;
- case TEST_CMD_APPLY_POLICY:
- if (paramCount != 3) {
- throw new IllegalArgumentException("invalid cmd syntax: " + cmdStr);
- }
- cmd = new PowerPolicyTestCommand.ApplyPolicyCommand(testcase, mPowerManager);
- cmd.mPolicyData = data;
+ case TEST_CMD_REMOVE_POLICY_LISTENER:
+ cmd = new PowerPolicyTestCommand.RemoveListenerCommand(this, tokens[1]);
+ break;
+ case TEST_CMD_DUMP_POLICY_LISTENER:
+ cmd = new PowerPolicyTestCommand.DumpListenerCommand(this, tokens[1]);
+ break;
+ case TEST_CMD_WAIT_POLICY_LISTENERS:
+ cmd = new PowerPolicyTestCommand.WaitListenersCommand(this);
+ break;
+ case TEST_CMD_RESET_POLICY_LISTENERS:
+ cmd = new PowerPolicyTestCommand.ResetListenersCommand(this);
break;
default:
throw new IllegalArgumentException("invalid power policy test command: "
+ cmdStr);
}
-
- Log.i(TAG, "testcase=" + testcase + ", command=" + action);
return cmd;
}
@@ -111,12 +128,89 @@
mPowerManager = pm;
}
+ public CarPowerManager getPowerManager() {
+ return mPowerManager;
+ }
+
+ public void setTestcase(String testcase) {
+ mCurrentTestcase = testcase;
+ }
+
+ public void clearTestcase() {
+ mCurrentTestcase = "Unknown";
+ }
+
+ public String getTestcase() {
+ return mCurrentTestcase;
+ }
+
public void cleanup() {
//TODO(b/183134882): add any necessary cleanup activities here
}
- public void registerAndGo() {
- mClientStartTime = SystemClock.uptimeMillis();
- //TODO(b/183134882): here is the place to add listeners
+ public void registerPowerPolicyListener(String compName) throws Exception {
+ int compId = PowerComponentUtil.toPowerComponent(compName);
+ if (compId == PowerComponentUtil.INVALID_POWER_COMPONENT) {
+ throw new IllegalArgumentException("invalid power component: " + compName);
+ }
+
+ if (mListenerMap.containsKey(compName)) {
+ throw new IllegalArgumentException("duplicated power component listener: " + compName);
+ }
+
+ PowerPolicyListenerImpl listener = new PowerPolicyListenerImpl(this, compName);
+ CarPowerPolicyFilter filter = new CarPowerPolicyFilter.Builder()
+ .setComponents(compId).build();
+ mPowerManager.addPowerPolicyListener(mExecutor, filter, listener);
+ mListenerMap.put(compName, listener);
+ Log.d(TAG, "registered policy listener: " + compName);
+ }
+
+ public void unregisterPowerPolicyListener(String compName) throws Exception {
+ PowerPolicyListenerImpl listener = mListenerMap.remove(compName);
+ if (listener == null) {
+ throw new IllegalArgumentException("no power component listener: " + compName);
+ }
+ mPowerManager.removePowerPolicyListener(listener);
+ Log.d(TAG, "unregistered policy listener: " + compName);
+ }
+
+ @Nullable
+ public CarPowerPolicy getListenerCurrentPolicy(String compName) throws Exception {
+ PowerPolicyListenerImpl listener = mListenerMap.get(compName);
+ if (listener == null) {
+ throw new IllegalArgumentException("no power component listener: " + compName);
+ }
+ return listener.getCurrentPolicy();
+ }
+
+ public void resetPowerPolicyListeners() {
+ mListenerMap.values().stream().forEach(l -> l.reset());
+ }
+
+ public boolean arePowerPolicyListenersUpdated() {
+ return mListenerMap.values().stream().allMatch(l -> l.getCurrentPolicy() != null);
+ }
+
+ private void assertNumberOfArgs(String[] tokens) {
+ if (tokens.length < 1) {
+ throw new IllegalArgumentException("expects at least one token");
+ }
+
+ int expectedParamCount = 1;
+ switch (tokens[0]) {
+ case TEST_CMD_SET_TEST:
+ case TEST_CMD_ADD_POLICY_LISTENER:
+ case TEST_CMD_REMOVE_POLICY_LISTENER:
+ case TEST_CMD_DUMP_POLICY_LISTENER:
+ expectedParamCount = 2;
+ break;
+ }
+
+ if (expectedParamCount != tokens.length) {
+ String msg = String.format("%s expects %d params but received %d",
+ tokens[0], expectedParamCount, tokens.length);
+ throw new IllegalArgumentException(msg);
+ }
}
}
diff --git a/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestCommand.java b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestCommand.java
index c665198..65d086f 100644
--- a/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestCommand.java
+++ b/hostsidetests/car/app/src/android/car/cts/app/PowerPolicyTestCommand.java
@@ -16,154 +16,212 @@
package android.car.cts.app;
-import android.car.hardware.power.CarPowerManager;
import android.car.hardware.power.CarPowerPolicy;
import android.util.Log;
-import java.io.PrintWriter;
+import com.android.compatibility.common.util.PollingCheck;
+
import java.util.Arrays;
public abstract class PowerPolicyTestCommand {
- private static final String TAG = PowerPolicyTestCommand.class.getSimpleName();
- private static final String TEST_RESULT_HEADER = "PowerPolicyTestResult: ";
-
- private final String mTestcase;
- private final TestCommandType mType;
- protected final CarPowerManager mPowerManager;
-
- protected String mPolicyData;
-
- PowerPolicyTestCommand(String tc, CarPowerManager pm, TestCommandType type) {
- mTestcase = tc;
- mPowerManager = pm;
- mType = type;
+ enum TestCommandType {
+ SET_TEST,
+ CLEAR_TEST,
+ DUMP_POLICY,
+ ADD_LISTENER,
+ REMOVE_LISTENER,
+ DUMP_LISTENER,
+ RESET_LISTENERS,
+ WAIT_LISTENERS
}
- String getTestcase() {
- return mTestcase;
+ private static final String TAG = PowerPolicyTestCommand.class.getSimpleName();
+ private static final String NO_POLICY = "null";
+ private static final int TEST_WAIT_DURATION_MS = 5_000;
+
+ private final TestCommandType mType;
+ protected final PowerPolicyTestClient mTestClient;
+ protected final String mData;
+
+ PowerPolicyTestCommand(PowerPolicyTestClient testClient, String data, TestCommandType type) {
+ mTestClient = testClient;
+ mData = data;
+ mType = type;
}
TestCommandType getType() {
return mType;
}
- abstract void execute(PowerPolicyTestClient testClient, PrintWriter resultLog);
+ public abstract void execute();
- enum TestCommandType {
- START,
- END,
- DUMP_STATE,
- DUMP_POLICY,
- APPLY_POLICY,
- SET_POLICY_GROUP
+ public String getData() {
+ return mData;
}
- protected void printResultHeader(PrintWriter resultLog, String action) {
- resultLog.printf("%s%s:%s:", TEST_RESULT_HEADER, getTestcase(), action);
+ public PowerPolicyTestClient getTestClient() {
+ return mTestClient;
}
- static final class StartTestcaseCommand extends PowerPolicyTestCommand {
- StartTestcaseCommand(String tc, CarPowerManager pm) {
- super(tc, pm, TestCommandType.START);
+ static final class SetTestCommand extends PowerPolicyTestCommand {
+ SetTestCommand(PowerPolicyTestClient testClient, String data) {
+ super(testClient, data, TestCommandType.SET_TEST);
}
@Override
- void execute(PowerPolicyTestClient testClient, PrintWriter resultLog) {
- testClient.registerAndGo();
- Log.d(TAG, String.format("%s starts", getTestcase()));
+ public void execute() {
+ mTestClient.printResultHeader(getType().name());
+ mTestClient.printlnResult(mData);
+ mTestClient.setTestcase(mData);
+ Log.d(TAG, "setTestcase: " + mData);
}
}
- static final class EndTestcaseCommand extends PowerPolicyTestCommand {
- EndTestcaseCommand(String tc, CarPowerManager pm) {
- super(tc, pm, TestCommandType.END);
+ static final class ClearTestCommand extends PowerPolicyTestCommand {
+ ClearTestCommand(PowerPolicyTestClient testClient) {
+ super(testClient, /* data = */ null, TestCommandType.CLEAR_TEST);
}
@Override
- void execute(PowerPolicyTestClient testClient, PrintWriter resultLog) {
- testClient.cleanup();
- Log.d(TAG, getTestcase() + "ends");
- }
- }
-
- static final class DumpStateCommand extends PowerPolicyTestCommand {
- DumpStateCommand(String tc, CarPowerManager pm) {
- super(tc, pm, TestCommandType.DUMP_STATE);
- }
-
- @Override
- void execute(PowerPolicyTestClient testClient, PrintWriter resultLog) {
- int curState = mPowerManager.getPowerState();
- printResultHeader(resultLog, "dumpstate");
- resultLog.println(curState);
- Log.d(TAG, "current pwer state is " + curState);
+ public void execute() {
+ mTestClient.clearTestcase();
+ mTestClient.printResultHeader(getType().name());
+ mTestClient.printlnResult();
+ Log.d(TAG, "clearTestcase: " + mTestClient.getTestcase());
}
}
static final class DumpPolicyCommand extends PowerPolicyTestCommand {
- DumpPolicyCommand(String tc, CarPowerManager pm) {
- super(tc, pm, TestCommandType.DUMP_POLICY);
+ DumpPolicyCommand(PowerPolicyTestClient testClient) {
+ super(testClient, /* data = */ null, TestCommandType.DUMP_POLICY);
}
@Override
- void execute(PowerPolicyTestClient testClient, PrintWriter resultLog) {
- String policyId;
- CarPowerPolicy cpp = mPowerManager.getCurrentPowerPolicy();
+ public void execute() {
+ CarPowerPolicy cpp = mTestClient.getPowerManager().getCurrentPowerPolicy();
if (cpp == null) {
Log.d(TAG, "null current power policy");
return;
}
- policyId = cpp.getPolicyId();
- int[] enabledComponents = cpp.getEnabledComponents();
- int[] disabledComponents = cpp.getDisabledComponents();
+ String policyId = cpp.getPolicyId();
if (policyId == null) {
- policyId = "null";
+ policyId = NO_POLICY;
}
- printResultHeader(resultLog, "dumppolicy");
- resultLog.printf("policyId=%s, ", policyId);
- resultLog.printf("enabledComponents=[%s], ", Arrays.toString(enabledComponents));
- resultLog.printf("disabledComponents=[%s]\n", Arrays.toString(disabledComponents));
+ String[] enables = Arrays.stream(cpp.getEnabledComponents())
+ .mapToObj(PowerComponentUtil::componentToString)
+ .toArray(String[]::new);
+ String[] disables = Arrays.stream(cpp.getDisabledComponents())
+ .mapToObj(PowerComponentUtil::componentToString)
+ .toArray(String[]::new);
+ mTestClient.printResultHeader(getType().name());
+ mTestClient.printfResult("%s (", policyId);
+ mTestClient.printfResult("enabledComponents:%s ", String.join(",", enables));
+ mTestClient.printfResult("disabledComponents:%s)\n", String.join(",", disables));
+
Log.d(TAG, "dump power policy " + policyId);
}
}
- static final class SetPolicyGroupCommand extends PowerPolicyTestCommand {
- SetPolicyGroupCommand(String tc, CarPowerManager pm) {
- super(tc, pm, TestCommandType.SET_POLICY_GROUP);
+ static final class AddListenerCommand extends PowerPolicyTestCommand {
+ AddListenerCommand(PowerPolicyTestClient testClient, String compName) {
+ super(testClient, compName, TestCommandType.ADD_LISTENER);
}
@Override
- void execute(PowerPolicyTestClient testClient, PrintWriter resultLog) {
- if (mPolicyData == null) {
- Log.e(TAG, "null policy group id");
- return;
+ public void execute() {
+ Log.d(TAG, "addListener: " + mTestClient.getTestcase());
+ mTestClient.printResultHeader(getType().name());
+ try {
+ mTestClient.registerPowerPolicyListener(mData);
+ mTestClient.printlnResult("succeed");
+ } catch (Exception e) {
+ mTestClient.printlnResult("failed");
+ Log.e(TAG, "failed to addListener", e);
}
-
- mPowerManager.setPowerPolicyGroup(mPolicyData);
- printResultHeader(resultLog, "setpolicygroup");
- resultLog.println(mPolicyData);
- Log.d(TAG, "set policy group Id: " + mPolicyData);
}
}
- static final class ApplyPolicyCommand extends PowerPolicyTestCommand {
- ApplyPolicyCommand(String tc, CarPowerManager pm) {
- super(tc, pm, TestCommandType.APPLY_POLICY);
+ static final class RemoveListenerCommand extends PowerPolicyTestCommand {
+ RemoveListenerCommand(PowerPolicyTestClient testClient, String compName) {
+ super(testClient, compName, TestCommandType.REMOVE_LISTENER);
}
@Override
- void execute(PowerPolicyTestClient testClient, PrintWriter resultLog) {
- if (mPolicyData == null) {
- Log.w(TAG, "missing policy id for applying policy");
- return;
+ public void execute() {
+ Log.d(TAG, "removeListener: " + mTestClient.getTestcase());
+ mTestClient.printResultHeader(getType().name());
+ try {
+ mTestClient.unregisterPowerPolicyListener(mData);
+ mTestClient.printlnResult("succeed");
+ } catch (Exception e) {
+ mTestClient.printlnResult("failed");
+ Log.e(TAG, "failed to removeListener", e);
}
+ }
+ }
- mPowerManager.applyPowerPolicy(mPolicyData);
- printResultHeader(resultLog, "applypolicy");
- resultLog.println(mPolicyData);
- Log.d(TAG, "apply policy with Id: " + mPolicyData);
+ static final class DumpListenerCommand extends PowerPolicyTestCommand {
+ DumpListenerCommand(PowerPolicyTestClient testClient, String compName) {
+ super(testClient, compName, TestCommandType.DUMP_LISTENER);
+ }
+
+ @Override
+ public void execute() {
+ Log.d(TAG, "dumpListener: " + mTestClient.getTestcase());
+ mTestClient.printResultHeader(getType().name() + ": " + mData);
+ try {
+ CarPowerPolicy policy = mTestClient.getListenerCurrentPolicy(mData);
+ String policyStr = NO_POLICY;
+ if (policy != null) {
+ policyStr = PowerPolicyListenerImpl.getPolicyString(policy);
+ }
+ mTestClient.printlnResult(policyStr);
+ Log.d(TAG, "received power policy: " + policyStr);
+ } catch (Exception e) {
+ mTestClient.printlnResult("not_registered");
+ Log.d(TAG, "failed to find registered policy " + mData, e);
+ }
+ }
+ }
+
+ static final class WaitListenersCommand extends PowerPolicyTestCommand {
+ WaitListenersCommand(PowerPolicyTestClient testClient) {
+ super(testClient, /* data = */ null, TestCommandType.WAIT_LISTENERS);
+ }
+
+ @Override
+ public void execute() {
+ Log.d(TAG, "waitListeners: " + mTestClient.getTestcase());
+ mTestClient.printResultHeader(getType().name());
+ try {
+ PollingCheck.check("policy change isn't propagated", TEST_WAIT_DURATION_MS,
+ () -> mTestClient.arePowerPolicyListenersUpdated());
+ mTestClient.printlnResult("propagated");
+ Log.d(TAG, "policy change is propagated");
+ } catch (Exception e) {
+ mTestClient.printlnResult("not_propagated");
+ Log.d(TAG, "failed propagate power policy", e);
+ }
+ }
+ }
+
+ static final class ResetListenersCommand extends PowerPolicyTestCommand {
+ ResetListenersCommand(PowerPolicyTestClient testClient) {
+ super(testClient, /* data = */ null, TestCommandType.RESET_LISTENERS);
+ }
+
+ @Override
+ public void execute() {
+ Log.d(TAG, "resetListeners: " + mTestClient.getTestcase());
+ mTestClient.printResultHeader(getType().name());
+ try {
+ mTestClient.resetPowerPolicyListeners();
+ mTestClient.printlnResult("succeed");
+ } catch (Exception e) {
+ mTestClient.printlnResult("failed");
+ }
}
}
}
diff --git a/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java b/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java
index 62ab22e..38fb862 100644
--- a/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java
+++ b/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java
@@ -21,10 +21,13 @@
import android.car.cts.powerpolicy.PowerPolicyConstants;
import android.car.cts.powerpolicy.PowerPolicyDef;
import android.car.cts.powerpolicy.PowerPolicyGroups;
+import android.car.cts.powerpolicy.PowerPolicyTestAnalyzer;
import android.car.cts.powerpolicy.PowerPolicyTestHelper;
+import android.car.cts.powerpolicy.PowerPolicyTestResult;
import android.car.cts.powerpolicy.SilentModeInfo;
import android.car.cts.powerpolicy.SystemInfoParser;
+import com.android.compatibility.common.util.CommonTestUtils;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
@@ -37,7 +40,12 @@
private static final String ANDROID_CLIENT_PKG = "android.car.cts.app";
private static final String ANDROID_CLIENT_ACTIVITY = ANDROID_CLIENT_PKG
+ "/.PowerPolicyTestActivity";
- private static final String POWER_POLICY_TEST_RESULT_HEADER = "PowerPolicyTestClientResult";
+ private static final String TEST_COMMAND_HEADER =
+ "am start -n " + ANDROID_CLIENT_ACTIVITY + " --es powerpolicy ";
+
+ private static final int DEFAULT_TIMEOUT_SEC = 20;
+
+ private final PowerPolicyTestAnalyzer mTestAnalyzer = new PowerPolicyTestAnalyzer(this);
@Before
public void checkPrecondition() throws Exception {
@@ -129,27 +137,58 @@
testHelper = getTestHelper(testcase, 6, teststep);
testHelper.checkCurrentPolicy(PowerPolicyDef.IdSet.DEFAULT_ALL_ON);
+ // add "test power policy listener" here so that one reboot clears all
+ defineAndCheckPolicyListenerTest(testcase, 7, ++expectedTotalPolicies);
+ String clientTestcase = "PowerPolicyListenerTest";
+ PowerPolicyTestResult testResult = new PowerPolicyTestResult(mTestAnalyzer);
+ String clientAction = "DUMP_LISTENER";
+ String component = "AUDIO";
+
+ setClientTestcase(clientTestcase);
+ int currentNumberListeners = getNumberPolicyListeners();
+ registerPowerPolicyListener(component);
+ resetPowerPolicyListeners();
+ waitUntilNumberPolicyListenersEquals(++currentNumberListeners);
+ applyPowerPolicy(PowerPolicyDef.IdSet.LISTENER_TEST);
+ waitPowerPolicyListenersUpdated();
+
+ dumpPowerPolicyListener(component);
+ testResult.checkLastTestResultEntry(clientTestcase, clientAction,
+ component, PowerPolicyDef.PolicySet.LISTENER_TEST);
+
+ unregisterPowerPolicyListener(component);
+ applyPowerPolicy(PowerPolicyDef.IdSet.DEFAULT_ALL_ON);
+ waitPowerPolicyListenersUpdated();
+
+ dumpPowerPolicyListener(component);
+ testResult.checkLastTestResultEntry(clientTestcase, clientAction,
+ component, "not_registered");
+ clearClientTestcase();
+
// add power policy group test here to utilize added test1 and test2 policies
teststep = "check default power policy group";
PowerPolicyGroups emptyGroups = new PowerPolicyGroups();
- testHelper = getTestHelper(testcase, 7, teststep);
+ testHelper = getTestHelper(testcase, 8, teststep);
testHelper.checkCurrentPolicyGroupId(null);
testHelper.checkPowerPolicyGroups(emptyGroups);
teststep = "define power policy group";
definePowerPolicyGroup(PowerPolicyGroups.TestSet.POLICY_GROUP_DEF1.toShellCommandString());
definePowerPolicyGroup(PowerPolicyGroups.TestSet.POLICY_GROUP_DEF2.toShellCommandString());
- testHelper = getTestHelper(testcase, 8, teststep);
+ testHelper = getTestHelper(testcase, 9, teststep);
testHelper.checkPowerPolicyGroups(PowerPolicyGroups.TestSet.POLICY_GROUPS1);
teststep = "set power policy group";
setPowerPolicyGroup(PowerPolicyGroups.TestSet.GROUP_ID1);
- testHelper = getTestHelper(testcase, 9, teststep);
+ testHelper = getTestHelper(testcase, 10, teststep);
testHelper.checkCurrentPolicyGroupId(PowerPolicyGroups.TestSet.GROUP_ID1);
+ // reboot device to clear created TEST1 and TEST2 test cases.
+ // need to find a way to move reboot device into AfterAll
rebootDevice();
teststep = "reboot to clear added test power policies";
- testHelper = getTestHelper(testcase, 10, teststep);
+ testHelper = getTestHelper(testcase, 11, teststep);
+
expectedTotalPolicies = PowerPolicyDef.PolicySet.TOTAL_DEFAULT_REGISTERED_POLICIES;
testHelper.checkCurrentState(PowerPolicyConstants.CarPowerState.ON);
testHelper.checkCurrentPolicy(PowerPolicyDef.IdSet.DEFAULT_ALL_ON);
@@ -157,8 +196,7 @@
}
public String fetchActivityDumpsys() throws Exception {
- return executeCommand("shell dumpsys activity %s | grep %s",
- ANDROID_CLIENT_ACTIVITY, POWER_POLICY_TEST_RESULT_HEADER);
+ return executeCommand("dumpsys activity %s", ANDROID_CLIENT_ACTIVITY);
}
private PowerPolicyTestHelper getTestHelper(String testcase, int stepNo, String stepName)
@@ -221,6 +259,44 @@
executeCommand("cmd car_service set-power-policy-group %s", policyGroupId);
}
+ private void setClientTestcase(String testcase) throws Exception {
+ executeCommand("%s settest,%s", TEST_COMMAND_HEADER, testcase);
+ }
+
+ private void clearClientTestcase() throws Exception {
+ executeCommand("%s cleartest", TEST_COMMAND_HEADER);
+ }
+
+ private void registerPowerPolicyListener(String componentName) throws Exception {
+ executeCommand("%s addlistener,%s", TEST_COMMAND_HEADER, componentName);
+ }
+
+ private void unregisterPowerPolicyListener(String componentName) throws Exception {
+ executeCommand("%s removelistener,%s", TEST_COMMAND_HEADER, componentName);
+ }
+
+ private void dumpPowerPolicyListener(String componentName) throws Exception {
+ executeCommand("%s dumplistener,%s", TEST_COMMAND_HEADER, componentName);
+ }
+
+ private void waitPowerPolicyListenersUpdated() throws Exception {
+ executeCommand("%s waitlisteners", TEST_COMMAND_HEADER);
+ }
+
+ private void resetPowerPolicyListeners() throws Exception {
+ executeCommand("%s resetlisteners", TEST_COMMAND_HEADER);
+ }
+
+ private int getNumberPolicyListeners() throws Exception {
+ return getCpmsFrameworkLayerStateInfo().getNumberPolicyListeners();
+ }
+
+ private void waitUntilNumberPolicyListenersEquals(int numListeners) throws Exception {
+ CommonTestUtils.waitUntil("timed out (" + DEFAULT_TIMEOUT_SEC
+ + "s) getting number policy listeners", DEFAULT_TIMEOUT_SEC,
+ () -> (getNumberPolicyListeners() == numListeners));
+ }
+
private void waitForDeviceAvailable() throws Exception {
try {
getDevice().waitForDeviceAvailable();
@@ -257,4 +333,13 @@
testHelper.checkRegisteredPolicy(PowerPolicyDef.PolicySet.TEST2);
testHelper.checkTotalRegisteredPolicies(expectedTotalPolicies);
}
+
+ private void defineAndCheckPolicyListenerTest(String testcase, int stepNo,
+ int expectedTotalPolicies) throws Exception {
+ String teststep = stepNo + ". define a new power policy with id listener_test";
+ definePowerPolicy(PowerPolicyDef.PolicySet.LISTENER_TEST.toString());
+ PowerPolicyTestHelper testHelper = getTestHelper(testcase, stepNo, teststep);
+ testHelper.checkRegisteredPolicy(PowerPolicyDef.PolicySet.LISTENER_TEST);
+ testHelper.checkTotalRegisteredPolicies(expectedTotalPolicies);
+ }
}
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java b/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java
index 330ae50..4d1e77d 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/CpmsFrameworkLayerStateInfo.java
@@ -33,6 +33,7 @@
public static final String CURRENT_POLICY_ID_HDR = "mCurrentPowerPolicyId:";
public static final String PENDING_POLICY_ID_HDR = "mPendingPowerPolicyId:";
public static final String CURRENT_POLICY_GROUP_ID_HDR = "mCurrentPowerPolicyGroupId:";
+ public static final String NUMBER_POLICY_LISTENERS_HDR = "# of power policy change listener:";
public static final String POWER_POLICY_GROUPS_HDR = "Power policy groups:";
public static final String PREEMPTIVE_POWER_POLICY_HDR = "Preemptive power policy:";
public static final String COMPONENT_STATE_HDR = "Power components state:";
@@ -57,13 +58,14 @@
private final String mCurrentPolicyId;
private final String mPendingPolicyId;
private final String mCurrentPolicyGroupId;
+ private final int mNumberPolicyListeners;
private final boolean mMonitoringHw;
private final boolean mSilentModeByHw;
private final boolean mForcedSilentMode;
private final int mCurrentState;
private CpmsFrameworkLayerStateInfo(String currentPolicyId, String pendingPolicyId,
- String currentPolicyGroupId, String[] changedComponents,
+ String currentPolicyGroupId, int numberPolicyListeners, String[] changedComponents,
ArrayList<String> enables, ArrayList<String> disables, PowerPolicyGroups policyGroups,
ArrayList<String> controlledEnables, ArrayList<String> controlledDisables,
boolean monitoringHw, boolean silentModeByHw, boolean forcedSilentMode,
@@ -77,6 +79,7 @@
mCurrentPolicyId = currentPolicyId;
mPendingPolicyId = pendingPolicyId;
mCurrentPolicyGroupId = currentPolicyGroupId;
+ mNumberPolicyListeners = numberPolicyListeners;
mMonitoringHw = monitoringHw;
mSilentModeByHw = silentModeByHw;
mForcedSilentMode = forcedSilentMode;
@@ -115,6 +118,10 @@
return mPowerPolicyGroups;
}
+ public int getNumberPolicyListeners() {
+ return mNumberPolicyListeners;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(STRING_BUILDER_BUF_SIZE);
@@ -122,6 +129,7 @@
sb.append("mCurrentPolicyId=").append(mCurrentPolicyId).append(' ');
sb.append("mPendingPolicyId=").append(mPendingPolicyId).append(' ');
sb.append("mCurrentPolicyGroupId=").append(mCurrentPolicyGroupId).append(' ');
+ sb.append("mNumberPolicyListeners=").append(mNumberPolicyListeners).append(' ');
sb.append("silentmode=").append(mMonitoringHw).append(',');
sb.append(mSilentModeByHw).append(',').append(mForcedSilentMode).append(' ');
sb.append("enables=").append(String.join(",", mEnables)).append(' ');
@@ -141,6 +149,7 @@
&& mMonitoringHw == that.mMonitoringHw
&& mSilentModeByHw == that.mSilentModeByHw
&& mForcedSilentMode == that.mForcedSilentMode
+ && mNumberPolicyListeners == that.mNumberPolicyListeners
&& mEnables.equals(that.mEnables)
&& mDisables.equals(that.mDisables)
&& mPowerPolicyGroups.equals(that.mPowerPolicyGroups)
@@ -157,7 +166,7 @@
return Objects.hash(mEnables, mDisables, mControlledEnables, mControlledDisables,
mChangedComponents, mPowerPolicyGroups, mCurrentPolicyId, mPendingPolicyId,
mCurrentPolicyGroupId, mCurrentState, mMonitoringHw, mSilentModeByHw,
- mForcedSilentMode);
+ mForcedSilentMode, mNumberPolicyListeners);
}
public static CpmsFrameworkLayerStateInfo parse(String cmdOutput) throws Exception {
@@ -174,6 +183,7 @@
boolean monitoringHw = false;
boolean silentModeByHw = false;
boolean forcedSilentMode = false;
+ int numberPolicyListeners = 0;
String[] lines = cmdOutput.split("\n");
StateInfoParser parser = new StateInfoParser(lines);
@@ -227,6 +237,9 @@
case FORCED_SILENT_MODE_HDR:
forcedSilentMode = parser.getBooleanData(FORCED_SILENT_MODE_HDR);
break;
+ case NUMBER_POLICY_LISTENERS_HDR:
+ numberPolicyListeners = parser.getIntData(NUMBER_POLICY_LISTENERS_HDR);
+ break;
default:
throw new IllegalArgumentException("parser header mismatch: " + header);
}
@@ -240,9 +253,9 @@
}
return new CpmsFrameworkLayerStateInfo(currentPolicyId, pendingPolicyId,
- currentPolicyGroupId, changedComponents, enables, disables, policyGroups,
- controlledEnables, controlledDisables, monitoringHw, silentModeByHw,
- forcedSilentMode, currentState);
+ currentPolicyGroupId, numberPolicyListeners, changedComponents, enables,
+ disables, policyGroups, controlledEnables, controlledDisables, monitoringHw,
+ silentModeByHw, forcedSilentMode, currentState);
}
private static final class StateInfoParser {
@@ -251,6 +264,7 @@
CURRENT_POLICY_ID_HDR,
PENDING_POLICY_ID_HDR,
CURRENT_POLICY_GROUP_ID_HDR,
+ NUMBER_POLICY_LISTENERS_HDR,
COMPONENT_STATE_HDR,
COMPONENT_CONTROLLED_HDR,
POWER_POLICY_GROUPS_HDR,
@@ -287,6 +301,10 @@
}
val = Integer.parseInt(tokens[4].trim().substring(tokens[4].length() - 1));
break;
+ case NUMBER_POLICY_LISTENERS_HDR:
+ int strLen = mLines[mIdx].length();
+ val = Integer.parseInt(mLines[mIdx].substring(strLen - 1).trim());
+ break;
default:
break;
}
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java
index 61aaed4..fcf4bbc 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyDef.java
@@ -147,6 +147,7 @@
public static final String TEST2 = "test2";
public static final String ERROR_TEST1 = "error_test1";
public static final String ERROR_TEST2 = "error_test2";
+ public static final String LISTENER_TEST = "listener_test";
}
public enum PowerComponent {
@@ -235,6 +236,7 @@
PowerComponent.DISPLAY,
PowerComponent.CPU
};
+
static final PowerComponent[] INIT_ALL_ON_DISABLE = {
PowerComponent.MEDIA,
PowerComponent.BLUETOOTH,
@@ -261,6 +263,7 @@
PowerComponent.TRUSTED_DEVICE_DETECTION,
PowerComponent.CPU
};
+
static final PowerComponent[] NO_USER_INTERACT_DISABLE = {
PowerComponent.AUDIO,
PowerComponent.MEDIA,
@@ -326,6 +329,28 @@
PowerComponent.CPU
};
static final PowerComponent[] RUNTIME_SILENT_DISABLE = {};
+
+ static final PowerComponent[] LISTENER_TEST_ENABLE = {
+ PowerComponent.MEDIA,
+ PowerComponent.DISPLAY,
+ PowerComponent.BLUETOOTH,
+ PowerComponent.WIFI,
+ PowerComponent.CELLULAR,
+ PowerComponent.ETHERNET,
+ PowerComponent.PROJECTION,
+ PowerComponent.NFC,
+ PowerComponent.INPUT,
+ PowerComponent.VOICE_INTERACTION,
+ PowerComponent.VISUAL_INTERACTION,
+ PowerComponent.TRUSTED_DEVICE_DETECTION,
+ PowerComponent.LOCATION,
+ PowerComponent.CPU
+ };
+
+ static final PowerComponent[] LISTENER_TEST_DISABLE = {
+ PowerComponent.AUDIO,
+ PowerComponent.MICROPHONE
+ };
}
public static final class PolicySet {
@@ -354,6 +379,9 @@
public static final PowerPolicyDef ERROR_TEST2 = new PowerPolicyDef(IdSet.ERROR_TEST2,
ComponentList.ERROR_TEST2_ENABLE, ComponentList.ERROR_TEST2_DISABLE);
+
+ public static final PowerPolicyDef LISTENER_TEST = new PowerPolicyDef(IdSet.LISTENER_TEST,
+ ComponentList.LISTENER_TEST_ENABLE, ComponentList.LISTENER_TEST_DISABLE);
}
public static final class ComponentSet {
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestAnalyzer.java b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestAnalyzer.java
index 8e07555..e48978a 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestAnalyzer.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestAnalyzer.java
@@ -18,9 +18,8 @@
import android.car.cts.PowerPolicyHostTest;
-import com.android.tradefed.log.LogUtil.CLog;
-
public final class PowerPolicyTestAnalyzer {
+ private static final String TEST_RESULT_PREFIX = "PowerPolicyTestResult";
private final PowerPolicyHostTest mHostTest;
public PowerPolicyTestAnalyzer(PowerPolicyHostTest hostTest) {
@@ -30,13 +29,22 @@
/**
* Compares results.
*/
- public boolean checkIfTestResultMatch(TestResultTable result1, TestResultTable result2) {
+ public static boolean checkIfTestResultMatch(TestResultTable result1,
+ TestResultTable result2, boolean withPowerPolicyData) {
+ if ((result1 == null || result2 == null)) {
+ return result1 == result2;
+ }
+
int size = result1.size();
if (size != result2.size()) {
return false;
}
for (int i = 0; i < size; i++) {
- if (!result1.get(i).equals(result2.get(i))) {
+ TestResultTable.RecordEntry entry1 = result1.get(i);
+ TestResultTable.RecordEntry entry2 = result2.get(i);
+ boolean status = withPowerPolicyData
+ ? entry1.equalsWithPowerPolicyData(entry2) : entry1.equals(entry2);
+ if (!status) {
return false;
}
}
@@ -44,28 +52,55 @@
}
public TestResultTable snapshotTestResult() throws Exception {
- TestResultTable snapshot = new TestResultTable();
String shellOutput = mHostTest.fetchActivityDumpsys();
+ return getTestResults(shellOutput, false);
+ }
+
+ public TestResultTable getLatestTestResultEntry() throws Exception {
+ String shellOutput = mHostTest.fetchActivityDumpsys();
+ return getTestResults(shellOutput, true);
+ }
+
+ private TestResultTable getTestResults(String shellOutput, boolean onlyLastEntry)
+ throws Exception {
+ TestResultTable snapshot = new TestResultTable();
String[] lines = shellOutput.split("\n");
- for (String line : lines) {
- String[] tokens = line.split(",");
- if (tokens.length != 3 && tokens.length != 4) {
- CLog.w("Malformatted power policy test result: %s", line);
- return null;
- }
- if (tokens.length == 3) {
- snapshot.add(tokens[0], tokens[1], tokens[2], null);
- } else {
- snapshot.add(tokens[0], tokens[1], tokens[2], tokens[3]);
- }
+
+ int i = 0;
+ if (onlyLastEntry) {
+ i = lines.length == 0 ? 0 : lines.length - 1;
}
+ for (; i < lines.length; i++) {
+ parseAndAddTestEntry(snapshot, lines[i]);
+ }
+
return snapshot;
}
+ private void parseAndAddTestEntry(TestResultTable results, String line) throws Exception {
+ if (!line.trim().startsWith(TEST_RESULT_PREFIX)) {
+ return;
+ }
+
+ String[] tokens = line.split("##");
+ if (tokens.length != 2) {
+ throw new IllegalArgumentException("malformatted result entry: " + line);
+ }
+ String header = tokens[0].trim();
+ String data = tokens[1].trim();
+ String[] hdrTokens = header.split(":\\s*");
+ if (hdrTokens.length != 3 && hdrTokens.length != 4) {
+ throw new IllegalArgumentException("malformatted result header: " + line);
+ }
+
+ String subject = hdrTokens.length == 3 ? null : hdrTokens[3];
+ results.add(hdrTokens[1], hdrTokens[2], subject, data);
+ }
+
/**
* Subtract the common front TestResultEntry items.
*/
- public TestResultTable getDiff(TestResultTable result1, TestResultTable result2) {
+ public static TestResultTable getDiff(TestResultTable result1, TestResultTable result2) {
TestResultTable diff;
if (result1 != null && result2 != null) {
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestResult.java b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestResult.java
index dfbbe40..5e5a125 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestResult.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/PowerPolicyTestResult.java
@@ -16,34 +16,32 @@
package android.car.cts.powerpolicy;
-import com.android.tradefed.log.LogUtil.CLog;
+import static com.google.common.truth.Truth.assertWithMessage;
public final class PowerPolicyTestResult {
- private static final String TESTCASE_NAME_HEADER = "Testcase";
private final PowerPolicyTestAnalyzer mTestAnalyzer;
private final TestResultTable mExpected = new TestResultTable();
private TestResultTable mStartSnapshot;
private TestResultTable mEndSnapshot;
- private final int mTestcaseNo;
- private final String mTestcaseName;
- public PowerPolicyTestResult(int caseNo, PowerPolicyTestAnalyzer testAnalyzer) {
- mTestcaseNo = caseNo;
- mTestcaseName = TESTCASE_NAME_HEADER + caseNo;
+ public PowerPolicyTestResult(PowerPolicyTestAnalyzer testAnalyzer) {
mTestAnalyzer = testAnalyzer;
}
- public int getTestcaseNo() {
- return mTestcaseNo;
- }
-
/**
* Adds test passing criteria.
*
* <p> For multiple criteria, the order of adding them into this object matters.
*/
- public void addCriteria(String action, String powerState, String data) {
- mExpected.add(mTestcaseName, action, powerState, data);
+ public void addCriteria(String testcase, String action, String subject, String data)
+ throws Exception {
+ if (testcase == null || action == null) {
+ throw new IllegalArgumentException("testcase and action should not be null");
+ }
+ if (data == null) {
+ data = "null";
+ }
+ mExpected.add(testcase, action, subject, data);
}
public void takeStartSnapshot() throws Exception {
@@ -60,19 +58,35 @@
mEndSnapshot = mTestAnalyzer.snapshotTestResult();
}
- public boolean checkTestStatus() {
+ public void checkFullTestResult() throws Exception {
TestResultTable testResult;
if (mStartSnapshot == null || mEndSnapshot == null) {
- CLog.e("start snapshot or end snapshot is null");
- return false;
+ throw new IllegalArgumentException("start snapshot or end snapshot is null");
}
-
testResult = mTestAnalyzer.getTailDiff(mStartSnapshot, mEndSnapshot);
if (testResult == null) {
- CLog.e("empty test result");
- return false;
+ throw new IllegalArgumentException("empty test result");
}
+ assertWithMessage("checkFullTestresult")
+ .that(mTestAnalyzer.checkIfTestResultMatch(mExpected, testResult, false))
+ .isTrue();
+ }
- return mTestAnalyzer.checkIfTestResultMatch(mExpected, testResult);
+ public void checkLastTestResultEntry(String testcase, String action,
+ String subject, PowerPolicyDef policy) throws Exception {
+ TestResultTable.RecordEntry lastEntry = mTestAnalyzer.snapshotTestResult().getLastEntry();
+ assertWithMessage("checkLastTestEntry with policy data")
+ .that(lastEntry.equalsWithPowerPolicyData(testcase, action, subject, policy))
+ .isTrue();
+ }
+
+ public void checkLastTestResultEntry(String testcase, String action,
+ String subject, String data) throws Exception {
+ TestResultTable expected = new TestResultTable();
+ expected.add(testcase, action, subject, data);
+ TestResultTable.RecordEntry lastEntry1 = expected.getLastEntry();
+ TestResultTable.RecordEntry lastEntry2 = mTestAnalyzer.snapshotTestResult().getLastEntry();
+ assertWithMessage("checkLastTestEntry with string data")
+ .that(lastEntry1.equals(lastEntry2)).isTrue();
}
}
diff --git a/hostsidetests/car/src/android/car/cts/powerpolicy/TestResultTable.java b/hostsidetests/car/src/android/car/cts/powerpolicy/TestResultTable.java
index e54d293..4a11895 100644
--- a/hostsidetests/car/src/android/car/cts/powerpolicy/TestResultTable.java
+++ b/hostsidetests/car/src/android/car/cts/powerpolicy/TestResultTable.java
@@ -16,64 +16,82 @@
package android.car.cts.powerpolicy;
+import com.android.tradefed.log.LogUtil.CLog;
+
import java.util.ArrayList;
/**
- * TestResultTable consists of a list of TestResultEntry records
+ * TestResultTable consists of a list of RecordEntry records
*
* <p>Each record represents one entry line in the device data file,
* {@code /storage/emulated/obb/PowerPolicyData.txt}, which records the power
* state and policy behavior.
*/
public final class TestResultTable {
- private final ArrayList<TestResultEntry> mTestResults = new ArrayList<TestResultEntry>();
+ private final ArrayList<RecordEntry> mTestResults = new ArrayList<RecordEntry>();
public int size() {
return mTestResults.size();
}
- public TestResultEntry get(int i) throws IndexOutOfBoundsException {
+ public RecordEntry get(int i) throws IndexOutOfBoundsException {
return mTestResults.get(i);
}
- public void add(TestResultEntry entry) {
+ public RecordEntry getLastEntry() {
+ if (mTestResults.isEmpty()) {
+ return null;
+ }
+ return mTestResults.get(mTestResults.size() - 1);
+ }
+
+ public void add(RecordEntry entry) {
mTestResults.add(entry);
}
- public void add(String testcase, String action, String powerState, String data) {
- add(new TestResultEntry(testcase, action, powerState, data));
+ public void add(String testcase, String action, String subject, String data)
+ throws Exception {
+ if (testcase == null || action == null || data == null) {
+ throw new IllegalArgumentException("testcase, action or data can not be null");
+ }
+
+ add(new RecordEntry(testcase, action, subject, data));
}
- static final class TestResultEntry {
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = new StringBuilder();
+ mTestResults.forEach(l -> strBuilder.append(l).append('\n'));
+ return strBuilder.toString();
+ }
+
+ static final class RecordEntry {
private final String mTestcase;
private final String mAction;
- private final String mPowerState;
+ private final String mSubject;
private final String mData;
- TestResultEntry(String testcase, String action, String powerState, String data) {
+ private RecordEntry(String testcase, String action, String subject, String data) {
mTestcase = testcase;
mAction = action;
- mPowerState = powerState;
+ mSubject = subject;
mData = data;
}
@Override
public boolean equals(Object obj) {
- TestResultEntry peerEntry;
- if (!(obj instanceof TestResultEntry)) {
+ RecordEntry peerEntry;
+ if (!(obj instanceof RecordEntry)) {
return false;
}
- peerEntry = (TestResultEntry) obj;
- if ((mTestcase != null && !mTestcase.equals(peerEntry.mTestcase))
- || (mAction != null && !mAction.equals(peerEntry.mAction))
- || (mPowerState != null && !mPowerState.equals(peerEntry.mPowerState))
- || (mData != null && !mData.equals(peerEntry.mData))) {
+ peerEntry = (RecordEntry) obj;
+ if ((mSubject == null && null != peerEntry.mSubject)
+ || (mSubject != null && !mSubject.equals(peerEntry.mSubject))) {
return false;
}
- if ((mTestcase == null && null != peerEntry.mTestcase)
- || (mAction == null && null != peerEntry.mAction)
- || (mPowerState == null && null != peerEntry.mPowerState)
- || (mData == null && null != peerEntry.mData)) {
+ if (!mTestcase.equals(peerEntry.mTestcase)
+ || !mAction.equals(peerEntry.mAction)
+ || !mData.equals(peerEntry.mData)) {
return false;
}
return true;
@@ -81,11 +99,55 @@
@Override
public int hashCode() {
- int code = mTestcase != null ? mTestcase.hashCode() : 0;
- code += mAction != null ? mAction.hashCode() : 0;
- code += mPowerState != null ? mPowerState.hashCode() : 0;
- code += mData != null ? mData.hashCode() : 0;
+ int code = mTestcase.hashCode() + mAction.hashCode() + mData.hashCode();
+ code += mSubject != null ? mSubject.hashCode() : 0;
return code;
}
+
+ @Override
+ public String toString() {
+ StringBuilder strBuilder = (new StringBuilder())
+ .append(mTestcase).append(": ").append(mAction).append(": ");
+ if (mSubject != null) {
+ strBuilder.append(mSubject).append(": ");
+ }
+ return strBuilder.append(mData).toString();
+ }
+
+ public boolean equalsWithPowerPolicyData(RecordEntry peerEntry) {
+ PowerPolicyDef peerPolicy;
+ try {
+ peerPolicy = PowerPolicyDef.parse(/* policyDefStr= */ peerEntry.mData,
+ /* hasPolicyId= */ true, /* offset= */ 0);
+ } catch (Exception e) {
+ CLog.wtf("failed to parse policy string: " + peerEntry.mData, e);
+ return false;
+ }
+ return equalsWithPowerPolicyData(peerEntry.mTestcase, peerEntry.mAction,
+ peerEntry.mSubject, peerPolicy);
+ }
+
+ public boolean equalsWithPowerPolicyData(String testcase, String action,
+ String subject, PowerPolicyDef policy) {
+ if ((mSubject == null && null != subject)
+ || (mSubject != null && !mSubject.equals(subject))
+ || !mTestcase.equals(testcase)
+ || !mAction.equals(action)) {
+ return false;
+ }
+
+ try {
+ PowerPolicyDef myPolicy = PowerPolicyDef.parse(/* policyDefStr= */ mData,
+ /* hasPolicyId= */ true, /* offset= */ 0);
+ if (!myPolicy.equals(policy)) {
+ return false;
+ }
+ } catch (Exception e) {
+ CLog.wtf("failed to parse policy string: " + mData, e);
+ return false;
+ }
+
+ return true;
+ }
}
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
index c854b2a..88573d9 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/LockTaskTest.java
@@ -156,69 +156,6 @@
mContext.unregisterReceiver(mReceiver);
}
- // Verifies that the act of finishing is blocked by ActivityManager in lock task.
- // This results in onDestroy not being called until stopLockTask is called before finish.
- public void testCannotFinish() throws Exception {
- mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
- startLockTask(UTILITY_ACTIVITY);
-
- // If lock task has not exited then the activity shouldn't actually receive onDestroy.
- finishAndWait(UTILITY_ACTIVITY);
- assertLockTaskModeActive();
- assertTrue(mIsActivityRunning);
-
- stopAndFinish(UTILITY_ACTIVITY);
- }
-
- // Verifies that updating the allowlist during lock task mode finishes the locked task.
- public void testUpdateAllowlist() throws Exception {
- mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
- startLockTask(UTILITY_ACTIVITY);
-
- mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[0]);
-
- synchronized (mActivityRunningLock) {
- mActivityRunningLock.wait(ACTIVITY_DESTROYED_TIMEOUT_MILLIS);
- }
-
- assertLockTaskModeInactive();
- assertFalse(mIsActivityRunning);
- assertFalse(mIsActivityResumed);
- }
-
- // Verifies that removing the allowlist authorization immediately finishes the corresponding
- // locked task. The other locked task(s) should remain locked.
- public void testUpdateAllowlist_twoTasks() throws Exception {
- mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME,
- RECEIVER_ACTIVITY_PACKAGE_NAME});
-
- // Start first locked task
- startLockTask(UTILITY_ACTIVITY);
- waitForResume();
-
- // Start the other task from the running activity
- mIsReceiverActivityRunning = false;
- Intent launchIntent = createReceiverActivityIntent(true /*newTask*/, true /*shouldWait*/);
- mContext.startActivity(launchIntent);
- synchronized (mReceiverActivityRunningLock) {
- mReceiverActivityRunningLock.wait(ACTIVITY_RESUMED_TIMEOUT_MILLIS);
- assertTrue(mIsReceiverActivityRunning);
- }
-
- // Remove allowlist authorization of the second task
- mDevicePolicyManager.setLockTaskPackages(ADMIN_COMPONENT, new String[] { PACKAGE_NAME });
- synchronized (mReceiverActivityRunningLock) {
- mReceiverActivityRunningLock.wait(ACTIVITY_DESTROYED_TIMEOUT_MILLIS);
- assertFalse(mIsReceiverActivityRunning);
- }
-
- assertLockTaskModeActive();
- assertTrue(mIsActivityRunning);
- assertTrue(mIsActivityResumed);
-
- stopAndFinish(UTILITY_ACTIVITY);
- }
-
// This launches an activity that is in the current task.
// This should always be permitted as a part of lock task (since it isn't a new task).
public void testStartActivity_withinTask() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
index 22bd3f6..f717086 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
@@ -99,8 +99,9 @@
++updateCount;
}
}
+ // There might be auto-upgrade configs returned.
assertWithMessage("number of updated configs (the DO created one and the regular one)")
- .that(updateCount).isEqualTo(2);
+ .that(updateCount).isAtLeast(2);
}
public void testRegularAppCannotUpdateDeviceOwnerConfig() throws Exception {
@@ -115,8 +116,9 @@
++updateCount;
}
}
+ // There might be auto-upgrade configs returned.
assertWithMessage("number of updated configs (the DO created one)")
- .that(updateCount).isEqualTo(1);
+ .that(updateCount).isAtLeast(1);
// Assert nothing has changed
configs = mWifiConfigCreator.getConfiguredNetworks();
@@ -129,7 +131,8 @@
++notChangedCount;
}
}
- assertWithMessage("number of unchanged configs").that(notChangedCount).isEqualTo(1);
+ // There might be auto-upgrade configs returned.
+ assertWithMessage("number of unchanged configs").that(notChangedCount).isAtLeast(1);
}
public void testRegularAppCannotRemoveDeviceOwnerConfig() throws Exception {
@@ -145,8 +148,9 @@
}
}
+ // There might be auto-upgrade configs returned.
assertWithMessage("number of removed configs (the DO created one)")
- .that(removeCount).isEqualTo(1);
+ .that(removeCount).isAtLeast(1);
// Assert nothing has changed
configs = mWifiConfigCreator.getConfiguredNetworks();
@@ -157,7 +161,8 @@
++notChangedCount;
}
}
- assertWithMessage("number of unchanged configs").that(notChangedCount).isEqualTo(1);
+ // There might be auto-upgrade configs returned.
+ assertWithMessage("number of unchanged configs").that(notChangedCount).isAtLeast(1);
}
private void startRegularActivity(String action, int netId, String ssid, int securityType,
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/Android.bp b/hostsidetests/devicepolicy/app/SimpleApp/Android.bp
index b73f38a..3a822af 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/SimpleApp/Android.bp
@@ -26,7 +26,6 @@
"cts",
"general-tests",
"mts",
- "sts",
],
sdk_version: "current",
// V4 signature required by Incremental installs
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 560c3ed..aa92b77 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -102,95 +102,6 @@
}
}
- @LargeTest
- @Test
- @Ignore
- public void testAppLinks_verificationStatus() throws Exception {
- // Disable all pre-existing browsers in the managed profile so they don't interfere with
- // intents resolution.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testDisableAllBrowsers", mProfileUserId);
- installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
- installAppAsUser(INTENT_SENDER_APK, USER_ALL);
-
- changeVerificationStatus(mParentUserId, INTENT_RECEIVER_PKG, "ask");
- changeVerificationStatus(mProfileUserId, INTENT_RECEIVER_PKG, "ask");
- // We should have two receivers: IntentReceiverActivity and BrowserActivity in the
- // managed profile
- assertAppLinkResult("testTwoReceivers");
-
- changeUserRestrictionOrFail("allow_parent_profile_app_linking", true, mProfileUserId);
- // Now we should also have one receiver in the primary user, so three receivers in total.
- assertAppLinkResult("testThreeReceivers");
-
- changeVerificationStatus(mParentUserId, INTENT_RECEIVER_PKG, "never");
- // The primary user one has been set to never: we should only have the managed profile ones.
- assertAppLinkResult("testTwoReceivers");
-
- changeVerificationStatus(mProfileUserId, INTENT_RECEIVER_PKG, "never");
- // Now there's only the browser in the managed profile left
- assertAppLinkResult("testReceivedByBrowserActivityInManaged");
-
- changeVerificationStatus(mProfileUserId, INTENT_RECEIVER_PKG, "always");
- changeVerificationStatus(mParentUserId, INTENT_RECEIVER_PKG, "always");
- // We have one always in the primary user and one always in the managed profile: the managed
- // profile one should have precedence.
- assertAppLinkResult("testReceivedByAppLinkActivityInManaged");
- }
-
- @LargeTest
- @Test
- @Ignore
- public void testAppLinks_enabledStatus() throws Exception {
- // Disable all pre-existing browsers in the managed profile so they don't interfere with
- // intents resolution.
- runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "testDisableAllBrowsers", mProfileUserId);
- installAppAsUser(INTENT_RECEIVER_APK, USER_ALL);
- installAppAsUser(INTENT_SENDER_APK, USER_ALL);
-
- final String APP_HANDLER_COMPONENT = "com.android.cts.intent.receiver/.AppLinkActivity";
-
- // allow_parent_profile_app_linking is not set, try different enabled state combinations.
- // We should not have app link handler in parent user no matter whether it is enabled.
-
- disableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- disableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testReceivedByBrowserActivityInManaged");
-
- enableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- disableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testReceivedByBrowserActivityInManaged");
-
- disableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- enableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testTwoReceivers");
-
- enableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- enableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testTwoReceivers");
-
- // We now set allow_parent_profile_app_linking, and hence we should have the app handler
- // in parent user if it is enabled.
- changeUserRestrictionOrFail("allow_parent_profile_app_linking", true, mProfileUserId);
-
- disableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- disableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testReceivedByBrowserActivityInManaged");
-
- enableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- disableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testTwoReceivers");
-
- disableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- enableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testTwoReceivers");
-
- enableComponentOrPackage(mParentUserId, APP_HANDLER_COMPONENT);
- enableComponentOrPackage(mProfileUserId, APP_HANDLER_COMPONENT);
- assertAppLinkResult("testThreeReceivers");
- }
-
@Test
public void testSettingsIntents() throws Exception {
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".SettingsIntentsTest",
@@ -716,17 +627,4 @@
throws DeviceNotAvailableException {
return changeUserRestriction(key, value, userId, MANAGED_PROFILE_PKG);
}
-
- // status should be one of never, undefined, ask, always
- private void changeVerificationStatus(int userId, String packageName, String status)
- throws DeviceNotAvailableException {
- String command = "pm set-app-link --user " + userId + " " + packageName + " " + status;
- CLog.d("Output for command " + command + ": "
- + getDevice().executeShellCommand(command));
- }
-
- private void assertAppLinkResult(String methodName) throws DeviceNotAvailableException {
- runDeviceTestsAsUser(INTENT_SENDER_PKG, ".AppLinkTest", methodName,
- mProfileUserId);
- }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java
index e38ed50..ac20ace 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/SeparateProfileChallengeTest.java
@@ -16,15 +16,19 @@
package com.android.cts.devicepolicy;
+import static com.android.cts.devicepolicy.DeviceAdminFeaturesCheckerRule.FEATURE_MANAGED_USERS;
+
import android.platform.test.annotations.SecurityTest;
+import com.android.cts.devicepolicy.DeviceAdminFeaturesCheckerRule.RequiresAdditionalFeatures;
+
import org.junit.Test;
/**
* Host side tests for separate profile challenge permissions.
* Run the CtsSeparateProfileChallengeApp device side test.
*/
-
+@RequiresAdditionalFeatures({FEATURE_MANAGED_USERS})
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";
diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
index 0d6782c..654251c 100644
--- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
+++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/DeviceTestConstants.java
@@ -53,6 +53,9 @@
new TestInfo(PACKAGE, SERVICE_TEST, "testInputUnbindsOnAppStopped");
public static final TestInfo TEST_IME_VISIBILITY_AFTER_IME_SWITCHING =
new TestInfo(PACKAGE, SERVICE_TEST, "testImeVisibilityAfterImeSwitching");
+ public static final TestInfo TEST_IME_SWITCHING_WITHOUT_WINDOW_FOCUS_AFTER_DISPLAY_OFF_ON =
+ new TestInfo(PACKAGE, SERVICE_TEST,
+ "testImeSwitchingWithoutWindowFocusAfterDisplayOffOn");
/**
* Device test class: ShellCommandDeviceTest.
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
index 4833884..5be2cb4 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodServiceDeviceTest.java
@@ -34,7 +34,8 @@
import static android.inputmethodservice.cts.common.ImeCommandConstants.EXTRA_COMMAND;
import static android.inputmethodservice.cts.devicetest.MoreCollectors.startingFrom;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
+import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.inputmethodservice.cts.DeviceEvent;
@@ -320,24 +321,44 @@
"-e", EXTRA_ARG_STRING1, Ime2Constants.IME_ID));
InputMethodVisibilityVerifier.assertIme2Visible(TIMEOUT);
+ }
- // Make sure the IME switch UI still works after device screen off / on with focusing
- // same Editor.
- turnScreenOff(helper);
- turnScreenOn(helper);
- helper.shell(ShellCommandUtils.unlockScreen());
- assertTrue(helper.findUiObject(EditTextAppConstants.EDIT_TEXT_RES_NAME).isFocused());
+ /**
+ * Test IME switcher dialog after turning off/on the screen.
+ *
+ * <p>Regression test for Bug 160391516.</p>
+ */
+ @Test
+ public void testImeSwitchingWithoutWindowFocusAfterDisplayOffOn() throws Throwable {
+ final TestHelper helper = new TestHelper();
- // Switch IME from CtsInputMethod2 to CtsInputMethod1.
- showInputMethodPicker(helper);
- helper.shell(ShellCommandUtils.broadcastIntent(
- ACTION_IME_COMMAND, Ime2Constants.PACKAGE,
- "-e", EXTRA_COMMAND, COMMAND_SWITCH_INPUT_METHOD,
- "-e", EXTRA_ARG_STRING1, Ime1Constants.IME_ID));
+ helper.launchActivity(EditTextAppConstants.PACKAGE, EditTextAppConstants.CLASS,
+ EditTextAppConstants.URI);
+
+ helper.findUiObject(EditTextAppConstants.EDIT_TEXT_RES_NAME).click();
InputMethodVisibilityVerifier.assertIme1Visible(TIMEOUT);
- // Switch IME from CtsInputMethod1 to CtsInputMethod2.
+ turnScreenOff(helper);
+ turnScreenOn(helper);
+ helper.shell(ShellCommandUtils.dismissKeyguard());
+ helper.shell(ShellCommandUtils.unlockScreen());
+ {
+ final UiObject2 editText = helper.findUiObject(EditTextAppConstants.EDIT_TEXT_RES_NAME);
+ assumeNotNull("App's view focus behavior after turning off/on the screen is not fully"
+ + " guaranteed. If the IME is not shown here, just skip this test.",
+ editText);
+ assumeTrue("App's view focus behavior after turning off/on the screen is not fully"
+ + " guaranteed. If the IME is not shown here, just skip this test.",
+ editText.isFocused());
+ }
+
+ InputMethodVisibilityVerifier.assumeIme1Visible("IME behavior after turning off/on the"
+ + " screen is not fully guaranteed. If the IME is not shown here, just skip this.",
+ TIMEOUT);
+
+ // Emulating IME switching with the IME switcher dialog. An interesting point is that
+ // the IME target window is not focused when the IME switcher dialog is shown.
showInputMethodPicker(helper);
helper.shell(ShellCommandUtils.broadcastIntent(
ACTION_IME_COMMAND, Ime1Constants.PACKAGE,
diff --git a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodVisibilityVerifier.java b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodVisibilityVerifier.java
index 7501da8..cfb6f16 100644
--- a/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodVisibilityVerifier.java
+++ b/hostsidetests/inputmethodservice/deviceside/devicetest/src/android/inputmethodservice/cts/devicetest/InputMethodVisibilityVerifier.java
@@ -17,6 +17,7 @@
package android.inputmethodservice.cts.devicetest;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.app.Instrumentation;
import android.app.UiAutomation;
@@ -92,6 +93,16 @@
}
/**
+ * Assumes that IME1 is visible to the user.
+ *
+ * @param message message to be shown when the assumption is not satisfied.
+ * @param timeout timeout in milliseconds.
+ */
+ static void assumeIme1Visible(String message, long timeout) {
+ assumeTrue(message, waitUntilWatermarkBecomesVisible(timeout, Watermark.IME1));
+ }
+
+ /**
* Asserts that IME2 is visible to the user.
*
* @param timeout timeout in milliseconds.
diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
index 06465b5..846c31f 100644
--- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
+++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java
@@ -435,6 +435,47 @@
testImeVisibilityAfterImeSwitching(true);
}
+ private void testImeSwitchingWithoutWindowFocusAfterDisplayOffOn(boolean instant)
+ throws Exception {
+ sendTestStartEvent(
+ DeviceTestConstants.TEST_IME_SWITCHING_WITHOUT_WINDOW_FOCUS_AFTER_DISPLAY_OFF_ON);
+ installPossibleInstantPackage(
+ EditTextAppConstants.APK, EditTextAppConstants.PACKAGE, instant);
+ installImePackageSync(Ime1Constants.APK, Ime1Constants.IME_ID);
+ installImePackageSync(Ime2Constants.APK, Ime2Constants.IME_ID);
+ shell(ShellCommandUtils.enableIme(Ime1Constants.IME_ID));
+ shell(ShellCommandUtils.enableIme(Ime2Constants.IME_ID));
+ waitUntilImesAreEnabled(Ime1Constants.IME_ID, Ime2Constants.IME_ID);
+ shell(ShellCommandUtils.setCurrentImeSync(Ime1Constants.IME_ID));
+
+ assertTrue(runDeviceTestMethod(
+ DeviceTestConstants.TEST_IME_SWITCHING_WITHOUT_WINDOW_FOCUS_AFTER_DISPLAY_OFF_ON));
+ }
+
+ /**
+ * Test IME switching while another window (e.g. IME switcher dialog) is focused on top of the
+ * IME target window after turning off/on the screen.
+ *
+ * <p>Regression test for Bug 160391516.</p>
+ */
+ @AppModeFull
+ @Test
+ public void testImeSwitchingWithoutWindowFocusAfterDisplayOffOnFull() throws Exception {
+ testImeSwitchingWithoutWindowFocusAfterDisplayOffOn(false);
+ }
+
+ /**
+ * Test IME switching while another window (e.g. IME switcher dialog) is focused on top of the
+ * IME target window after turning off/on the screen.
+ *
+ * <p>Regression test for Bug 160391516.</p>
+ */
+ @AppModeInstant
+ @Test
+ public void testImeSwitchingWithoutWindowFocusAfterDisplayOffOnInstant() throws Exception {
+ testImeSwitchingWithoutWindowFocusAfterDisplayOffOn(true);
+ }
+
private void sendTestStartEvent(TestInfo deviceTest) throws Exception {
final String sender = deviceTest.getTestName();
// {@link EventType#EXTRA_EVENT_TIME} will be recorded at device side.
diff --git a/hostsidetests/media/app/MediaMetricsTest/src/android/media/metrics/cts/MediaMetricsAtomHostSideTests.java b/hostsidetests/media/app/MediaMetricsTest/src/android/media/metrics/cts/MediaMetricsAtomHostSideTests.java
index 442386d..cc2f21c 100644
--- a/hostsidetests/media/app/MediaMetricsTest/src/android/media/metrics/cts/MediaMetricsAtomHostSideTests.java
+++ b/hostsidetests/media/app/MediaMetricsTest/src/android/media/metrics/cts/MediaMetricsAtomHostSideTests.java
@@ -91,6 +91,52 @@
}
@Test
+ public void testTrackChangeEvent_audio() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ MediaMetricsManager manager = context.getSystemService(MediaMetricsManager.class);
+ PlaybackSession s = manager.createPlaybackSession();
+ TrackChangeEvent e =
+ new TrackChangeEvent.Builder(TrackChangeEvent.TRACK_TYPE_AUDIO)
+ .setTimeSinceCreatedMillis(37278L)
+ .setTrackState(TrackChangeEvent.TRACK_STATE_OFF)
+ .setTrackChangeReason(TrackChangeEvent.TRACK_CHANGE_REASON_INITIAL)
+ .setContainerMimeType("audio/foo")
+ .setSampleMimeType("audio/avc")
+ .setCodecName("codec_2")
+ .setBitrate(1025)
+ .setLanguage("EN")
+ .setLanguageRegion("US")
+ .setAudioSampleRate(89)
+ .setChannelCount(3)
+ .build();
+ s.reportTrackChangeEvent(e);
+ }
+
+ @Test
+ public void testTrackChangeEvent_video() throws Exception {
+ Context context = InstrumentationRegistry.getContext();
+ MediaMetricsManager manager = context.getSystemService(MediaMetricsManager.class);
+ PlaybackSession s = manager.createPlaybackSession();
+ TrackChangeEvent e =
+ new TrackChangeEvent.Builder(TrackChangeEvent.TRACK_TYPE_VIDEO)
+ .setTimeSinceCreatedMillis(37278L)
+ .setTrackState(TrackChangeEvent.TRACK_STATE_OFF)
+ .setTrackChangeReason(TrackChangeEvent.TRACK_CHANGE_REASON_INITIAL)
+ .setContainerMimeType("video/foo")
+ .setSampleMimeType("video/mpeg")
+ .setCodecName("codec_3")
+ .setBitrate(1025)
+ .setLanguage("EN")
+ .setLanguageRegion("US")
+ .setHeight(1080)
+ .setWidth(1440)
+ .setVideoFrameRate(60)
+ .setMetricsBundle(new Bundle())
+ .build();
+ s.reportTrackChangeEvent(e);
+ }
+
+ @Test
public void testNetworkEvent() throws Exception {
Context context = InstrumentationRegistry.getContext();
MediaMetricsManager manager = context.getSystemService(MediaMetricsManager.class);
@@ -127,6 +173,7 @@
.setNetworkTransferDurationMillis(6000)
.setDrmSessionId(new byte[] {2, 3, 3, 10})
.setMetricsBundle(new Bundle())
+ .addExperimentId(123)
.build();
s.reportPlaybackMetrics(e);
}
@@ -135,23 +182,25 @@
public void testSessionId() throws Exception {
Context context = InstrumentationRegistry.getContext();
MediaMetricsManager manager = context.getSystemService(MediaMetricsManager.class);
- PlaybackSession s = manager.createPlaybackSession();
- LogSessionId idObj = s.getSessionId();
- assertThat(idObj).isNotEqualTo(null);
- assertThat(idObj.getStringId().length()).isGreaterThan(0);
+ try(PlaybackSession s = manager.createPlaybackSession()) {
+ LogSessionId idObj = s.getSessionId();
+ assertThat(idObj).isNotEqualTo(null);
+ assertThat(idObj.getStringId().length()).isGreaterThan(0);
+ }
}
@Test
public void testRecordingSession() throws Exception {
Context context = InstrumentationRegistry.getContext();
MediaMetricsManager manager = context.getSystemService(MediaMetricsManager.class);
- RecordingSession s = manager.createRecordingSession();
- assertThat(s).isNotEqualTo(null);
- LogSessionId idObj = s.getSessionId();
- assertThat(idObj).isNotEqualTo(null);
- assertThat(idObj.getStringId().length()).isGreaterThan(0);
+ try(RecordingSession s = manager.createRecordingSession()) {
+ assertThat(s).isNotEqualTo(null);
+ LogSessionId idObj = s.getSessionId();
+ assertThat(idObj).isNotEqualTo(null);
+ assertThat(idObj.getStringId().length()).isGreaterThan(0);
+ }
}
/**
diff --git a/hostsidetests/media/src/android/media/metrics/cts/MediaMetricsAtomTests.java b/hostsidetests/media/src/android/media/metrics/cts/MediaMetricsAtomTests.java
index defde84..9e12ed6 100644
--- a/hostsidetests/media/src/android/media/metrics/cts/MediaMetricsAtomTests.java
+++ b/hostsidetests/media/src/android/media/metrics/cts/MediaMetricsAtomTests.java
@@ -141,6 +141,69 @@
assertThat(result.getLanguageRegion()).isEqualTo("US");
}
+ public void testTrackChangeEvent_audio() throws Exception {
+ ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
+ AtomsProto.Atom.MEDIA_PLAYBACK_TRACK_CHANGED_FIELD_NUMBER);
+ DeviceUtils.runDeviceTests(
+ getDevice(),
+ TEST_PKG,
+ "android.media.metrics.cts.MediaMetricsAtomHostSideTests",
+ "testTrackChangeEvent_audio");
+ Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
+
+ List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
+
+ assertThat(data.size()).isEqualTo(1);
+ assertThat(data.get(0).getAtom().hasMediaPlaybackTrackChanged()).isTrue();
+ AtomsProto.MediaPlaybackTrackChanged result =
+ data.get(0).getAtom().getMediaPlaybackTrackChanged();
+
+ assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(37278L);
+ assertThat(result.getState().toString()).isEqualTo("OFF");
+ assertThat(result.getReason().toString()).isEqualTo("REASON_INITIAL");
+ assertThat(result.getContainerMimeType()).isEqualTo("audio/foo");
+ assertThat(result.getSampleMimeType()).isEqualTo("audio/avc");
+ assertThat(result.getCodecName()).isEqualTo("codec_2");
+ assertThat(result.getBitrate()).isEqualTo(1025);
+ assertThat(result.getType().toString()).isEqualTo("AUDIO");
+ assertThat(result.getLanguage()).isEqualTo("EN");
+ assertThat(result.getLanguageRegion()).isEqualTo("US");
+ assertThat(result.getSampleRate()).isEqualTo(89);
+ assertThat(result.getChannelCount()).isEqualTo(3);
+ }
+
+ public void testTrackChangeEvent_video() throws Exception {
+ ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
+ AtomsProto.Atom.MEDIA_PLAYBACK_TRACK_CHANGED_FIELD_NUMBER);
+ DeviceUtils.runDeviceTests(
+ getDevice(),
+ TEST_PKG,
+ "android.media.metrics.cts.MediaMetricsAtomHostSideTests",
+ "testTrackChangeEvent_video");
+ Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
+
+ List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
+
+ assertThat(data.size()).isEqualTo(1);
+ assertThat(data.get(0).getAtom().hasMediaPlaybackTrackChanged()).isTrue();
+ AtomsProto.MediaPlaybackTrackChanged result =
+ data.get(0).getAtom().getMediaPlaybackTrackChanged();
+
+ assertThat(result.getTimeSincePlaybackCreatedMillis()).isEqualTo(37278L);
+ assertThat(result.getState().toString()).isEqualTo("OFF");
+ assertThat(result.getReason().toString()).isEqualTo("REASON_INITIAL");
+ assertThat(result.getContainerMimeType()).isEqualTo("video/foo");
+ assertThat(result.getSampleMimeType()).isEqualTo("video/mpeg");
+ assertThat(result.getCodecName()).isEqualTo("codec_3");
+ assertThat(result.getBitrate()).isEqualTo(1025);
+ assertThat(result.getType().toString()).isEqualTo("VIDEO");
+ assertThat(result.getLanguage()).isEqualTo("EN");
+ assertThat(result.getLanguageRegion()).isEqualTo("US");
+ assertThat(result.getHeight()).isEqualTo(1080);
+ assertThat(result.getWidth()).isEqualTo(1440);
+ assertThat(result.getVideoFrameRate()).isEqualTo(60);
+ }
+
public void testNetworkEvent() throws Exception {
ConfigUtils.uploadConfigForPushedAtom(getDevice(), TEST_PKG,
AtomsProto.Atom.MEDIA_NETWORK_INFO_CHANGED_FIELD_NUMBER);
@@ -195,6 +258,8 @@
assertThat(result.getNetworkBytesRead()).isEqualTo(102400);
assertThat(result.getLocalBytesRead()).isEqualTo(2000);
assertThat(result.getNetworkTransferDurationMillis()).isEqualTo(6000);
+ // TODO: fix missing experiment ID impl
+ assertThat(result.getExperimentIds()).isNotEqualTo(null);
// TODO: needs Base64 decoders to verify the data
assertThat(result.getDrmSessionId()).isNotEqualTo(null);
}
diff --git a/hostsidetests/packagemanager/domainverification/device/Android.bp b/hostsidetests/packagemanager/domainverification/device/multiuser/Android.bp
similarity index 91%
copy from hostsidetests/packagemanager/domainverification/device/Android.bp
copy to hostsidetests/packagemanager/domainverification/device/multiuser/Android.bp
index 081da66..f2054a7 100644
--- a/hostsidetests/packagemanager/domainverification/device/Android.bp
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/Android.bp
@@ -17,7 +17,7 @@
}
android_test {
- name: "CtsDomainVerificationDeviceTestCases",
+ name: "CtsDomainVerificationDeviceMultiUserTestCases",
srcs: [ "src/**/*.kt" ],
test_suites: [
"cts",
@@ -31,10 +31,12 @@
"compatibility-device-util-axt",
"CtsDomainVerificationAndroidConstantsLibrary",
"CtsDomainVerificationJavaConstantsLibrary",
+ "Harrier",
"junit",
+ "Nene",
"truth-prebuilt",
],
- data: [
+ java_resources: [
":CtsDomainVerificationTestDeclaringApp1",
":CtsDomainVerificationTestDeclaringApp2",
],
diff --git a/hostsidetests/packagemanager/domainverification/device/AndroidManifest.xml b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidManifest.xml
similarity index 96%
rename from hostsidetests/packagemanager/domainverification/device/AndroidManifest.xml
rename to hostsidetests/packagemanager/domainverification/device/multiuser/AndroidManifest.xml
index 9afe67d..2140573 100644
--- a/hostsidetests/packagemanager/domainverification/device/AndroidManifest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidManifest.xml
@@ -15,7 +15,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.packagemanager.verify.domain.device">
+ package="com.android.cts.packagemanager.verify.domain.device.multiuser">
<application android:label="Device Test App">
<uses-library android:name="android.test.runner" />
@@ -23,7 +23,7 @@
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.packagemanager.verify.domain.device" />
+ android:targetPackage="com.android.cts.packagemanager.verify.domain.device.multiuser" />
<queries>
<package android:name="com.android.cts.packagemanager.verify.domain.declaringapp1"/>
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
new file mode 100644
index 0000000..b88adb4
--- /dev/null
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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 domain verification multi user 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="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="multiuser" />
+ <!-- Instant apps can never be device admin / profile owner / device owner so positive tests
+ here are not applicable -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <!-- Device admin/owner requires being run in system user -->
+ <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsDomainVerificationDeviceMultiUserTestCases.apk" />
+ <option name="install-arg" value="-t" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package"
+ value="com.android.cts.packagemanager.verify.domain.device.multiuser" />
+ <option name="exclude-annotation"
+ value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" />
+ <option name="exclude-annotation"
+ value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" />
+ </test>
+</configuration>
+
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileAllowParentLinkingTests.kt b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileAllowParentLinkingTests.kt
new file mode 100644
index 0000000..8ebe89b
--- /dev/null
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileAllowParentLinkingTests.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packagemanager.verify.domain.device.multiuser
+
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile
+import com.android.bedstead.harrier.annotations.Postsubmit
+import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@EnsureHasWorkProfile(forUser = DeviceState.UserType.PRIMARY_USER)
+@RunWith(BedsteadJUnit4::class)
+class DomainVerificationWorkProfileAllowParentLinkingTests :
+ DomainVerificationWorkProfileTestsBase() {
+
+ private var initialAppLinkPolicy: Boolean? = null
+
+ @Before
+ fun saveAndSetPolicy() {
+ val manager = deviceState.getWorkDevicePolicyManager()
+ initialAppLinkPolicy = manager.getAppLinkPolicy()
+ if (initialAppLinkPolicy != true) {
+ manager.setAppLinkPolicy(true)
+ }
+ }
+
+ @After
+ fun resetPolicy() {
+ val manager = deviceState.getWorkDevicePolicyManager()
+ if (initialAppLinkPolicy ?: return != manager.getAppLinkPolicy()) {
+ manager.setAppLinkPolicy(initialAppLinkPolicy!!)
+ }
+ }
+
+ @RequireRunOnPrimaryUser
+ @Postsubmit(reason = "New test")
+ @Test
+ override fun inPersonal_verifiedInOtherProfile() {
+ verify(WORK_APP)
+
+ // General configuration does not allow parent -> managed, so only browsers returned
+ assertResolvesTo(personalBrowsers)
+ }
+}
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileCrossProfileIntentTests.kt b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileCrossProfileIntentTests.kt
new file mode 100644
index 0000000..4fb836e
--- /dev/null
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileCrossProfileIntentTests.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packagemanager.verify.domain.device.multiuser
+
+import android.app.admin.DevicePolicyManager
+import android.content.Intent
+import android.content.IntentFilter
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile
+import com.android.bedstead.harrier.annotations.Postsubmit
+import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DOMAIN_1
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@EnsureHasWorkProfile(forUser = DeviceState.UserType.PRIMARY_USER)
+@RunWith(BedsteadJUnit4::class)
+class DomainVerificationWorkProfileCrossProfileIntentTests :
+ DomainVerificationWorkProfileTestsBase() {
+
+ // The specific cross profile Intent test requires general app link policy to be disabled
+ private var initialAppLinkPolicy: Boolean? = null
+
+ @Before
+ fun saveAndSetPolicy() {
+ val manager = deviceState.getWorkDevicePolicyManager()
+ initialAppLinkPolicy = manager.getAppLinkPolicy()
+ if (initialAppLinkPolicy != false) {
+ manager.setAppLinkPolicy(false)
+ }
+
+ val intentFilter = IntentFilter().apply {
+ addAction(Intent.ACTION_VIEW)
+ addCategory(Intent.CATEGORY_BROWSABLE)
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addDataScheme("https")
+ addDataAuthority(DOMAIN_1, null)
+ }
+ manager.addCrossProfileIntentFilter(
+ intentFilter,
+ DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED
+ or DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT
+ )
+ }
+
+ @After
+ fun resetPolicy() {
+ val manager = deviceState.getWorkDevicePolicyManager()
+ if (initialAppLinkPolicy ?: return != manager.getAppLinkPolicy()) {
+ manager.setAppLinkPolicy(initialAppLinkPolicy!!)
+ }
+ manager.clearCrossProfileIntentFilters()
+ }
+
+ @RequireRunOnPrimaryUser
+ @Postsubmit(reason = "New test")
+ @Test
+ override fun inPersonal_verifiedInOtherProfile() {
+ verify(WORK_APP)
+
+ // Specific configuration does allow parent -> managed
+ assertResolvesTo(personalBrowsers + FORWARD_TO_MANAGED)
+ }
+}
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileTestsBase.kt b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileTestsBase.kt
new file mode 100644
index 0000000..f317de8
--- /dev/null
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkProfileTestsBase.kt
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packagemanager.verify.domain.device.multiuser
+
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import com.android.bedstead.harrier.BedsteadJUnit4
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile
+import com.android.bedstead.harrier.annotations.Postsubmit
+import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser
+import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile
+import com.android.bedstead.nene.TestApis
+import com.android.bedstead.nene.packages.Packages
+import com.android.bedstead.nene.users.UserReference
+import com.android.bedstead.nene.utils.ShellCommand
+import com.android.compatibility.common.util.ShellUtils
+import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_1_COMPONENT
+import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_2_COMPONENT
+import com.android.cts.packagemanager.verify.domain.android.SharedVerifications
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DECLARING_PKG_APK_1
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DECLARING_PKG_APK_2
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DECLARING_PKG_NAME_1
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DECLARING_PKG_NAME_2
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DOMAIN_1
+import com.android.cts.packagemanager.verify.domain.java.DomainUtils.DOMAIN_UNHANDLED
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import org.junit.After
+import org.junit.AfterClass
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@EnsureHasWorkProfile(forUser = DeviceState.UserType.PRIMARY_USER)
+@RunWith(BedsteadJUnit4::class)
+abstract class DomainVerificationWorkProfileTestsBase {
+
+ companion object {
+
+ @JvmField
+ @ClassRule
+ @Rule
+ val deviceState = DeviceState()
+
+ private val testApis = TestApis()
+ private val TARGET_INTENT = Intent(Intent.ACTION_VIEW, Uri.parse("https://$DOMAIN_1"))
+ private val BROWSER_INTENT =
+ Intent(Intent.ACTION_VIEW, Uri.parse("https://$DOMAIN_UNHANDLED"))
+
+ @JvmStatic
+ protected val FORWARD_TO_PARENT =
+ ComponentName("android", "com.android.internal.app.ForwardIntentToParent")
+
+ @JvmStatic
+ protected val FORWARD_TO_MANAGED =
+ ComponentName("android", "com.android.internal.app.ForwardIntentToManagedProfile")
+
+ @JvmStatic
+ protected val PERSONAL_APP = DECLARING_PKG_NAME_1
+
+ @JvmStatic
+ protected val WORK_APP = DECLARING_PKG_NAME_2
+
+ @JvmStatic
+ protected val PERSONAL_COMPONENT = DECLARING_PKG_1_COMPONENT
+
+ @JvmStatic
+ protected val WORK_COMPONENT = DECLARING_PKG_2_COMPONENT
+
+ @JvmStatic
+ protected lateinit var personalBrowsers: Collection<ComponentName>
+
+ @JvmStatic
+ protected lateinit var workBrowsers: Collection<ComponentName>
+
+ private lateinit var personalUser: UserReference
+ private lateinit var workUser: UserReference
+
+ @JvmStatic
+ @BeforeClass
+ fun installApks() {
+ personalUser = deviceState.primaryUser()
+ workUser = deviceState.workProfile(DeviceState.UserType.PRIMARY_USER)
+ personalBrowsers = collectBrowsers(personalUser)
+ workBrowsers = collectBrowsers(workUser)
+ testApis.packages().run {
+ install(personalUser, Packages.JavaResource.javaResource(DECLARING_PKG_APK_1.value))
+ install(workUser, Packages.JavaResource.javaResource(DECLARING_PKG_APK_2.value))
+ }
+ }
+
+ @JvmStatic
+ @AfterClass
+ fun uninstallApks() {
+ testApis.packages().run {
+ find(PERSONAL_APP).uninstallFromAllUsers()
+ find(WORK_APP).uninstallFromAllUsers()
+ }
+ }
+
+ private fun collectBrowsers(user: UserReference) =
+ testApis.withUserContext(user) { context ->
+ context.packageManager
+ .queryIntentActivities(BROWSER_INTENT, PackageManager.MATCH_DEFAULT_ONLY)
+ .map { it.activityInfo }
+ .map { ComponentName(it.packageName, it.name) }
+ .also { assumeTrue(it.isNotEmpty()) }
+ }
+
+ @JvmStatic
+ protected fun assertResolvesTo(vararg components: ComponentName) =
+ assertResolvesTo(components.toList())
+
+ @JvmStatic
+ protected fun assertResolvesTo(components: Collection<ComponentName>) {
+ val results = testApis.context()
+ .instrumentedContext()
+ .packageManager
+ .queryIntentActivities(TARGET_INTENT, PackageManager.MATCH_DEFAULT_ONLY)
+ .map { it.activityInfo }
+ .map { ComponentName(it.packageName, it.name) }
+ assertThat(results).containsExactlyElementsIn(components)
+ }
+
+ @JvmStatic
+ protected fun verify(vararg packageNames: String) = packageNames.forEach {
+ assertWithMessage("pm set-app-links should be empty on success").that(
+ ShellUtils.runShellCommand(DomainUtils.setAppLinks(it, "STATE_APPROVED", DOMAIN_1))
+ ).isEmpty()
+ }
+ }
+
+ @Before
+ @After
+ fun resetState() {
+ listOf(personalUser, workUser).forEach {
+ testApis.withUserContext(it) {
+ SharedVerifications.reset(it, resetEnable = true)
+ }
+ }
+ }
+
+ @RequireRunOnPrimaryUser
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inPersonal_unverified() {
+ assertResolvesTo(personalBrowsers)
+ }
+
+ @RequireRunOnPrimaryUser
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inPersonal_verifiedInCurrentProfile() {
+ verify(PERSONAL_APP)
+
+ assertResolvesTo(PERSONAL_COMPONENT)
+ }
+
+ // The assertion for this method varies based on general versus specific cross profile config
+ abstract fun inPersonal_verifiedInOtherProfile()
+
+ @RequireRunOnPrimaryUser
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inPersonal_verifiedInBothProfiles() {
+ verify(PERSONAL_APP, WORK_APP)
+
+ assertResolvesTo(PERSONAL_COMPONENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_unverified() {
+ assertResolvesTo(workBrowsers)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInCurrentProfile() {
+ verify(WORK_APP)
+
+ assertResolvesTo(WORK_COMPONENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInOtherProfile() {
+ verify(PERSONAL_APP)
+
+ assertResolvesTo(workBrowsers + FORWARD_TO_PARENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInOtherProfileDisabledApp() {
+ verify(PERSONAL_APP)
+ disableApp(personalUser, PERSONAL_APP)
+
+ assertResolvesTo(workBrowsers)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInOtherProfileDisabledComponent() {
+ verify(PERSONAL_APP)
+ disableComponent(personalUser, PERSONAL_COMPONENT)
+
+ assertResolvesTo(workBrowsers)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInBothProfiles() {
+ verify(PERSONAL_APP, WORK_APP)
+
+ assertResolvesTo(WORK_COMPONENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInBothProfilesDisabledAppInOther() {
+ verify(PERSONAL_APP, WORK_APP)
+ disableApp(personalUser, PERSONAL_APP)
+
+ assertResolvesTo(WORK_COMPONENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInBothProfilesDisabledComponentInOther() {
+ verify(PERSONAL_APP, WORK_APP)
+ disableComponent(personalUser, PERSONAL_COMPONENT)
+
+ assertResolvesTo(WORK_COMPONENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInBothProfilesDisabledAppInCurrent() {
+ verify(PERSONAL_APP, WORK_APP)
+ disableApp(workUser, WORK_APP)
+
+ assertResolvesTo(workBrowsers + FORWARD_TO_PARENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInBothProfilesDisabledComponentInCurrent() {
+ verify(PERSONAL_APP, WORK_APP)
+ disableComponent(workUser, WORK_COMPONENT)
+
+ assertResolvesTo(workBrowsers + FORWARD_TO_PARENT)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInBothProfilesDisabledAppInBoth() {
+ verify(PERSONAL_APP, WORK_APP)
+ disableApp(personalUser, PERSONAL_APP)
+ disableApp(workUser, WORK_APP)
+
+ assertResolvesTo(workBrowsers)
+ }
+
+ @RequireRunOnWorkProfile
+ @Postsubmit(reason = "New test")
+ @Test
+ fun inWork_verifiedInBothProfilesDisabledComponentInBoth() {
+ verify(PERSONAL_APP, WORK_APP)
+ disableComponent(personalUser, PERSONAL_COMPONENT)
+ disableComponent(workUser, WORK_COMPONENT)
+
+ assertResolvesTo(workBrowsers)
+ }
+
+ private fun disableApp(user: UserReference, packageName: String) {
+ ShellCommand.builderForUser(user, "pm disable-user")
+ .addOperand(packageName)
+ .validate { it.trim().endsWith("new state: disabled-user") }
+ .execute()
+ }
+
+ private fun disableComponent(user: UserReference, component: ComponentName) {
+ ShellCommand.builderForUser(user, "pm disable")
+ .addOperand(component.flattenToString())
+ .validate { it.trim().endsWith("new state: disabled") }
+ .execute()
+ }
+}
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkUtils.kt b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkUtils.kt
new file mode 100644
index 0000000..39da4cc
--- /dev/null
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/src/com/android/cts/packagemanager/verify/domain/device/multiuser/DomainVerificationWorkUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packagemanager.verify.domain.device.multiuser
+
+import android.content.Context
+import android.os.UserManager
+import com.android.bedstead.harrier.DeviceState
+import com.android.bedstead.nene.TestApis
+import com.android.bedstead.nene.users.UserReference
+import com.android.bedstead.remotedpc.managers.RemoteDevicePolicyManager
+
+internal fun RemoteDevicePolicyManager.getAppLinkPolicy() =
+ getUserRestrictions()?.getBoolean(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING, false) ?: false
+
+internal fun RemoteDevicePolicyManager.setAppLinkPolicy(allow: Boolean) {
+ if (allow) {
+ addUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING)
+ } else {
+ clearUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING)
+ }
+}
+
+internal fun DeviceState.getWorkDevicePolicyManager() =
+ profileOwner(workProfile(DeviceState.UserType.PRIMARY_USER))!!.devicePolicyManager()
+
+internal fun <T> TestApis.withUserContext(user: UserReference, block: (context: Context) -> T) =
+ permissions()
+ .withPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
+ .use { block(context().androidContextAsUser(user)) }
diff --git a/hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/CallingActivity.kt b/hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/CallingActivity.kt
deleted file mode 100644
index 1b1af08..0000000
--- a/hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/CallingActivity.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.packagemanager.verify.domain.callingapp
-
-import android.app.Activity
-
-class CallingActivity : Activity()
diff --git a/hostsidetests/packagemanager/domainverification/device/Android.bp b/hostsidetests/packagemanager/domainverification/device/standalone/Android.bp
similarity index 95%
rename from hostsidetests/packagemanager/domainverification/device/Android.bp
rename to hostsidetests/packagemanager/domainverification/device/standalone/Android.bp
index 081da66..8990d84 100644
--- a/hostsidetests/packagemanager/domainverification/device/Android.bp
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/Android.bp
@@ -17,7 +17,7 @@
}
android_test {
- name: "CtsDomainVerificationDeviceTestCases",
+ name: "CtsDomainVerificationDeviceStandaloneTestCases",
srcs: [ "src/**/*.kt" ],
test_suites: [
"cts",
diff --git a/hostsidetests/packagemanager/domainverification/device/AndroidManifest.xml b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidManifest.xml
similarity index 92%
copy from hostsidetests/packagemanager/domainverification/device/AndroidManifest.xml
copy to hostsidetests/packagemanager/domainverification/device/standalone/AndroidManifest.xml
index 9afe67d..fba5376 100644
--- a/hostsidetests/packagemanager/domainverification/device/AndroidManifest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidManifest.xml
@@ -15,15 +15,16 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.packagemanager.verify.domain.device">
+ package="com.android.cts.packagemanager.verify.domain.device.standalone"
+ >
- <application android:label="Device Test App">
+ <application android:label="Device Test App" android:testOnly="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.packagemanager.verify.domain.device" />
+ android:targetPackage="com.android.cts.packagemanager.verify.domain.device.standalone" />
<queries>
<package android:name="com.android.cts.packagemanager.verify.domain.declaringapp1"/>
diff --git a/hostsidetests/packagemanager/domainverification/device/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
similarity index 81%
rename from hostsidetests/packagemanager/domainverification/device/AndroidTest.xml
rename to hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
index 59b295a..d346a62 100644
--- a/hostsidetests/packagemanager/domainverification/device/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
~ Copyright (C) 2021 The Android Open Source Project
~
~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<configuration description="Config for CTS package manager metrics device test cases">
+<configuration description="Config for CTS domain verification device standalone 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="multi_abi" />
@@ -23,14 +22,15 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsDomainVerificationDeviceTestCases.apk" />
+ <option name="test-file-name" value="CtsDomainVerificationDeviceStandaloneTestCases.apk" />
<option name="test-file-name" value="CtsDomainVerificationTestDeclaringApp1.apk" />
<option name="test-file-name" value="CtsDomainVerificationTestDeclaringApp2.apk" />
- <option name="install-arg" value="-t"/>
+ <option name="install-arg" value="-t" />
</target_preparer>
- <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
- <option name="package" value="com.android.cts.packagemanager.verify.domain.device" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package"
+ value="com.android.cts.packagemanager.verify.domain.device.standalone" />
</test>
</configuration>
diff --git a/hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/DomainVerificationIntentInvalidHostTests.kt b/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentInvalidHostTests.kt
similarity index 94%
rename from hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/DomainVerificationIntentInvalidHostTests.kt
rename to hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentInvalidHostTests.kt
index f57d599..ba7d2dc 100644
--- a/hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/DomainVerificationIntentInvalidHostTests.kt
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentInvalidHostTests.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.packagemanager.verify.domain.device
+package com.android.cts.packagemanager.verify.domain.device.standalone
import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_1_COMPONENT
import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_2_COMPONENT
diff --git a/hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/DomainVerificationIntentStandaloneTests.kt b/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentStandaloneTests.kt
similarity index 98%
rename from hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/DomainVerificationIntentStandaloneTests.kt
rename to hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentStandaloneTests.kt
index dd456fa..fab5eb3 100644
--- a/hostsidetests/packagemanager/domainverification/device/src/com/android/cts/packagemanager/verify/domain/device/DomainVerificationIntentStandaloneTests.kt
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/src/com/android/cts/packagemanager/verify/domain/device/standalone/DomainVerificationIntentStandaloneTests.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.cts.packagemanager.verify.domain.device
+package com.android.cts.packagemanager.verify.domain.device.standalone
import android.content.pm.verify.domain.DomainVerificationUserState
import com.android.cts.packagemanager.verify.domain.android.DomainUtils.DECLARING_PKG_1_COMPONENT
diff --git a/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTestBase.java b/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTestBase.java
index a0ee10c..00afa9a 100644
--- a/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTestBase.java
+++ b/hostsidetests/packagemanager/extractnativelibs/src/android/extractnativelibs/cts/CtsExtractNativeLibsHostTestBase.java
@@ -35,7 +35,6 @@
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -202,12 +201,21 @@
final Set<String> getDeviceAbis() throws Exception {
String[] abiArray = getDevice().getProperty("ro.product.cpu.abilist").split(",");
- return new HashSet<String>(Arrays.asList(abiArray));
+ // Ignore native bridge ABIs if they are of different base arch
+ String deviceBaseArch = AbiUtils.getArchForAbi(getDeviceAbi());
+ Set<String> deviceBaseArchSupportedAbis = AbiUtils.getAbisForArch(deviceBaseArch);
+ HashSet<String> deviceSupportedAbis = new HashSet<>();
+ for (String abi : abiArray) {
+ if (deviceBaseArchSupportedAbis.contains(abi)) {
+ deviceSupportedAbis.add(abi);
+ }
+ }
+ return deviceSupportedAbis;
}
final Set<String> getDeviceAbiSuffixes() throws Exception {
HashSet<String> abiSuffixes = new HashSet<String>();
- for (String abi : getDevice().getProperty("ro.product.cpu.abilist").split(",")) {
+ for (String abi : getDeviceAbis()) {
abiSuffixes.add(AbiUtils.getBitness(abi));
}
return abiSuffixes;
diff --git a/hostsidetests/packagemanager/parsing/host/src/android/content/pm/parsing/cts/host/UsesSdkTest.kt b/hostsidetests/packagemanager/parsing/host/src/android/content/pm/parsing/cts/host/UsesSdkTest.kt
index aa5e813..7fce59c 100644
--- a/hostsidetests/packagemanager/parsing/host/src/android/content/pm/parsing/cts/host/UsesSdkTest.kt
+++ b/hostsidetests/packagemanager/parsing/host/src/android/content/pm/parsing/cts/host/UsesSdkTest.kt
@@ -63,7 +63,7 @@
"""
val result = ApkGenerator.install(device, xml, tempFolder)
assertThat(result.error).isEmpty()
- assertSdks(result, min = 0, target = 0)
+ assertSdks(result, min = 1, target = 0)
}
@Test
diff --git a/hostsidetests/packagemanager/stats/Android.bp b/hostsidetests/packagemanager/stats/Android.bp
index d05ed3b..b7e1912 100644
--- a/hostsidetests/packagemanager/stats/Android.bp
+++ b/hostsidetests/packagemanager/stats/Android.bp
@@ -38,6 +38,8 @@
":CtsStatsdAtomEmptyApp",
":CtsStatsdAtomEmptyApp2",
":CtsStatsdAtomEmptySplitApp",
+ ":CtsStatsdAtomApp",
+ ":IncrementalAppErrorStatsTestsHelper",
],
java_resource_dirs: ["res"],
}
diff --git a/hostsidetests/packagemanager/stats/OWNERS b/hostsidetests/packagemanager/stats/OWNERS
index ee4101d..fd0ce2d 100644
--- a/hostsidetests/packagemanager/stats/OWNERS
+++ b/hostsidetests/packagemanager/stats/OWNERS
@@ -2,4 +2,6 @@
toddke@google.com
patb@google.com
schfan@google.com
-chiuwinson@google.com
\ No newline at end of file
+chiuwinson@google.com
+alexbuy@google.com
+zyy@google.com
\ No newline at end of file
diff --git a/hostsidetests/packagemanager/stats/device/Android.bp b/hostsidetests/packagemanager/stats/device/Android.bp
new file mode 100644
index 0000000..7d00c06
--- /dev/null
+++ b/hostsidetests/packagemanager/stats/device/Android.bp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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: "IncrementalAppErrorStatsTestsHelper",
+ defaults: ["cts_defaults"],
+ platform_apis: true,
+ min_sdk_version: "28",
+ srcs: [
+ "src/**/*.java",
+ ],
+ libs: [
+ "android.test.runner",
+ "junit",
+ ],
+ static_libs: [
+ "ctstestrunner-axt",
+ "compatibility-device-util-axt",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.test.rules",
+ "cts-net-utils",
+ "apache-commons-compress",
+ ],
+ compile_multilib: "both",
+}
diff --git a/common/device-side/bedstead/testapp/src/communication/main/AndroidManifest.xml b/hostsidetests/packagemanager/stats/device/AndroidManifest.xml
similarity index 72%
rename from common/device-side/bedstead/testapp/src/communication/main/AndroidManifest.xml
rename to hostsidetests/packagemanager/stats/device/AndroidManifest.xml
index 98c01d8..384496a 100644
--- a/common/device-side/bedstead/testapp/src/communication/main/AndroidManifest.xml
+++ b/hostsidetests/packagemanager/stats/device/AndroidManifest.xml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!--
~ Copyright (C) 2021 The Android Open Source Project
~
@@ -17,9 +16,12 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.DeviceAdminTestApp">
+ package="com.android.cts.packagemanager.stats.device">
<application>
- <service android:name="com.google.android.enterprise.connectedapps.CrossProfileConnector_Service" android:exported="true" />
+ <uses-library android:name="android.test.runner" />
</application>
- <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28"/>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.packagemanager.stats.device" />
</manifest>
+
diff --git a/hostsidetests/packagemanager/stats/device/src/com/android/cts/packagemanager/stats/device/IncrementalAppErrorStatsTestsHelper.java b/hostsidetests/packagemanager/stats/device/src/com/android/cts/packagemanager/stats/device/IncrementalAppErrorStatsTestsHelper.java
new file mode 100644
index 0000000..fdcefd7
--- /dev/null
+++ b/hostsidetests/packagemanager/stats/device/src/com/android/cts/packagemanager/stats/device/IncrementalAppErrorStatsTestsHelper.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packagemanager.stats.device;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
+import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+
+public class IncrementalAppErrorStatsTestsHelper {
+ private static final String HELPER_ARG = "remoteApkPath";
+ private static final String PAGE_INDEX_TO_BLOCK = "pageIndexToBlock";
+ // The apk contains a video resource file which has 9 pages. We only need to block 1 page
+ // such that the loading progress is never completed.
+ private static final String FILE_PAGE_TO_BLOCK = "res/raw/colors_video.mp4";
+ private static final int BLOCK_SIZE = 4096;
+ // Instrumentation status code used to write resolution to metrics
+ private static final int INST_STATUS_IN_PROGRESS = 2;
+
+ @Test
+ public void getPageIndexToBlock() throws IOException {
+ final Bundle testArgs = InstrumentationRegistry.getArguments();
+ final String apkPath = (String) testArgs.get(HELPER_ARG);
+ assertNotNull(apkPath);
+ assertTrue(new File(apkPath).exists());
+ ZipFile zip = new ZipFile(apkPath);
+ final ZipArchiveEntry info = zip.getEntry(FILE_PAGE_TO_BLOCK);
+ assertTrue(info.getSize() > BLOCK_SIZE);
+ assertTrue(info.getDataOffset() > BLOCK_SIZE * 2);
+ final int pageToBlock = (int) info.getDataOffset() / 4096 + 1;
+ // Pass data to the host-side test
+ Instrumentation inst = InstrumentationRegistry.getInstrumentation();
+ Bundle bundle = new Bundle();
+ bundle.putString(PAGE_INDEX_TO_BLOCK, String.valueOf(pageToBlock));
+ inst.sendStatus(INST_STATUS_IN_PROGRESS, bundle);
+ }
+}
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/incremental/AppErrorAtomTests.java b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/IncrementalAppErrorStatsTests.java
similarity index 70%
rename from hostsidetests/statsdatom/src/android/cts/statsdatom/incremental/AppErrorAtomTests.java
rename to hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/IncrementalAppErrorStatsTests.java
index 5474633..12441f5 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/incremental/AppErrorAtomTests.java
+++ b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/IncrementalAppErrorStatsTests.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package android.cts.statsdatom.incremental;
+package com.android.cts.packagemanager.stats.host;
+
+import static com.android.cts.packagemanager.stats.host.Utils.FEATURE_INCREMENTAL_DELIVERY;
+import static com.android.cts.packagemanager.stats.host.Utils.SIGNATURE_FILE_SUFFIX;
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assume.assumeTrue;
-
-import android.cts.statsdatom.lib.AtomTestUtils;
import android.cts.statsdatom.lib.ConfigUtils;
import android.cts.statsdatom.lib.DeviceUtils;
import android.cts.statsdatom.lib.ReportUtils;
@@ -32,6 +32,7 @@
import com.android.os.AtomsProto;
import com.android.os.StatsLog;
import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.log.LogUtil;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IBuildReceiver;
@@ -40,23 +41,30 @@
import java.io.File;
import java.nio.file.Paths;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-public class AppErrorAtomTests extends DeviceTestCase implements IBuildReceiver {
- private static final String FEATURE_INCREMENTAL_DELIVERY =
- "android.software.incremental_delivery";
- private static final String IDSIG_SUFFIX = ".idsig";
- private static int INSTALL_TIMEOUT_SECONDS = 10;
+public final class IncrementalAppErrorStatsTests extends DeviceTestCase implements IBuildReceiver {
+ private static final String TEST_REMOTE_DIR = "/data/local/tmp/appErrorTest";
+ private static final String HELPER_PACKAGE = "com.android.cts.packagemanager.stats.device";
+ private static final String HELPER_APK = "IncrementalAppErrorStatsTestsHelper.apk";
+ private static final String HELPER_CLASS = ".IncrementalAppErrorStatsTestsHelper";
+ private static final String HELPER_METHOD = "getPageIndexToBlock";
+ private static final String HELPER_ARG = "remoteApkPath";
+ private static final String PAGE_INDEX_TO_BLOCK = "pageIndexToBlock";
+ private static final int INSTALL_TIMEOUT_SECONDS = 10;
+ private static final int METRICS_WAIT_MILLISECONDS = 1_000;
private IBuildInfo mCtsBuild;
+ private IncrementalInstallSession mSession;
@Override
public void setBuild(IBuildInfo buildInfo) {
mCtsBuild = buildInfo;
}
- private IncrementalInstallSession mSession;
@Before
public void setUp() throws Exception {
@@ -66,18 +74,35 @@
super.setUp();
ConfigUtils.removeConfig(getDevice());
ReportUtils.clearReports(getDevice());
+
+ DeviceUtils.installTestApp(getDevice(), HELPER_APK, HELPER_PACKAGE, mCtsBuild);
+ assertTrue(getDevice().isPackageInstalled(HELPER_PACKAGE));
+
+ String remoteApkPath = Utils.pushApkToRemote(
+ DeviceUtils.STATSD_ATOM_TEST_APK, TEST_REMOTE_DIR, mCtsBuild, getDevice());
+ final HashMap<String, String> testArgs = new HashMap<>();
+ testArgs.put(HELPER_ARG, remoteApkPath);
+ Map<String, String> testResult = Utils.runDeviceTests(getDevice(), HELPER_PACKAGE,
+ HELPER_CLASS, HELPER_METHOD, testArgs);
+ assertNotNull(testResult);
+ assertEquals(1, testResult.size());
+
+ int blockedPageIndex = Integer.parseInt(testResult.get(PAGE_INDEX_TO_BLOCK));
+ assertTrue(blockedPageIndex > 0);
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
final File apk = buildHelper.getTestFile(DeviceUtils.STATSD_ATOM_TEST_APK);
assertNotNull(apk);
- final File v4Signature = buildHelper.getTestFile(DeviceUtils.STATSD_ATOM_TEST_APK + IDSIG_SUFFIX);
+ final File v4Signature = buildHelper.getTestFile(
+ DeviceUtils.STATSD_ATOM_TEST_APK + SIGNATURE_FILE_SUFFIX);
assertNotNull(v4Signature);
+ LogUtil.CLog.i("Blocking page at index: " + blockedPageIndex);
mSession = new IncrementalInstallSession.Builder()
.addApk(Paths.get(apk.getAbsolutePath()),
Paths.get(v4Signature.getAbsolutePath()))
.addExtraArgs("-g") // grant permissions
.setBlockFilter(block -> {
// block a page from res/raw; does not affect test run
- return block.getBlockIndex() != 3152;
+ return block.getBlockIndex() != blockedPageIndex;
})
.build();
mSession.start(Executors.newCachedThreadPool(),
@@ -95,6 +120,8 @@
}
getDevice().uninstallPackage(DeviceUtils.STATSD_ATOM_TEST_PKG);
assertFalse(getDevice().isPackageInstalled(DeviceUtils.STATSD_ATOM_TEST_PKG));
+ getDevice().uninstallPackage(HELPER_PACKAGE);
+ assertFalse(getDevice().isPackageInstalled(HELPER_PACKAGE));
super.tearDown();
}
@@ -108,7 +135,7 @@
DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
"StatsdCtsForegroundActivity", "action", "action.crash");
-
+ Thread.sleep(METRICS_WAIT_MILLISECONDS);
// Sorted list of events in order in which they occurred.
List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice());
diff --git a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/InstalledIncrementalPackageStatsTests.java b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/InstalledIncrementalPackageStatsTests.java
index aec859d..0fb546f 100644
--- a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/InstalledIncrementalPackageStatsTests.java
+++ b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/InstalledIncrementalPackageStatsTests.java
@@ -16,6 +16,8 @@
package com.android.cts.packagemanager.stats.host;
+import static com.android.cts.packagemanager.stats.host.Utils.FEATURE_INCREMENTAL_DELIVERY;
+
import android.cts.statsdatom.lib.AtomTestUtils;
import android.cts.statsdatom.lib.ConfigUtils;
import android.cts.statsdatom.lib.DeviceUtils;
diff --git a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java
index ec177f6..0bbe4f1 100644
--- a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java
+++ b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageInstallerV2StatsTests.java
@@ -16,6 +16,8 @@
package com.android.cts.packagemanager.stats.host;
+import static com.android.cts.packagemanager.stats.host.Utils.FEATURE_INCREMENTAL_DELIVERY;
+
import android.cts.statsdatom.lib.AtomTestUtils;
import android.cts.statsdatom.lib.ConfigUtils;
import android.cts.statsdatom.lib.DeviceUtils;
diff --git a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageManagerStatsTestsBase.java b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageManagerStatsTestsBase.java
index 7a27f74..30b0262 100644
--- a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageManagerStatsTestsBase.java
+++ b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/PackageManagerStatsTestsBase.java
@@ -34,10 +34,8 @@
import java.util.regex.Pattern;
public class PackageManagerStatsTestsBase extends DeviceTestCase implements IBuildReceiver {
- protected static final String FEATURE_INCREMENTAL_DELIVERY =
- "android.software.incremental_delivery";
+
protected static final String TEST_REMOTE_DIR = "/data/local/tmp/statsdatom";
- private static final String SIGNATURE_FILE_SUFFIX = ".idsig";
protected IBuildInfo mCtsBuild;
@Override
@@ -68,7 +66,8 @@
getDevice().executeShellCommand("mkdir -p " + TEST_REMOTE_DIR);
String[] remoteApkPaths = new String[apkNames.length];
for (int i = 0; i < remoteApkPaths.length; i++) {
- remoteApkPaths[i] = pushApkToRemote(apkNames[i], TEST_REMOTE_DIR);
+ remoteApkPaths[i] = Utils.pushApkToRemote(
+ apkNames[i], TEST_REMOTE_DIR, mCtsBuild, getDevice());
}
String installResult = getDevice().executeShellCommand(
"pm install-incremental -t -g " + "--user " + getDevice().getCurrentUser() + " "
@@ -76,20 +75,6 @@
assertEquals("Success\n", installResult);
}
- protected String pushApkToRemote(String apkName, String remoteDirPath)
- throws Exception {
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- final File apk = buildHelper.getTestFile(apkName);
- final File signature = buildHelper.getTestFile(apkName + SIGNATURE_FILE_SUFFIX);
- assertNotNull(apk);
- assertNotNull(signature);
- final String remoteApkPath = remoteDirPath + "/" + apk.getName();
- final String remoteSignaturePath = remoteApkPath + SIGNATURE_FILE_SUFFIX;
- assertTrue(getDevice().pushFile(apk, remoteApkPath));
- assertTrue(getDevice().pushFile(signature, remoteSignaturePath));
- return remoteApkPath;
- }
-
protected int getAppUid(String pkgName) throws Exception {
final int currentUser = getDevice().getCurrentUser();
final String uidLine = getDevice().executeShellCommand(
diff --git a/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/Utils.java b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/Utils.java
new file mode 100644
index 0000000..e12beda
--- /dev/null
+++ b/hostsidetests/packagemanager/stats/src/com/android/cts/packagemanager/stats/host/Utils.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packagemanager.stats.host;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.metrics.proto.MetricMeasurement.Metric;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.result.TestResult;
+import com.android.tradefed.result.TestRunResult;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class Utils {
+ public static final String SIGNATURE_FILE_SUFFIX = ".idsig";
+ public static final String FEATURE_INCREMENTAL_DELIVERY =
+ "android.software.incremental_delivery";
+
+ public static String pushApkToRemote(String apkName, String remoteDirPath, IBuildInfo ctsBuild,
+ ITestDevice device) throws Exception {
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(ctsBuild);
+ final File apk = buildHelper.getTestFile(apkName);
+ final File signature = buildHelper.getTestFile(apkName + SIGNATURE_FILE_SUFFIX);
+ assertNotNull(apk);
+ assertNotNull(signature);
+ final String remoteApkPath = remoteDirPath + "/" + apk.getName();
+ final String remoteSignaturePath = remoteApkPath + SIGNATURE_FILE_SUFFIX;
+ assertTrue(device.pushFile(apk, remoteApkPath));
+ assertTrue(device.pushFile(signature, remoteSignaturePath));
+ return remoteApkPath;
+ }
+
+ public static Map<String, String> runDeviceTests(ITestDevice device, String packageName,
+ String testClassName, String testMethodName, Map<String, String> testArgs)
+ throws DeviceNotAvailableException {
+ if (testClassName != null && testClassName.startsWith(".")) {
+ testClassName = packageName + testClassName;
+ }
+ RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+ "androidx.test.runner.AndroidJUnitRunner", device.getIDevice());
+
+ if (testClassName != null && testMethodName != null) {
+ testRunner.setMethodName(testClassName, testMethodName);
+ } else if (testClassName != null) {
+ testRunner.setClassName(testClassName);
+ }
+
+ if (testArgs != null && testArgs.size() > 0) {
+ for (String name : testArgs.keySet()) {
+ final String value = testArgs.get(name);
+ testRunner.addInstrumentationArg(name, value);
+ }
+ }
+ final TestResultListener listener = new TestResultListener();
+ assertTrue(device.runInstrumentationTests(testRunner, listener));
+
+ final TestRunResult result = listener.getCurrentRunResults();
+ if (result.isRunFailure()) {
+ throw new Error("Failed to successfully run device tests for "
+ + result.getName() + ": " + result.getRunFailureMessage());
+ }
+ if (result.getNumTests() == 0) {
+ throw new Error("No tests were run on the device");
+ }
+ if (result.hasFailedTests()) {
+ StringBuilder errorBuilder = new StringBuilder("On-device tests failed:\n");
+ for (Map.Entry<TestDescription, TestResult> resultEntry :
+ result.getTestResults().entrySet()) {
+ if (!resultEntry.getValue().getStatus().equals(
+ com.android.ddmlib.testrunner.TestResult.TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ }
+
+ if (listener.mFailureStackTrace != null) {
+ throw new AssertionError("Failed to successfully run device tests: "
+ + listener.mFailureStackTrace);
+ }
+ return listener.mMetrics;
+ }
+
+ /* Special listener for collecting data from the test result */
+ private static class TestResultListener extends CollectingTestListener {
+ private final Map<String, String> mMetrics = new HashMap<>();
+ private String mFailureStackTrace = null;
+
+ @Override
+ public void testEnded(TestDescription test, Map<String, String> metrics) {
+ mMetrics.putAll(metrics);
+ }
+
+ @Override
+ public void testEnded(TestDescription test, HashMap<String, Metric> metrics) {
+ for (Map.Entry<String, Metric> e: metrics.entrySet()) {
+ mMetrics.put(e.getKey(), e.getValue().getMeasurements().getSingleString());
+ }
+ }
+
+ @Override
+ public void testFailed(TestDescription test, String trace) {
+ mFailureStackTrace = trace;
+ }
+ }
+}
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
index fbbb849..6f5db20 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -2078,7 +2078,7 @@
}
@Test
- public void testDeletePendingAndTrashed() throws Exception {
+ public void testDeletePendingAndTrashed_ownerCanDelete() throws Exception {
final File pendingVideoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
final File trashedImageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
final File pendingPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
@@ -2097,7 +2097,25 @@
// App can delete its own pending and trashed file.
assertCanDeletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
trashedPdfFilePath);
+ } finally {
+ deletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
+ trashedPdfFilePath);
+ deleteFiles(pendingVideoFile, trashedImageFile, pendingPdfFile, trashedPdfFile);
+ }
+ }
+ @Test
+ public void testDeletePendingAndTrashed_otherAppCantDelete() throws Exception {
+ final File pendingVideoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
+ final File trashedImageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+ final File pendingPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+ final File trashedPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
+ // Actual path of the file gets rewritten for pending and trashed files.
+ String pendingVideoFilePath = null;
+ String trashedImageFilePath = null;
+ String pendingPdfFilePath = null;
+ String trashedPdfFilePath = null;
+ try {
pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
@@ -2106,11 +2124,52 @@
// App can't delete other app's pending and trashed file.
assertCantDeletePathsAs(APP_A_HAS_RES, pendingVideoFilePath, trashedImageFilePath,
pendingPdfFilePath, trashedPdfFilePath);
+ } finally {
+ deletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
+ trashedPdfFilePath);
+ deleteFiles(pendingVideoFile, trashedImageFile, pendingPdfFile, trashedPdfFile);
+ }
+ }
+
+ @Test
+ public void testDeletePendingAndTrashed_fileManagerCanDelete() throws Exception {
+ final File pendingVideoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
+ final File trashedImageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+ final File pendingPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+ final File trashedPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
+ // Actual path of the file gets rewritten for pending and trashed files.
+ String pendingVideoFilePath = null;
+ String trashedImageFilePath = null;
+ String pendingPdfFilePath = null;
+ String trashedPdfFilePath = null;
+ try {
+ pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
+ trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
+ pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
+ trashedPdfFilePath = getFilePathFromUri(createTrashedFile(trashedPdfFile));
// File Manager can delete any pending and trashed file
assertCanDeletePathsAs(APP_FM, pendingVideoFilePath, trashedImageFilePath,
pendingPdfFilePath, trashedPdfFilePath);
+ } finally {
+ deletePaths(pendingVideoFilePath, trashedImageFilePath, pendingPdfFilePath,
+ trashedPdfFilePath);
+ deleteFiles(pendingVideoFile, trashedImageFile, pendingPdfFile, trashedPdfFile);
+ }
+ }
+ @Test
+ public void testDeletePendingAndTrashed_systemGalleryCanDeleteMedia() throws Exception {
+ final File pendingVideoFile = new File(getDcimDir(), VIDEO_FILE_NAME);
+ final File trashedImageFile = new File(getPicturesDir(), IMAGE_FILE_NAME);
+ final File pendingPdfFile = new File(getDownloadDir(), NONMEDIA_FILE_NAME);
+ final File trashedPdfFile = new File(getDocumentsDir(), NONMEDIA_FILE_NAME);
+ // Actual path of the file gets rewritten for pending and trashed files.
+ String pendingVideoFilePath = null;
+ String trashedImageFilePath = null;
+ String pendingPdfFilePath = null;
+ String trashedPdfFilePath = null;
+ try {
pendingVideoFilePath = getFilePathFromUri(createPendingFile(pendingVideoFile));
trashedImageFilePath = getFilePathFromUri(createTrashedFile(trashedImageFile));
pendingPdfFilePath = getFilePathFromUri(createPendingFile(pendingPdfFile));
diff --git a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java b/hostsidetests/securitybulletin/securityPatch/Bug-187957589/Android.bp
similarity index 68%
copy from common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
copy to hostsidetests/securitybulletin/securityPatch/Bug-187957589/Android.bp
index a64c3a0..2f0d2d0 100644
--- a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-187957589/Android.bp
@@ -14,7 +14,19 @@
* limitations under the License.
*/
-package com.android.bedstead.testapp.processor.annotations;
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
-public @interface TestAppCommunication {
+cc_test {
+ name: "Bug-187957589",
+ defaults: [
+ "cts_hostsidetests_securitybulletin_defaults"
+ ],
+ srcs: [
+ "poc.cpp"
+ ],
+ shared_libs: [
+ "libstatssocket",
+ ],
}
diff --git a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java b/hostsidetests/securitybulletin/securityPatch/Bug-187957589/poc.cpp
similarity index 68%
copy from common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
copy to hostsidetests/securitybulletin/securityPatch/Bug-187957589/poc.cpp
index a64c3a0..d3a6868 100644
--- a/common/device-side/bedstead/testapp/src/processor/main/java/com/android/bedstead/testapp/processor/annotations/TestAppCommunication.java
+++ b/hostsidetests/securitybulletin/securityPatch/Bug-187957589/poc.cpp
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +14,16 @@
* limitations under the License.
*/
-package com.android.bedstead.testapp.processor.annotations;
+#include <stats_event.h>
-public @interface TestAppCommunication {
+void poc() {
+ AStatsEvent* event = AStatsEvent_obtain();
+ AStatsEvent_setAtomId(event, -1338885832);
+ AStatsEvent_write(event);
+ AStatsEvent_release(event);
}
+
+int main(int /* argc */, char** /* argv */) {
+ poc();
+ return 0;
+}
\ No newline at end of file
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_187957589.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_187957589.java
new file mode 100644
index 0000000..6957ff9
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_187957589.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+import static org.junit.Assume.assumeFalse;
+
+import android.platform.test.annotations.SecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class Bug_187957589 extends SecurityTestCase {
+ /**
+ * b/187957589
+ * Vulnerability Behaviour: out of bounds write in noteAtomLogged for negative atom ids.
+ */
+ @SecurityTest(minPatchLevel = "unknown")
+ @Test
+ public void testPocBug_187957589() throws Exception {
+ assumeFalse(moduleIsPlayManaged("com.google.android.os.statsd"));
+ AdbUtils.runPoc("Bug-187957589", getDevice());
+ // Sleep to ensure statsd was able to process the injected event.
+ Thread.sleep(5_000);
+ AdbUtils.assertNoCrashes(getDevice(), "statsd");
+ }
+}
diff --git a/hostsidetests/silentupdate/app/Android.bp b/hostsidetests/silentupdate/app/Android.bp
index 6c6ef2c..3ea3ad6 100644
--- a/hostsidetests/silentupdate/app/Android.bp
+++ b/hostsidetests/silentupdate/app/Android.bp
@@ -19,17 +19,20 @@
android_test_helper_app {
name: "SilentInstallCurrent",
manifest: "AndroidManifest.xml",
- target_sdk_version: "30"
+ target_sdk_version: "30",
+ min_sdk_version:"28"
}
android_test_helper_app {
name: "SilentInstallQ",
manifest: "AndroidManifest.xml",
- target_sdk_version: "29"
+ target_sdk_version: "29",
+ min_sdk_version:"28"
}
android_test_helper_app {
name: "SilentInstallP",
manifest: "AndroidManifest.xml",
- target_sdk_version: "28"
+ target_sdk_version: "28",
+ min_sdk_version:"28"
}
diff --git a/hostsidetests/stagedinstall/Android.bp b/hostsidetests/stagedinstall/Android.bp
index e3e84f1..39618ff 100644
--- a/hostsidetests/stagedinstall/Android.bp
+++ b/hostsidetests/stagedinstall/Android.bp
@@ -19,7 +19,10 @@
java_test_host {
name: "CtsStagedInstallHostTestCases",
defaults: ["cts_defaults"],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ ":apex-info-list",
+ ],
libs: [
"cts-tradefed",
"cts-shim-host-lib",
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index 08da8a1..b9b12a2 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -30,12 +30,15 @@
import android.cts.install.lib.host.InstallUtilsHost;
import android.platform.test.annotations.LargeTest;
+import com.android.apex.ApexInfo;
+import com.android.apex.XmlParser;
import com.android.ddmlib.Log;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -44,6 +47,12 @@
import org.junit.runner.Description;
import org.junit.runner.RunWith;
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
@RunWith(DeviceJUnit4ClassRunner.class)
public class StagedInstallTest extends BaseHostJUnit4Test {
@@ -701,6 +710,82 @@
assertThat(getDevice().pullFile(shimApex.sourceDir)).isNotNull();
}
+ @Test
+ public void testApexInfoList() throws Exception {
+ assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+
+ // Check that content of /apex/apex-info-list.xml matches output of
+ // `adb shell pm list packages --apex-only --show-versioncode -f`.
+ List<ApexInfo> apexInfoList = readApexInfoList();
+ Set<ITestDevice.ApexInfo> activeApexes = getDevice().getActiveApexes();
+ assertThat(apexInfoList.size()).isEqualTo(activeApexes.size());
+ for (ITestDevice.ApexInfo apex : activeApexes) {
+ // Note: we can't assert equality of the apex.name and apexInfo.getModuleName() since
+ // they represent different concepts (the former is package name, while latter is apex
+ // module name)
+ List<ApexInfo> temp =
+ apexInfoList.stream()
+ .filter(a -> a.getModulePath().equals(apex.sourceDir))
+ .collect(Collectors.toList());
+ assertThat(temp).hasSize(1);
+ ApexInfo apexInfo = temp.get(0);
+ assertThat(apexInfo.getModulePath()).isEqualTo(apex.sourceDir);
+ assertThat(apexInfo.getVersionCode()).isEqualTo(apex.versionCode);
+ assertThat(apexInfo.getIsActive()).isTrue();
+ }
+ }
+
+ @Test
+ public void testApexInfoListAfterUpdate() throws Exception {
+ assumeTrue("Device does not support updating APEX", mHostUtils.isApexUpdateSupported());
+
+ installV2Apex();
+
+ List<ApexInfo> shimApexInfo =
+ readApexInfoList().stream()
+ .filter(a -> a.getModuleName().equals(SHIM_APEX_PACKAGE_NAME))
+ .collect(Collectors.toList());
+
+ assertThat(shimApexInfo).hasSize(2);
+
+ ApexInfo factoryShimApexInfo =
+ shimApexInfo.stream()
+ .filter(ApexInfo::getIsFactory)
+ .findAny()
+ .orElseThrow(() ->
+ new AssertionError(
+ "No factory version of " + SHIM_APEX_PACKAGE_NAME
+ + " found in /apex/apex-info-list.xml"));
+ assertThat(factoryShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
+ assertThat(factoryShimApexInfo.getIsActive()).isFalse();
+ assertThat(factoryShimApexInfo.getIsFactory()).isTrue();
+ assertThat(factoryShimApexInfo.getVersionCode()).isEqualTo(1);
+ assertThat(factoryShimApexInfo.getModulePath())
+ .isEqualTo(factoryShimApexInfo.getPreinstalledModulePath());
+
+ ApexInfo activeShimApexInfo =
+ shimApexInfo.stream()
+ .filter(ApexInfo::getIsActive)
+ .findAny()
+ .orElseThrow(() ->
+ new AssertionError(
+ "No active version of " + SHIM_APEX_PACKAGE_NAME
+ + " found in /apex/apex-info-list.xml"));
+ assertThat(activeShimApexInfo.getModuleName()).isEqualTo(SHIM_APEX_PACKAGE_NAME);
+ assertThat(activeShimApexInfo.getIsActive()).isTrue();
+ assertThat(activeShimApexInfo.getIsFactory()).isFalse();
+ assertThat(activeShimApexInfo.getVersionCode()).isEqualTo(2);
+ assertThat(activeShimApexInfo.getPreinstalledModulePath())
+ .isEqualTo(factoryShimApexInfo.getModulePath());
+ }
+
+ private List<ApexInfo> readApexInfoList() throws Exception {
+ File file = getDevice().pullFile("/apex/apex-info-list.xml");
+ try (FileInputStream stream = new FileInputStream(file)) {
+ return XmlParser.readApexInfoList(stream).getApexInfo();
+ }
+ }
+
/**
* Store the component name of the default launcher. This value will be used to reset the
* default launcher to its correct component upon test completion.
diff --git a/hostsidetests/statsdatom/Android.bp b/hostsidetests/statsdatom/Android.bp
index 80b99e9..181f32e 100644
--- a/hostsidetests/statsdatom/Android.bp
+++ b/hostsidetests/statsdatom/Android.bp
@@ -26,6 +26,7 @@
"src/**/appexit/*.java",
"src/**/appstart/*.java",
"src/**/batterycycle/*.java",
+ "src/**/batterystats/*.java",
"src/**/binderstats/*.java",
"src/**/bluetooth/*.java",
"src/**/cpu/*.java",
diff --git a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
index 060cb3f..6254cc1 100644
--- a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
+++ b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/AtomTests.java
@@ -42,7 +42,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
@@ -56,10 +55,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.cts.util.CtsNetUtils;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
-import android.net.wifi.WifiNetworkSuggestion;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
@@ -92,11 +88,8 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
@@ -226,10 +219,6 @@
APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_RECORD_INCOMING_PHONE_AUDIO, 115);
}
- private static boolean sWasVerboseLoggingEnabled;
- private static boolean sWasScanThrottleEnabled;
- private static boolean sWasWifiEnabled;
-
@Test
// Start the isolated service, which logs an AppBreadcrumbReported atom, and then exit.
public void testIsolatedProcessService() throws Exception {
@@ -983,11 +972,9 @@
* Bring up and generate some traffic on cellular data connection.
*/
@Test
- public void testGenerateMobileTraffic() throws IllegalStateException {
+ public void testGenerateMobileTraffic() throws Exception {
final Context context = InstrumentationRegistry.getContext();
- if (!doGenerateNetworkTraffic(context, NetworkCapabilities.TRANSPORT_CELLULAR)) {
- throw new IllegalStateException("Mobile network is not available.");
- }
+ doGenerateNetworkTraffic(context, NetworkCapabilities.TRANSPORT_CELLULAR);
}
// Constants which are locally used by doGenerateNetworkTraffic.
@@ -995,42 +982,21 @@
private static final String HTTPS_HOST_URL =
"https://connectivitycheck.gstatic.com/generate_204";
- /**
- * Returns a Network given a request. The return value is null if the requested Network is
- * unavailable, and the caller is responsible for logging the error.
- */
- private Network getNetworkFromRequest(@NonNull Context context,
- @NonNull NetworkRequest request) {
+ private void doGenerateNetworkTraffic(@NonNull Context context, int transport)
+ throws InterruptedException {
final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
+ final NetworkRequest request = new NetworkRequest.Builder().addCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET).addTransportType(transport).build();
final CtsNetUtils.TestNetworkCallback callback = new CtsNetUtils.TestNetworkCallback();
- ShellIdentityUtils.invokeWithShellPermissions(() -> cm.requestNetwork(request, callback));
- final Network network;
- try {
- network = callback.waitForAvailable();
- return network;
- } catch (InterruptedException e) {
- Log.w(TAG, "Caught an InterruptedException while looking for requested network!");
- return null;
- } finally {
- cm.unregisterNetworkCallback(callback);
- }
- }
- /**
- * Attempts to generate traffic on a network for with a given NetworkRequest. Returns true if
- * successful, and false is unsuccessful.
- */
- private boolean doGenerateNetworkTraffic(@NonNull Context context,
- @NonNull NetworkRequest request) throws IllegalStateException {
- final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
- final Network network = getNetworkFromRequest(context, request);
+ // Request network, and make http query when the network is available.
+ cm.requestNetwork(request, callback);
+
+ // If network is not available, throws IllegalStateException.
+ final Network network = callback.waitForAvailable();
if (network == null) {
- // Caller should log an error.
- return false;
- }
- if(!cm.bindProcessToNetwork(network)) {
- Log.e(TAG, "bindProcessToNetwork Failed!");
- throw new IllegalStateException("bindProcessToNetwork failed!");
+ throw new IllegalStateException("network with transport " + transport
+ + " is not available.");
}
final long startTime = SystemClock.elapsedRealtime();
@@ -1041,340 +1007,9 @@
} catch (Exception e) {
Log.e(TAG, "exerciseRemoteHost failed in " + (SystemClock.elapsedRealtime()
- startTime) + " ms: " + e);
+ } finally {
+ cm.unregisterNetworkCallback(callback);
}
- return true;
- }
-
- /**
- * Generates traffic on a network with a given transport.
- */
- private boolean doGenerateNetworkTraffic(@NonNull Context context, int transport)
- throws IllegalStateException {
- final NetworkRequest request = new NetworkRequest.Builder().addCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET).addTransportType(transport).build();
- return doGenerateNetworkTraffic(context, request);
- }
-
- /**
- * Generates traffic on a network with a given set of OEM managed network capabilities.
- */
- private boolean doGenerateOemManagedNetworkTraffic(@NonNull Context context,
- List<Integer> capabilities)
- throws IllegalStateException {
- final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder().addCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET);
- for (final Integer capability : capabilities) {
- requestBuilder.addCapability(capability);
- }
- final NetworkRequest request = requestBuilder.build();
- return doGenerateNetworkTraffic(context, request);
- }
-
- /**
- * Assembles a String representation of a list of NetworkCapabilities.
- */
- private String oemManagedCapabilitiesToString(@NonNull List<Integer> capabilities) {
- return "{" + TextUtils.join(", ", capabilities) + "}";
- }
- /**
- * Checks if a network with a given set of OEM managed capabilities (OEM_PAID, for example) is
- * avaialable.
- */
- private boolean isOemManagedNetworkAvailable(@NonNull Context context,
- List<Integer> capabilities) {
- final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder().addCapability(
- NetworkCapabilities.NET_CAPABILITY_INTERNET);
- for (final Integer capability : capabilities) {
- requestBuilder.addCapability(capability);
- }
- final NetworkRequest request = requestBuilder.build();
- final Network network = getNetworkFromRequest(context, request);
- if (network == null) {
- return false;
- }
- // There's an OEM managed network already available, so use that.
- return true;
- }
-
- /**
- * Callback WiFi scan results, on which we can await().
- */
- private static class TestScanResultsCallback extends WifiManager.ScanResultsCallback {
- private final CountDownLatch mCountDownLatch;
- public boolean onAvailableCalled = false;
-
- TestScanResultsCallback(CountDownLatch countDownLatch) {
- mCountDownLatch = countDownLatch;
- }
-
- @Override
- public void onScanResultsAvailable() {
- onAvailableCalled = true;
- mCountDownLatch.countDown();
- }
- }
-
- /**
- * Searches for saved WiFi networks, and returns a set of in-range saved WiFi networks.
- */
- private Set<WifiConfiguration> getAvailableSavedNetworks(@NonNull Context context,
- @NonNull WifiManager wifiManager) throws IllegalStateException {
- final Set<WifiConfiguration> availableNetworks = new HashSet<WifiConfiguration>();
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- final TestScanResultsCallback scanCallback = new TestScanResultsCallback(countDownLatch);
- final List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.getPrivilegedConfiguredNetworks());
-
- // If there are no saved networks, we can't connect to WiFi.
- if(savedNetworks.isEmpty()) {
- throw new IllegalStateException("Device has no saved WiFi networks.");
- }
- setUpOemManagedWifi(wifiManager, savedNetworks);
- wifiManager.registerScanResultsCallback(context.getMainExecutor(), scanCallback);
- wifiManager.startScan();
- try {
- final boolean didFinish = countDownLatch.await(10, TimeUnit.SECONDS);
- if (!didFinish) {
- Log.e(TAG, "Failed to get WiFi scan results: operation timed out.");
- // Empty list.
- return availableNetworks;
- }
- } catch (InterruptedException e) {
- Log.w(TAG, "Caught InterruptedException while waiting for WiFi scan results!");
- return availableNetworks;
- }
-
- // ScanResult could refer to Bluetooth or WiFi, so it has to be explicitly stated here.
- final List<android.net.wifi.ScanResult> scanResults = wifiManager.getScanResults();
- // Search for a saved network in range.
- for (final WifiConfiguration savedNetwork : savedNetworks) {
- for (final android.net.wifi.ScanResult scanResult : scanResults) {
- if (WifiInfo.sanitizeSsid(savedNetwork.SSID).equals(WifiInfo.sanitizeSsid(
- scanResult.SSID))) {
- // We found a saved network that's in range.
- availableNetworks.add(savedNetwork);
- }
- }
- }
- if(availableNetworks.isEmpty()) {
- throw new IllegalStateException("No saved networks in range.");
- }
- return availableNetworks;
- }
-
- /**
- * Causes WiFi to disconnect and prevents auto-join from reconnecting.
- */
- private void disableNetworksAndDisconnectWifi(@NonNull WifiManager wifiManager,
- @NonNull List<WifiConfiguration> savedNetworks) {
- // Disable auto-join for our saved networks, and disconnect from them.
- ShellIdentityUtils.invokeWithShellPermissions(
- () -> {
- for (final WifiConfiguration savedNetwork : savedNetworks) {
- wifiManager.disableNetwork(savedNetwork.networkId);
- }
- wifiManager.disconnect();
- });
- }
-
- /**
- * Puts the system back in the state we found it before setUpOemManagedWifi was called.
- */
- private void tearDownOemManagedWifi(@NonNull WifiManager wifiManager) {
- // Put the system back the way we found it.
- ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.setScanThrottleEnabled(sWasScanThrottleEnabled));
- ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.setVerboseLoggingEnabled(sWasVerboseLoggingEnabled));
- ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.setWifiEnabled(sWasWifiEnabled));
- }
-
- /**
- * Builds a suggestion based on a saved WiFi network with a given list of OEM managed
- * capabilities.
- */
- private static WifiNetworkSuggestion.Builder createOemManagedSuggestion(
- @NonNull WifiConfiguration network, List<Integer> capabilities)
- throws IllegalStateException {
- final WifiNetworkSuggestion.Builder suggestionBuilder = new WifiNetworkSuggestion.Builder();
- for (final Integer capability : capabilities) {
- switch (capability) {
- case NetworkCapabilities.NET_CAPABILITY_OEM_PAID:
- suggestionBuilder.setOemPaid(true);
- break;
- case NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE:
- suggestionBuilder.setOemPrivate(true);
- break;
- default:
- throw new IllegalStateException("Unsupported OEM network capability "
- + capability);
- }
- }
- suggestionBuilder.setSsid(WifiInfo.sanitizeSsid(network.SSID));
- if (network.preSharedKey != null) {
- if (network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
- suggestionBuilder.setWpa2Passphrase(WifiInfo.sanitizeSsid(network.preSharedKey));
- } else if (network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) {
- suggestionBuilder.setWpa3Passphrase(WifiInfo.sanitizeSsid(network.preSharedKey));
- } else {
- throw new IllegalStateException("Saved network has unsupported security type");
- }
- } else if (network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
- suggestionBuilder.setIsEnhancedOpen(true);
- } else if (!network.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
- throw new IllegalStateException("Saved network has unsupported security type");
- }
- suggestionBuilder.setIsHiddenSsid(network.hiddenSSID);
- return suggestionBuilder;
- }
-
- /**
- * Helper function for testGenerateOemManagedTraffic. Handles bringing up a WiFi network with a
- * given list of OEM managed capabilities, and generates traffic on said network.
- */
- private boolean setWifiOemManagedAndGenerateTraffic(@NonNull Context context,
- @NonNull WifiManager wifiManager, @NonNull WifiConfiguration network,
- List<Integer> capabilities) throws IllegalStateException {
- final WifiNetworkSuggestion.Builder oemSuggestionBuilder =
- createOemManagedSuggestion(network, capabilities);
- final WifiNetworkSuggestion oemSuggestion = oemSuggestionBuilder.build();
-
- final int status = ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.addNetworkSuggestions(Arrays.asList(oemSuggestion)));
- if (status != WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
- throw new IllegalStateException("Adding WifiNetworkSuggestion failed with "
- + status);
- }
-
- // Wait for the suggestion to go through.
- CountDownLatch suggestionLatch = new CountDownLatch(1);
- BroadcastReceiver receiver =
- registerReceiver(context, suggestionLatch, new IntentFilter(
- wifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION));
- PendingIntent pendingSuggestionIntent = PendingIntent.getBroadcast(context, 0,
- new Intent(wifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION),
- PendingIntent.FLAG_IMMUTABLE);
- waitForReceiver(context, 1_000, suggestionLatch, receiver);
-
- if (isOemManagedNetworkAvailable(context, capabilities)) {
- if (!doGenerateOemManagedNetworkTraffic(context, capabilities)) {
- throw new IllegalStateException("Network with "
- + oemManagedCapabilitiesToString(capabilities) + " is not available.");
- }
- } else {
- // Network didn't come up.
- Log.e(TAG, "isOemManagedNetworkAvailable reported " + network.SSID + " with "
- + oemManagedCapabilitiesToString(capabilities) + " is unavailable");
- wifiManager.removeNetworkSuggestions(Arrays.asList(oemSuggestion));
- return false;
- }
- wifiManager.removeNetworkSuggestions(Arrays.asList(oemSuggestion));
- return true;
- }
-
- /**
- * Does pre-requisite setup steps for testGenerateOemManagedTraffic via WiFi.
- */
- private void setUpOemManagedWifi(@NonNull WifiManager wifiManager,
- @NonNull List<WifiConfiguration> savedNetworks) {
- // Get the state the system was in before so we can put it back how we found it.
- sWasWifiEnabled = ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.isWifiEnabled());
- sWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.isVerboseLoggingEnabled());
- sWasScanThrottleEnabled = ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.isScanThrottleEnabled());
-
- // Turn on the wifi.
- if (!wifiManager.isWifiEnabled()) {
- ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.setWifiEnabled(true));
- }
-
- // Enable logging and disable scan throttling.
- ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.setVerboseLoggingEnabled(true));
- ShellIdentityUtils.invokeWithShellPermissions(
- () -> wifiManager.setScanThrottleEnabled(false));
-
- disableNetworksAndDisconnectWifi(wifiManager, savedNetworks);
- }
-
- /**
- * Brings up WiFi networks with specified combinations of OEM managed network capabilities and
- * generates traffic on said networks.
- */
- private void generateWifiOemManagedTraffic(Context context,
- List<List<Integer>> untestedCapabilities) {
- final PackageManager packageManager = context.getPackageManager();
- final WifiManager wifiManager = context.getSystemService(WifiManager.class);
- if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
- // If wifi isn't supported, don't bother trying.
- Log.w(TAG, "Feature WiFi is unavailable!");
- return;
- }
- final Set<WifiConfiguration> availableNetworks = getAvailableSavedNetworks(context,
- wifiManager);
-
- boolean foundGoodNetwork = false;
- // Try to connect to a saved network in range.
- for (final WifiConfiguration network : availableNetworks) {
- // Try each of the OEM network capabilities.
- for (final List<Integer> untestedCapability : untestedCapabilities) {
- boolean generatedTraffic = setWifiOemManagedAndGenerateTraffic(context, wifiManager,
- network, untestedCapability);
- if (foundGoodNetwork && !generatedTraffic) {
- // This already worked for a prior capability, so something is wrong.
- Log.e(TAG, network.SSID + " failed to come up with "
- + oemManagedCapabilitiesToString(untestedCapability));
- disableNetworksAndDisconnectWifi(wifiManager, Arrays.asList(network));
- tearDownOemManagedWifi(wifiManager);
- throw new IllegalStateException(network.SSID + " failed to come up!");
- } else if (!generatedTraffic) {
- // This network didn't work, try another one.
- break;
- }
- foundGoodNetwork = true;
- disableNetworksAndDisconnectWifi(wifiManager, Arrays.asList(network));
- }
- if (foundGoodNetwork) {
- break;
- }
- }
- tearDownOemManagedWifi(wifiManager);
- if (foundGoodNetwork == false) {
- throw new IllegalStateException("Couldn't connect to a good WiFi network!");
- }
- }
-
- /**
- * Bring up and generate some traffic on OEM managed WiFi network.
- */
- @Test
- public void testGenerateOemManagedTraffic() throws Exception {
- final Context context = InstrumentationRegistry.getContext();
-
- final List<List<Integer>> oemCapabilities = new LinkedList<>(List.of(
- List.of(NetworkCapabilities.NET_CAPABILITY_OEM_PAID),
- List.of(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE),
- List.of(NetworkCapabilities.NET_CAPABILITY_OEM_PAID,
- NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)));
- final List<List<Integer>> untestedCapabilities = new LinkedList<>(oemCapabilities);
-
- // In the event an OEM network exists already, use that to test.
- for (final List<Integer> oemCapability : oemCapabilities) {
- if (isOemManagedNetworkAvailable(context, oemCapability)) {
- doGenerateOemManagedNetworkTraffic(context,
- oemCapability);
- // Don't try to test on WiFi if the network already exists.
- untestedCapabilities.remove(oemCapability);
- }
- }
- if (untestedCapabilities.isEmpty()) return;
-
- // There are capabilities we still need to test, so use WiFi to simulate it.
- generateWifiOemManagedTraffic(context, untestedCapabilities);
}
/**
diff --git a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/StatsdCtsForegroundActivity.java b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/StatsdCtsForegroundActivity.java
index 58db263..ff50d21 100644
--- a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/StatsdCtsForegroundActivity.java
+++ b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/StatsdCtsForegroundActivity.java
@@ -51,6 +51,7 @@
public static final String ACTION_CREATE_CHANNEL_GROUP = "action.create_channel_group";
public static final String ACTION_POLL_NETWORK_STATS = "action.poll_network_stats";
public static final String ACTION_LMK = "action.lmk";
+ public static final String ACTION_DRAIN_POWER = "action.drain_power";
public static final int SLEEP_OF_ACTION_SLEEP_WHILE_TOP = 2_000;
public static final int SLEEP_OF_ACTION_SHOW_APPLICATION_OVERLAY = 2_000;
@@ -105,6 +106,9 @@
case ACTION_LMK:
new Thread(this::cmain).start();
break;
+ case ACTION_DRAIN_POWER:
+ doBusyWork();
+ break;
default:
Log.e(TAG, "Intent had invalid action " + action);
finish();
@@ -217,6 +221,26 @@
segfault();
}
+ private void doBusyWork() {
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ // Put a heavy load on the CPU
+ double sum = 0;
+ for (double t = 0; t < Double.MAX_VALUE; t += 1) {
+ sum += Math.sin(t);
+ }
+ Log.d(TAG, "Completed summing sines: " + sum);
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void nothing) {
+ finish();
+ }
+ }.execute();
+ }
+
private native void segfault();
/**
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/batterystats/BatteryUsageStatsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/batterystats/BatteryUsageStatsTests.java
new file mode 100644
index 0000000..dd179eb
--- /dev/null
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/batterystats/BatteryUsageStatsTests.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.cts.statsdatom.batterystats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.cts.statsdatom.lib.AtomTestUtils;
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.DeviceUtils;
+import android.cts.statsdatom.lib.ReportUtils;
+
+import com.android.internal.os.StatsdConfigProto;
+import com.android.os.AtomsProto;
+import com.android.os.AtomsProto.BatteryUsageStatsAtomsProto;
+import com.android.os.AtomsProto.BatteryUsageStatsAtomsProto.BatteryConsumerData;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.util.List;
+import java.util.function.Function;
+
+public class BatteryUsageStatsTests extends DeviceTestCase implements IBuildReceiver {
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assertThat(mCtsBuild).isNotNull();
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.installStatsdTestApp(getDevice(), mCtsBuild);
+ Thread.sleep(AtomTestUtils.WAIT_TIME_LONG);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ ConfigUtils.removeConfig(getDevice());
+ ReportUtils.clearReports(getDevice());
+ DeviceUtils.uninstallStatsdTestApp(getDevice());
+ super.tearDown();
+ }
+
+ public void testBatteryUsageStatsSinceReset() throws Exception {
+ if (!hasBattery()) {
+ return;
+ }
+
+ runBatteryUsageStatsAtomTest(AtomsProto.Atom.BATTERY_USAGE_STATS_SINCE_RESET_FIELD_NUMBER,
+ atom -> atom.getBatteryUsageStatsSinceReset().getBatteryUsageStats());
+ }
+
+ public void testBatteryUsageStatsSinceResetUsingPowerProfileModel() throws Exception {
+ if (!hasBattery()) {
+ return;
+ }
+
+ runBatteryUsageStatsAtomTest(
+ AtomsProto.Atom.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL_FIELD_NUMBER,
+ atom -> atom.getBatteryUsageStatsSinceResetUsingPowerProfileModel()
+ .getBatteryUsageStats());
+ }
+
+ private void runBatteryUsageStatsAtomTest(int atomFieldNumber,
+ Function<AtomsProto.Atom, BatteryUsageStatsAtomsProto> getter) throws Exception {
+ unplugDevice();
+ Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
+ try {
+ DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
+ "StatsdCtsForegroundActivity", "action", "action.drain_power", 5_000);
+ } finally {
+ plugInDevice();
+ }
+
+ StatsdConfigProto.StatsdConfig.Builder config =
+ ConfigUtils.createConfigBuilder(DeviceUtils.STATSD_ATOM_TEST_PKG);
+ ConfigUtils.addGaugeMetric(config, atomFieldNumber);
+ ConfigUtils.uploadConfig(getDevice(), config);
+
+ // Trigger atom pull.
+ AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
+ Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
+
+ final List<AtomsProto.Atom> atoms = ReportUtils.getGaugeMetricAtoms(getDevice());
+ assertThat(atoms.size()).isAtLeast(1);
+ for (final AtomsProto.Atom atom : atoms) {
+ final BatteryUsageStatsAtomsProto batteryUsageStats = getter.apply(atom);
+ assertBatteryUsageStatsAtom(batteryUsageStats);
+ }
+ }
+
+ private void assertBatteryUsageStatsAtom(BatteryUsageStatsAtomsProto batteryUsageStats)
+ throws Exception {
+ assertThat(batteryUsageStats.getSessionEndMillis()).isGreaterThan(
+ getDeviceTimeMs() - 5 * 60 * 1000);
+ assertThat(batteryUsageStats.getSessionDurationMillis()).isGreaterThan(0);
+
+ final BatteryConsumerData deviceBatteryConsumer =
+ batteryUsageStats.getDeviceBatteryConsumer();
+ assertThat(deviceBatteryConsumer.getTotalConsumedPowerDeciCoulombs()).isAtLeast(0);
+ for (BatteryConsumerData.PowerComponentUsage powerComponent :
+ deviceBatteryConsumer.getPowerComponentsList()) {
+ assertThat(powerComponent.getDurationMillis()).isAtLeast(0);
+ assertThat(powerComponent.getPowerDeciCoulombs()).isAtLeast(0);
+ }
+
+ boolean hasAppData = false;
+ final List<BatteryUsageStatsAtomsProto.UidBatteryConsumer> uidBatteryConsumers =
+ batteryUsageStats.getUidBatteryConsumersList();
+ for (BatteryUsageStatsAtomsProto.UidBatteryConsumer consumer : uidBatteryConsumers) {
+ if (consumer.getBatteryConsumerData().getTotalConsumedPowerDeciCoulombs() > 0
+ || consumer.getTimeInForegroundMillis() > 0
+ || consumer.getTimeInBackgroundMillis() > 0) {
+ hasAppData = true;
+ break;
+ }
+ }
+
+ assertThat(hasAppData).isTrue();
+ }
+
+ private boolean hasBattery() throws DeviceNotAvailableException {
+ final String batteryinfo = getDevice().executeShellCommand("dumpsys battery");
+ return batteryinfo.contains("present: true");
+ }
+
+ private void unplugDevice() throws Exception {
+ DeviceUtils.unplugDevice(getDevice());
+ getDevice().executeShellCommand("dumpsys battery suspend_input");
+ }
+
+ private void plugInDevice() throws Exception {
+ DeviceUtils.resetBatteryStatus(getDevice());
+ }
+
+ private long getDeviceTimeMs() throws Exception {
+ final String timeMs = getDevice().executeShellCommand("date +%s%3N");
+ return Long.parseLong(timeMs.trim());
+ }
+}
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/batterystats/OWNERS b/hostsidetests/statsdatom/src/android/cts/statsdatom/batterystats/OWNERS
new file mode 100644
index 0000000..3f5f220
--- /dev/null
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/batterystats/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:/BATTERY_STATS_OWNERS
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/incremental/OWNERS b/hostsidetests/statsdatom/src/android/cts/statsdatom/incremental/OWNERS
deleted file mode 100644
index 3794ba0..0000000
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/incremental/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-# Bug component: 554432
-alexbuy@google.com
-schfan@google.com
-toddke@google.com
-patb@google.com
-zyy@google.com
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java
index 3bd8a77..8e4ca4d 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/net/BytesTransferredTest.java
@@ -35,9 +35,7 @@
import java.util.List;
public class BytesTransferredTest extends DeviceTestCase implements IBuildReceiver {
- private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
- private static final String FEATURE_WIFI = "android.hardware.wifi";
private IBuildInfo mCtsBuild;
@@ -126,43 +124,20 @@
);
}
- public void testOemManagedBytesTransfer() throws Throwable {
- doTestOemManagedBytesTransferThat(Atom.OEM_MANAGED_BYTES_TRANSFER_FIELD_NUMBER, true,
- (atom) -> {
- final AtomsProto.OemManagedBytesTransfer data =
- atom.getOemManagedBytesTransfer();
- return new TransferredBytes(data.getRxBytes(), data.getTxBytes(),
- data.getRxPackets(), data.getTxPackets(), data.getUid(),
- data.getOemManagedType(), data.getTransportType());
- }
- );
- }
-
private static class TransferredBytes {
final long mRxBytes;
final long mTxBytes;
final long mRxPackets;
final long mTxPackets;
final long mAppUid;
- final long mOemManagedType;
- final long mTransportType;
public TransferredBytes(
long rxBytes, long txBytes, long rxPackets, long txPackets, long appUid) {
- this(rxBytes, txBytes, rxPackets, txPackets, appUid, /*oemManagedType=*/-1,
- /*transportType=*/-1);
- }
-
- public TransferredBytes(
- long rxBytes, long txBytes, long rxPackets, long txPackets, long appUid,
- long oemManagedType, long transportType) {
mRxBytes = rxBytes;
mTxBytes = txBytes;
mRxPackets = rxPackets;
mTxPackets = txPackets;
mAppUid = appUid;
- mOemManagedType = oemManagedType;
- mTransportType = transportType;
}
}
@@ -248,67 +223,6 @@
+ " is not found in " + atoms.size() + " atoms.").that(foundAppStats).isTrue();
}
- private void doTestOemManagedBytesTransferThat(int atomId, boolean isUidAtom,
- ThrowingPredicate<Atom, Exception> p)
- throws Throwable {
- /* PANS is for automotive platforms only, and this test relies on WiFi to simulate OEM
- * managed networks. */
- if (!DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE)
- || !DeviceUtils.hasFeature(getDevice(), FEATURE_WIFI)) return;
-
- // Upload the config.
- final StatsdConfig.Builder config = ConfigUtils.createConfigBuilder(
- DeviceUtils.STATSD_ATOM_TEST_PKG);
- if (isUidAtom) {
- ConfigUtils.addGaugeMetricForUidAtom(config, atomId, /*uidInAttributionChain=*/false,
- DeviceUtils.STATSD_ATOM_TEST_PKG);
- } else {
- ConfigUtils.addGaugeMetric(config, atomId);
- }
- ConfigUtils.uploadConfig(getDevice(), config);
- // Generate some mobile traffic.
- DeviceUtils.runDeviceTests(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, ".AtomTests",
- "testGenerateOemManagedTraffic");
- Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
- // Force poll NetworkStatsService to get most updated network stats from lower layer.
- DeviceUtils.runActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG,
- "PollNetworkStatsActivity",
- /*actionKey=*/null, /*actionValue=*/null);
- Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
- // Trigger atom pull.
- AtomTestUtils.sendAppBreadcrumbReportedAtom(getDevice());
- Thread.sleep(AtomTestUtils.WAIT_TIME_SHORT);
- final List<Atom> atoms = ReportUtils.getGaugeMetricAtoms(getDevice(),
- /*checkTimestampTruncated=*/false);
-
- assertThat(atoms.size()).isAtLeast(2);
- boolean foundAppStats = false;
- for (final Atom atom : atoms) {
- TransferredBytes transferredBytes = p.accept(atom);
- if (transferredBytes != null) {
- foundAppStats = true;
- // Checks that the uid in the atom corresponds to the app uid and checks that the
- // bytes and packet data are as expected.
- if (isUidAtom) {
- final int appUid = DeviceUtils.getAppUid(getDevice(),
- DeviceUtils.STATSD_ATOM_TEST_PKG);
- assertThat(transferredBytes.mAppUid).isEqualTo(appUid);
- }
- assertDataUsageAtomDataExpected(
- transferredBytes.mRxBytes, transferredBytes.mTxBytes,
- transferredBytes.mRxPackets, transferredBytes.mTxPackets);
-
- // Make sure we have a value for the OEM managed type.
- assertThat(transferredBytes.mOemManagedType).isGreaterThan(0);
- // Make sure there's a NetworkTemplate#MATCH_* value for transport_type.
- assertThat(transferredBytes.mTransportType).isGreaterThan(0);
- }
- }
- assertWithMessage("Data for uid " + DeviceUtils.getAppUid(getDevice(),
- DeviceUtils.STATSD_ATOM_TEST_PKG)
- + " is not found in " + atoms.size() + " atoms.").that(foundAppStats).isTrue();
- }
-
private void assertDataUsageAtomDataExpected(long rxb, long txb, long rxp, long txp) {
assertThat(rxb).isGreaterThan(0L);
assertThat(txb).isGreaterThan(0L);
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/net/OWNERS b/hostsidetests/statsdatom/src/android/cts/statsdatom/net/OWNERS
index 913ef27..f78f90b 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/net/OWNERS
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/net/OWNERS
@@ -1,5 +1,4 @@
# These atom tests are co-owned by statsd and network team
-chrisweir@google.com
jchalard@google.com
jeffreyhuang@google.com
jtnguyen@google.com
diff --git a/hostsidetests/theme/assets/S/140dpi.zip b/hostsidetests/theme/assets/31/140dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/140dpi.zip
rename to hostsidetests/theme/assets/31/140dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/180dpi.zip b/hostsidetests/theme/assets/31/180dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/180dpi.zip
rename to hostsidetests/theme/assets/31/180dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/200dpi.zip b/hostsidetests/theme/assets/31/200dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/200dpi.zip
rename to hostsidetests/theme/assets/31/200dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/220dpi.zip b/hostsidetests/theme/assets/31/220dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/220dpi.zip
rename to hostsidetests/theme/assets/31/220dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/260dpi.zip b/hostsidetests/theme/assets/31/260dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/260dpi.zip
rename to hostsidetests/theme/assets/31/260dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/280dpi.zip b/hostsidetests/theme/assets/31/280dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/280dpi.zip
rename to hostsidetests/theme/assets/31/280dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/300dpi.zip b/hostsidetests/theme/assets/31/300dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/300dpi.zip
rename to hostsidetests/theme/assets/31/300dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/340dpi.zip b/hostsidetests/theme/assets/31/340dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/340dpi.zip
rename to hostsidetests/theme/assets/31/340dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/360dpi.zip b/hostsidetests/theme/assets/31/360dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/360dpi.zip
rename to hostsidetests/theme/assets/31/360dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/400dpi.zip b/hostsidetests/theme/assets/31/400dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/400dpi.zip
rename to hostsidetests/theme/assets/31/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/420dpi.zip b/hostsidetests/theme/assets/31/420dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/420dpi.zip
rename to hostsidetests/theme/assets/31/420dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/440dpi.zip b/hostsidetests/theme/assets/31/440dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/440dpi.zip
rename to hostsidetests/theme/assets/31/440dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/560dpi.zip b/hostsidetests/theme/assets/31/560dpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/560dpi.zip
rename to hostsidetests/theme/assets/31/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/hdpi.zip b/hostsidetests/theme/assets/31/hdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/hdpi.zip
rename to hostsidetests/theme/assets/31/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/ldpi.zip b/hostsidetests/theme/assets/31/ldpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/ldpi.zip
rename to hostsidetests/theme/assets/31/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/mdpi.zip b/hostsidetests/theme/assets/31/mdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/mdpi.zip
rename to hostsidetests/theme/assets/31/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/tvdpi.zip b/hostsidetests/theme/assets/31/tvdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/tvdpi.zip
rename to hostsidetests/theme/assets/31/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/xhdpi.zip b/hostsidetests/theme/assets/31/xhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/xhdpi.zip
rename to hostsidetests/theme/assets/31/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/xxhdpi.zip b/hostsidetests/theme/assets/31/xxhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/xxhdpi.zip
rename to hostsidetests/theme/assets/31/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/S/xxxhdpi.zip b/hostsidetests/theme/assets/31/xxxhdpi.zip
similarity index 100%
rename from hostsidetests/theme/assets/S/xxxhdpi.zip
rename to hostsidetests/theme/assets/31/xxxhdpi.zip
Binary files differ
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java b/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
index 784fad4..4092360 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
@@ -127,6 +127,7 @@
.with("allow_while_idle_compat_quota", ALLOW_WHILE_IDLE_COMPAT_QUOTA)
.with("allow_while_idle_window", ALLOW_WHILE_IDLE_WINDOW)
.with("crash_non_clock_apps", true)
+ .with("kill_on_schedule_exact_alarm_revoked", false)
.commitAndAwaitPropagation();
}
@@ -440,7 +441,7 @@
}
@Test
- public void testActionScheduleExactAlarmPermissionStateChanged() throws Exception {
+ public void scheduleExactAlarmPermissionStateChangedSentAppOp() throws Exception {
// Revoke the permission.
revokeAppOp();
@@ -478,4 +479,44 @@
// the FGS.
assertTrue("App should be temp-allowlisted", checkThisAppTempAllowListed(myUid));
}
+
+ @Test
+ public void scheduleExactAlarmPermissionStateChangedSentDenyList() throws Exception {
+ mDeviceConfigHelper.with("exact_alarm_deny_list", sContext.getOpPackageName())
+ .commitAndAwaitPropagation();
+ assertFalse(mAlarmManager.canScheduleExactAlarms());
+
+ final int myUid = Process.myUid();
+
+ // Because prior tests may already put the app on the temp allowlist, wait until
+ // it's removed from it...
+ // TODO(b/188789296) We should use `cmd deviceidle tempwhitelist -r PACKAGE-NAME`, but
+ // it currently doesn't work.
+ TestUtils.waitUntil("App still on temp-allowlist", 60,
+ () -> !checkThisAppTempAllowListed(myUid));
+
+ final IntentFilter filter = new IntentFilter(
+ AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED);
+ final CountDownLatch latch = new CountDownLatch(1);
+ sContext.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ latch.countDown();
+ }
+ }, filter);
+
+ mDeviceConfigHelper.without("exact_alarm_deny_list").commitAndAwaitPropagation();
+ assertTrue(mAlarmManager.canScheduleExactAlarms());
+
+ assertTrue("Didn't receive ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED",
+ latch.await(30, TimeUnit.SECONDS));
+
+ // We really should try starting a foreground service to make sure the app is
+ // allowed to start an FGS here, but when an app is running on `am instrument`, it's always
+ // exempted anyway, so we can't do that. Instead, we just check the dumpsys output.
+ //
+ // TODO(b/188790230): Use the test app instead, and make sure the app can actually start
+ // the FGS.
+ assertTrue("App should be temp-allowlisted", checkThisAppTempAllowListed(myUid));
+ }
}
diff --git a/tests/MediaProviderTranscode/helper/AndroidManifest.xml b/tests/MediaProviderTranscode/helper/AndroidManifest.xml
index a9ad768..fcab321 100644
--- a/tests/MediaProviderTranscode/helper/AndroidManifest.xml
+++ b/tests/MediaProviderTranscode/helper/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application android:label="Transcode Test App">
+ <application android:label="Transcode Test App" android:debuggable="true">
<property android:name="android.media.PROPERTY_MEDIA_CAPABILITIES"
android:resource="@xml/media_capabilities" />
diff --git a/tests/app/ActivityManagerApi29Test/Android.bp b/tests/app/ActivityManagerApi29Test/Android.bp
index c1cf276..5418a8c 100644
--- a/tests/app/ActivityManagerApi29Test/Android.bp
+++ b/tests/app/ActivityManagerApi29Test/Android.bp
@@ -24,7 +24,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
sdk_version: "current",
}
diff --git a/tests/app/Android.bp b/tests/app/Android.bp
index 9fb2860..b3e421b 100644
--- a/tests/app/Android.bp
+++ b/tests/app/Android.bp
@@ -47,7 +47,6 @@
test_suites: [
"cts",
"general-tests",
- "sts"
],
instrumentation_for: "CtsAppTestStubs",
sdk_version: "test_current",
diff --git a/tests/app/BadProviderStubs/Android.bp b/tests/app/BadProviderStubs/Android.bp
index 6f3c010..1c4fca0 100644
--- a/tests/app/BadProviderStubs/Android.bp
+++ b/tests/app/BadProviderStubs/Android.bp
@@ -27,7 +27,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
optimize: {
enabled: false,
diff --git a/tests/app/CantSaveState1/Android.bp b/tests/app/CantSaveState1/Android.bp
index 22a3d21..9173cd9 100644
--- a/tests/app/CantSaveState1/Android.bp
+++ b/tests/app/CantSaveState1/Android.bp
@@ -24,7 +24,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
sdk_version: "current",
}
diff --git a/tests/app/CantSaveState2/Android.bp b/tests/app/CantSaveState2/Android.bp
index 4c461af..6e812af 100644
--- a/tests/app/CantSaveState2/Android.bp
+++ b/tests/app/CantSaveState2/Android.bp
@@ -24,7 +24,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
sdk_version: "current",
}
diff --git a/tests/app/NotificationDelegator/Android.bp b/tests/app/NotificationDelegator/Android.bp
index f4fbdd3..314742c 100644
--- a/tests/app/NotificationDelegator/Android.bp
+++ b/tests/app/NotificationDelegator/Android.bp
@@ -24,7 +24,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
sdk_version: "current",
}
diff --git a/tests/app/StorageDelegator/Android.bp b/tests/app/StorageDelegator/Android.bp
index bd38958..7727351 100644
--- a/tests/app/StorageDelegator/Android.bp
+++ b/tests/app/StorageDelegator/Android.bp
@@ -30,6 +30,5 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
}
diff --git a/tests/app/app/Android.bp b/tests/app/app/Android.bp
index d4594ed..8116415 100644
--- a/tests/app/app/Android.bp
+++ b/tests/app/app/Android.bp
@@ -45,7 +45,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
additional_manifests: ["ProviderAndroidManifest.xml"],
platform_apis: true,
@@ -78,7 +77,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
platform_apis: true,
aaptflags: [
@@ -113,7 +111,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
platform_apis: true,
aaptflags: [
@@ -148,7 +145,6 @@
test_suites: [
"cts",
"general-tests",
- "sts",
],
platform_apis: true,
aaptflags: [
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index 09faefe..2d02bc6 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -57,7 +57,6 @@
import android.os.PowerExemptionManager;
import android.os.SystemClock;
import android.permission.cts.PermissionUtils;
-import android.platform.test.annotations.SecurityTest;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -448,7 +447,6 @@
* @throws Exception
*/
@Test
- @SecurityTest(minPatchLevel = "2021-03")
public void testFgsLocationStartFromBGWithBind() throws Exception {
ApplicationInfo app1Info = mContext.getPackageManager().getApplicationInfo(
PACKAGE_NAME_APP1, 0);
diff --git a/tests/app/src/android/app/cts/DownloadManagerTestBase.java b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
index 7d174cf..30705f0 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTestBase.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTestBase.java
@@ -84,6 +84,7 @@
protected static final int MINIMUM_DOWNLOAD_BYTES = 100 * 1024 * 1024;
protected static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS;
+ protected static final long MEDIUM_TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
protected static final long LONG_TIMEOUT = 3 * DateUtils.MINUTE_IN_MILLIS;
private static final String ACTION_CREATE_FILE_WITH_CONTENT =
"com.android.cts.action.CREATE_FILE_WITH_CONTENT";
@@ -230,11 +231,11 @@
android.Manifest.permission.NETWORK_SETTINGS);
final long startTime = SystemClock.elapsedRealtime();
while (!hasConnectedNetwork(mCm)
- && (SystemClock.elapsedRealtime() - startTime) < SHORT_TIMEOUT) {
+ && (SystemClock.elapsedRealtime() - startTime) < MEDIUM_TIMEOUT) {
Thread.sleep(500);
}
if (!hasConnectedNetwork(mCm)) {
- Log.d(TAG, "Unable to connect to any network");
+ fail("Unable to connect to any network");
}
}
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index 3f8251f..922142f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -1449,9 +1449,12 @@
StreamConfigurationMap maxStreamConfigMap =
chars.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION);
openDevice(id);
-
- verifyBasicSensorPixelModes(id, defaultStreamConfigMap, /*maxResolution*/ false);
- verifyBasicSensorPixelModes(id, maxStreamConfigMap, /*maxResolution*/ true);
+ try {
+ verifyBasicSensorPixelModes(id, defaultStreamConfigMap, /*maxResolution*/ false);
+ verifyBasicSensorPixelModes(id, maxStreamConfigMap, /*maxResolution*/ true);
+ } finally {
+ closeDevice(id);
+ }
}
}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
index a6eb9aa..7dfafed 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/LockTaskTest.java
@@ -17,6 +17,7 @@
package android.devicepolicy.cts;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
+import static android.app.ActivityManager.LOCK_TASK_MODE_NONE;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
@@ -42,9 +43,12 @@
import com.android.bedstead.harrier.policies.LockTask;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.testapp.TestApp;
-import com.android.bedstead.testapp.TestAppActivityReference;
+import com.android.bedstead.testapp.TestAppActivity;
import com.android.bedstead.testapp.TestAppInstanceReference;
import com.android.bedstead.testapp.TestAppProvider;
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.eventlib.EventLogs;
+import com.android.eventlib.events.activities.ActivityDestroyedEvent;
import org.junit.ClassRule;
import org.junit.Ignore;
@@ -236,6 +240,28 @@
@Test
@Postsubmit(reason = "New test")
@PositivePolicyTest(policy = LockTask.class)
+ // TODO(scottjonathan): Support additional parameterization for cases like this
+ public void setLockTaskFeatures_overviewFeature_setsFeature() {
+
+ int originalLockTaskFeatures =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskFeatures();
+
+ try {
+ for (int flag : INDIVIDUALLY_SETTABLE_FLAGS) {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskFeatures(flag);
+
+ assertThat(sDeviceState.dpc().devicePolicyManager().getLockTaskFeatures())
+ .isEqualTo(flag);
+ }
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskFeatures(originalLockTaskFeatures);
+ }
+ }
+
+
+ @Test
+ @Postsubmit(reason = "New test")
+ @PositivePolicyTest(policy = LockTask.class)
public void setLockTaskFeatures_overviewFeature_throwsException() {
// Overview can only be used in combination with home
int originalLockTaskFeatures =
@@ -316,9 +342,9 @@
new String[]{sTestApp.packageName()});
try (TestAppInstanceReference testApp =
sTestApp.install(sTestApis.users().instrumented())) {
- TestAppActivityReference activity = testApp.activities().any().start();
+ TestAppActivity activity = testApp.activities().any().start();
- activity.remote().startLockTask();
+ startLockTaskAndWait(activity);
try {
assertThat(sTestApis.activities().foregroundActivity()).isEqualTo(
@@ -326,7 +352,7 @@
assertThat(sTestApis.activities().getLockTaskModeState()).isEqualTo(
LOCK_TASK_MODE_LOCKED);
} finally {
- activity.remote().stopLockTask();
+ stopLockTaskAndWait(activity);
}
} finally {
sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
@@ -341,9 +367,9 @@
sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{});
try (TestAppInstanceReference testApp =
sTestApp.install(sTestApis.users().instrumented())) {
- TestAppActivityReference activity = testApp.activities().any().start();
+ TestAppActivity activity = testApp.activities().any().start();
- activity.remote().startLockTask();
+ activity.startLockTask();
try {
assertThat(sTestApis.activities().foregroundActivity()).isEqualTo(
@@ -351,7 +377,7 @@
assertThat(sTestApis.activities().getLockTaskModeState()).isNotEqualTo(
LOCK_TASK_MODE_LOCKED);
} finally {
- activity.remote().stopLockTask();
+ stopLockTaskAndWait(activity);
}
} finally {
sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
@@ -368,9 +394,9 @@
new String[]{sTestApp.packageName()});
try (TestAppInstanceReference testApp =
sTestApp.install(sTestApis.users().instrumented())) {
- TestAppActivityReference activity = testApp.activities().any().start();
+ TestAppActivity activity = testApp.activities().any().start();
- activity.remote().startLockTask();
+ startLockTaskAndWait(activity);
try {
assertThat(sTestApis.activities().foregroundActivity()).isEqualTo(
@@ -378,10 +404,141 @@
assertThat(sTestApis.activities().getLockTaskModeState()).isNotEqualTo(
LOCK_TASK_MODE_LOCKED);
} finally {
- activity.remote().stopLockTask();
+ stopLockTaskAndWait(activity);
}
} finally {
sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
}
}
+
+ @Test
+ @PositivePolicyTest(policy = LockTask.class)
+ public void finish_isLocked_doesNotFinish() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(
+ new String[]{sTestApp.packageName()});
+ try (TestAppInstanceReference testApp =
+ sTestApp.install(sTestApis.users().instrumented())) {
+ TestAppActivity activity = testApp.activities().any().start();
+ startLockTaskAndWait(activity);
+
+ activity.finish();
+
+ try {
+ // We don't actually watch for the Destroyed event because that'd be waiting for a
+ // non occurrence of an event which is slow
+ assertThat(sTestApis.activities().foregroundActivity()).isEqualTo(
+ activity.reference());
+ assertThat(sTestApis.activities().getLockTaskModeState()).isEqualTo(
+ LOCK_TASK_MODE_LOCKED);
+ } finally {
+ stopLockTaskAndWait(activity);
+ }
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @PositivePolicyTest(policy = LockTask.class)
+ public void finish_hasStoppedLockTask_doesFinish() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(
+ new String[]{sTestApp.packageName()});
+ try (TestAppInstanceReference testApp =
+ sTestApp.install(sTestApis.users().instrumented())) {
+ TestAppActivity activity = testApp.activities().any().start();
+ startLockTaskAndWait(activity);
+ stopLockTaskAndWait(activity);
+
+ activity.finish();
+
+ // TODO(b/189327037): Replace with more direct integration between TestApp and EventLib
+ EventLogs<ActivityDestroyedEvent> events =
+ ActivityDestroyedEvent.queryPackage(sTestApp.packageName())
+ .whereActivity().className().isEqualTo(activity.reference().className());
+ assertThat(events.poll()).isNotNull();
+ assertThat(sTestApis.activities().foregroundActivity()).isNotEqualTo(
+ activity.reference());
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @PositivePolicyTest(policy = LockTask.class)
+ public void setLockTaskPackages_removingCurrentlyLockedTask_taskFinishes() {
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(
+ new String[]{sTestApp.packageName()});
+ try (TestAppInstanceReference testApp =
+ sTestApp.install(sTestApis.users().instrumented())) {
+ TestAppActivity activity = testApp.activities().any().start();
+ startLockTaskAndWait(activity);
+
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(new String[]{});
+
+ // TODO(b/189327037): Replace with more direct integration between TestApp and EventLib
+ EventLogs<ActivityDestroyedEvent> events =
+ ActivityDestroyedEvent.queryPackage(sTestApp.packageName())
+ .whereActivity().className().isEqualTo(activity.reference().className());
+ assertThat(events.poll()).isNotNull();
+ assertThat(sTestApis.activities().foregroundActivity()).isNotEqualTo(
+ activity.reference());
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ @Test
+ @PositivePolicyTest(policy = LockTask.class)
+ public void setLockTaskPackages_removingCurrentlyLockedTask_otherLockedTasksRemainLocked() {
+ TestApp secondTestApp = sTestAppProvider.any();
+ String[] originalLockTaskPackages =
+ sDeviceState.dpc().devicePolicyManager().getLockTaskPackages();
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(
+ new String[]{sTestApp.packageName(), secondTestApp.packageName()});
+ try (TestAppInstanceReference testApp =
+ sTestApp.install(sTestApis.users().instrumented());
+ TestAppInstanceReference testApp2 =
+ secondTestApp.install(sTestApis.users().instrumented())) {
+ TestAppActivity activity = testApp.activities().any().start();
+ startLockTaskAndWait(activity);
+ TestAppActivity activity2 = testApp2.activities().any().start();
+ startLockTaskAndWait(activity2);
+
+ try {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(
+ new String[]{sTestApp.packageName()});
+
+ // TODO(b/189327037): Replace with more direct integration between TestApp and EventLib
+ EventLogs<ActivityDestroyedEvent> events =
+ ActivityDestroyedEvent.queryPackage(secondTestApp.packageName())
+ .whereActivity().className().isEqualTo(
+ activity2.reference().className());
+ assertThat(events.poll()).isNotNull();
+ assertThat(sTestApis.activities().getLockTaskModeState()).isEqualTo(
+ LOCK_TASK_MODE_LOCKED);
+ assertThat(sTestApis.activities().foregroundActivity()).isEqualTo(
+ activity.reference());
+ } finally {
+ stopLockTaskAndWait(activity);
+ }
+ } finally {
+ sDeviceState.dpc().devicePolicyManager().setLockTaskPackages(originalLockTaskPackages);
+ }
+ }
+
+ private void startLockTaskAndWait(TestAppActivity activity) {
+ activity.startLockTask();
+ PollingCheck.waitFor(() -> sTestApis.activities().getLockTaskModeState() != LOCK_TASK_MODE_NONE);
+ }
+
+ private void stopLockTaskAndWait(TestAppActivity activity) {
+ activity.stopLockTask();
+ PollingCheck.waitFor(() -> sTestApis.activities().getLockTaskModeState() == LOCK_TASK_MODE_NONE);
+ }
}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 75cf23c..86b50fa 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -17,6 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="android.server.wm.cts"
android:targetSandboxVersion="2">
@@ -524,6 +525,13 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"/>
<activity android:name="android.server.wm.BlurTests$ListenerTestActivity"
android:exported="true"/>
+
+ <!-- Overrides the activity declaration in AndroidX test library to remove the starting
+ animation. -->
+ <activity
+ android:name="androidx.test.core.app.InstrumentationActivityInvoker$BootstrapActivity"
+ tools:replace="android:theme"
+ android:theme="@style/WhiteBackgroundTheme" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
index b009efe..2ae9628 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
@@ -34,7 +34,6 @@
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
@@ -67,7 +66,7 @@
* atest CtsWindowManagerDeviceTestCases:CompatChangeTests
*/
@Presubmit
-@FlakyTest(bugId = 182185145)
+@FlakyTest(bugId = 190609681)
public final class CompatChangeTests extends MultiDisplayTestBase {
private static final ComponentName RESIZEABLE_PORTRAIT_ACTIVITY =
component(ResizeablePortraitActivity.class);
@@ -81,7 +80,7 @@
component(SupportsSizeChangesPortraitActivity.class);
// Device aspect ratio (both portrait and landscape orientations) for min aspect ratio tests
- private static final float SIZE_COMPAT_DISPLAY_ASPECT_RATIO = 1.2f;
+ private static final float SIZE_COMPAT_DISPLAY_ASPECT_RATIO = 1.4f;
// Fixed orientation min aspect ratio
private static final float FIXED_ORIENTATION_MIN_ASPECT_RATIO = 1.03f;
// The min aspect ratio of NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY.
@@ -539,6 +538,8 @@
// Change the aspect ratio of the display to something that is smaller than all the aspect
// ratios used throughout those tests but still portrait. This ensures we're using
// enforcing aspect ratio behaviour within orientation.
+ // NOTE: using a smaller aspect ratio (e.g., 1.2) might cause activities to have a landscape
+ // window because of insets.
mDisplayMetricsSession.changeAspectRatio(SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
ORIENTATION_PORTRAIT);
launchActivity(activity);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
index afb3ffd..e145c52 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -287,7 +287,7 @@
void changeAspectRatio(double aspectRatio, int orientation) {
final Size originalSize = mInitialDisplayMetrics.physicalSize;
- final int smaller = originalSize.getWidth();
+ final int smaller = Math.min(originalSize.getWidth(), originalSize.getHeight());
final int larger = (int) (smaller * aspectRatio);
Size overrideSize;
if (orientation == ORIENTATION_LANDSCAPE) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleLegacySplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleLegacySplitScreenTests.java
index 79e56a0..c25d5e4 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleLegacySplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleLegacySplitScreenTests.java
@@ -39,7 +39,9 @@
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
+import android.app.ActivityOptions;
import android.app.Instrumentation;
+import android.app.WindowConfiguration;
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
@@ -131,9 +133,13 @@
// Launch an activity that will be moved to split-screen secondary
final Activity sideActivity = launchActivityAndWait(ThirdActivity.class);
+ ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+
// Launch an activity in a new task
final Activity firstActivity = new Launcher(FirstActivity.class)
.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK)
+ .setOptions(options)
.launch();
// Enter split screen
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
index e60ae4f..71e821a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java
@@ -40,15 +40,22 @@
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
+import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
import android.server.wm.ActivityLauncher;
+import android.server.wm.WindowManagerState;
import android.server.wm.app.Components;
+import android.util.Log;
+import android.view.WindowManager;
import org.junit.Test;
+import java.util.List;
+import java.util.function.Predicate;
+
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:ActivityStarterTests
@@ -392,9 +399,9 @@
// Launch alias activity.
getLaunchActivityBuilder().setUseInstrumentation().setTargetActivity(ALIAS_TEST_ACTIVITY)
.setIntentFlags(FLAG_ACTIVITY_NEW_TASK).execute();
- final int stacks = mWmState.getRootTasksCount(DEFAULT_DISPLAY);
- final int taskId =
- mWmState.getTaskByActivity(ALIAS_TEST_ACTIVITY).getTaskId();
+
+ final int testStacks = countTestRootTasks();
+ final int taskId = mWmState.getTaskByActivity(ALIAS_TEST_ACTIVITY).getTaskId();
// Return to home and launch the alias activity again.
launchHomeActivity();
@@ -402,8 +409,8 @@
.setIntentFlags(FLAG_ACTIVITY_NEW_TASK).execute();
assertEquals("Instance of the activity in its task must be only one", 1,
mWmState.getActivityCountInTask(taskId, ALIAS_TEST_ACTIVITY));
- assertEquals("Stacks counts should not be increased.", stacks,
- mWmState.getRootTasksCount(DEFAULT_DISPLAY));
+ assertEquals("Test stack count should not be increased.", testStacks,
+ countTestRootTasks());
// Return to home and launch the real activity.
launchHomeActivity();
@@ -411,8 +418,13 @@
.setIntentFlags(FLAG_ACTIVITY_NEW_TASK).execute();
assertEquals("Instance of the activity in its task must be only one", 1,
mWmState.getActivityCountInTask(taskId, ALIAS_TEST_ACTIVITY));
- assertEquals("Stacks counts should not be increased.", stacks,
- mWmState.getRootTasksCount(DEFAULT_DISPLAY));
+ assertEquals("Test stack count should not be increased.", testStacks,
+ countTestRootTasks());
+ }
+
+ private int countTestRootTasks() {
+ return mWmState.getRootTasksCount(
+ t -> ALIAS_TEST_ACTIVITY.getPackageName().equals(t.getPackageName()));
}
/**
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
index accfd35..388095c 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java
@@ -890,11 +890,14 @@
}
public int getRootTasksCount(int displayId) {
- int count = 0;
- for (ActivityTask rootTask : mRootTasks) {
- if (rootTask.mDisplayId == displayId) ++count;
- }
- return count;
+ return getRootTasksCount(t -> t.mDisplayId == displayId);
+ }
+
+ /**
+ * Count root tasks filtered by the predicate passed as argument.
+ */
+ public int getRootTasksCount(Predicate<? super ActivityTask> predicate) {
+ return (int) mRootTasks.stream().filter(predicate).count();
}
boolean pendingActivityContain(ComponentName activityName) {
@@ -1737,6 +1740,12 @@
return mName;
}
+ @NonNull
+ public String getPackageName() {
+ int sep = mName.indexOf('/');
+ return sep == -1 ? mName : mName.substring(0, sep);
+ }
+
String getToken() {
return mAppToken;
}
diff --git a/tests/location/common/src/android/location/cts/common/TestGnssMeasurementListener.java b/tests/location/common/src/android/location/cts/common/TestGnssMeasurementListener.java
index 1caea13..dbe3dc8 100644
--- a/tests/location/common/src/android/location/cts/common/TestGnssMeasurementListener.java
+++ b/tests/location/common/src/android/location/cts/common/TestGnssMeasurementListener.java
@@ -38,7 +38,8 @@
private boolean filterByEventSize = false;
// Timeout in sec for count down latch wait
private static final int STATUS_TIMEOUT_IN_SEC = 10;
- private static final int MEAS_TIMEOUT_IN_SEC = 75;
+ public static final int MEAS_TIMEOUT_IN_SEC = 75;
+ public static final int CORRELATION_VECTOR_TIMEOUT_IN_SEC = 10;
private static final int BIAS_UNCERTAINTY_TIMEOUT_IN_SEC = 10;
private static final int C_TO_N0_THRESHOLD_DB_HZ = 18;
private static final double BIAS_UNCERTAINTY_THRESHOLD_NANOS = 1e6; // 1 millisecond
@@ -49,6 +50,8 @@
private final CountDownLatch mCountDownLatch;
private final CountDownLatch mCountDownLatchStatus;
private final CountDownLatch mCountDownLatchBiasUncertainty;
+ private final CountDownLatch mCountDownLatchSatellitePvt;
+ private final CountDownLatch mCountDownLatchCorrelationVector;
/**
* Constructor for TestGnssMeasurementListener
@@ -68,16 +71,19 @@
}
/**
- * Constructor for TestGnssMeasurementListener
- * @param tag for Logging.
- * @param eventsToCollect wait until the number of events collected.
- * @param filterByEventSize whether filter the GnssMeasurementsEvents when we collect them.
- */
+ * Constructor for TestGnssMeasurementListener
+ *
+ * @param tag tag for Logging.
+ * @param eventsToCollect wait until this number of events collected.
+ * @param filterByEventSize whether to filter the GnssMeasurementsEvents when we collect them.
+ */
public TestGnssMeasurementListener(String tag, int eventsToCollect, boolean filterByEventSize) {
mTag = tag;
mCountDownLatch = new CountDownLatch(eventsToCollect);
mCountDownLatchStatus = new CountDownLatch(1);
mCountDownLatchBiasUncertainty = new CountDownLatch(1);
+ mCountDownLatchSatellitePvt = new CountDownLatch(1);
+ mCountDownLatchCorrelationVector = new CountDownLatch(1);
mMeasurementsEvents = new ArrayList<>(eventsToCollect);
this.filterByEventSize = filterByEventSize;
}
@@ -118,13 +124,29 @@
return;
}
}
- }
- else {
+ } else {
synchronized(mMeasurementsEvents) {
mMeasurementsEvents.add(event);
}
mCountDownLatch.countDown();
}
+
+ if (mCountDownLatchSatellitePvt.getCount() > 0) {
+ for (GnssMeasurement measurement : event.getMeasurements()) {
+ if (measurement.hasSatellitePvt()) {
+ Log.i(mTag, "Found a GnssMeasurement with SatellitePvt.");
+ mCountDownLatchSatellitePvt.countDown();
+ }
+ }
+ }
+ if (mCountDownLatchCorrelationVector.getCount() > 0) {
+ for (GnssMeasurement measurement : event.getMeasurements()) {
+ if (measurement.hasCorrelationVectors()) {
+ Log.i(mTag, "Found a GnssMeasurement with CorrelationVector.");
+ mCountDownLatchCorrelationVector.countDown();
+ }
+ }
+ }
GnssClock gnssClock = event.getClock();
if (gnssClock.hasBiasUncertaintyNanos()) {
if (gnssClock.getBiasUncertaintyNanos() < BIAS_UNCERTAINTY_THRESHOLD_NANOS) {
@@ -149,13 +171,28 @@
}
/**
- * Wait until {@link GnssClock#getBiasUncertaintyNanos()} ()} becomes small enough.
+ * Wait until {@link GnssClock#getBiasUncertaintyNanos()} becomes small enough.
*/
public boolean awaitSmallBiasUncertainty() throws InterruptedException {
return TestUtils.waitFor(mCountDownLatchBiasUncertainty, BIAS_UNCERTAINTY_TIMEOUT_IN_SEC);
}
/**
+ * Wait until a measurement with {@link GnssMeasurement#hasSatellitePvt()} is found.
+ */
+ public boolean awaitSatellitePvt() throws InterruptedException {
+ return TestUtils.waitFor(mCountDownLatchSatellitePvt, MEAS_TIMEOUT_IN_SEC);
+ }
+
+ /**
+ * Wait until a measurement with {@link GnssMeasurement#hasCorrelationVectors()} is found.
+ */
+ public boolean awaitCorrelationVector() throws InterruptedException {
+ return TestUtils.waitFor(mCountDownLatchCorrelationVector,
+ CORRELATION_VECTOR_TIMEOUT_IN_SEC);
+ }
+
+ /**
* @return {@code true} if the state of the test ensures that data is expected to be collected,
* {@code false} otherwise.
*/
diff --git a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
index ee25c6a..1e9d3b4 100644
--- a/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
+++ b/tests/location/common/src/android/location/cts/common/TestMeasurementUtil.java
@@ -30,6 +30,8 @@
import android.location.SatellitePvt;
import android.util.Log;
+import com.google.common.collect.Range;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -61,6 +63,13 @@
" listener has failed, this indicates a platform bug. Please report the issue with" +
" a full bugreport.";
+ private static final Range<Double> GPS_L5_QZSS_J5_FREQ_RANGE_HZ =
+ Range.closed(1.164e9, 1.189e9);
+ private static final Range<Double> GAL_E1_FREQ_RANGE_HZ =
+ Range.closed(1.559e9, 1.591e9);
+ private static final Range<Double> GAL_E5_FREQ_RANGE_HZ =
+ Range.closed(1.164e9, 1.218e9);
+
private enum GnssBand {
GNSS_L1,
GNSS_L2,
@@ -309,35 +318,17 @@
/**
* Assert all SystemApi fields in Gnss Measurement are in expected range.
*
- * @param testLocationManager TestLocationManager
* @param measurement GnssMeasurement
* @param softAssert custom SoftAssert
* @param timeInNs event time in ns
- * @param requireCorrVec assert correlation vectors outputs
- * @param requireSatPvt assert satellite PVT outputs
*/
- public static void assertAllGnssMeasurementSystemFields(
- TestLocationManager testLocationManager, GnssMeasurement measurement,
- SoftAssert softAssert, long timeInNs, boolean requireCorrVec, boolean requireSatPvt) {
+ public static void assertAllGnssMeasurementSystemFields(GnssMeasurement measurement,
+ SoftAssert softAssert, long timeInNs) {
- if (requireCorrVec) {
- softAssert.assertTrue("GnssMeasurement must has correlation vectors",
- timeInNs,
- "measurement.hasCorrelationVectors() == true",
- String.valueOf(measurement.hasCorrelationVectors()),
- measurement.hasCorrelationVectors());
- }
if (measurement.hasCorrelationVectors()) {
verifyCorrelationVectors(measurement, softAssert, timeInNs);
}
- if (requireSatPvt) {
- softAssert.assertTrue("GnssMeasurement must has satellite PVT",
- timeInNs,
- "measurement.hasSatellitePvt() == true",
- String.valueOf(measurement.hasSatellitePvt()),
- measurement.hasSatellitePvt());
- }
if (measurement.hasSatellitePvt()) {
verifySatellitePvt(measurement, softAssert, timeInNs);
}
@@ -826,13 +817,41 @@
} else if ((state & GnssMeasurement.STATE_BIT_SYNC)
== GnssMeasurement.STATE_BIT_SYNC) {
softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
- "GNSS_MEASUREMENT_STATE_BIT_SYNC",
- constellationType),
+ "GNSS_MEASUREMENT_STATE_BIT_SYNC",
+ constellationType),
timeInNs,
"0ms >= X <= 20ms",
String.valueOf(sv_time_ms),
sv_time_ms >= 0 && sv_time_ms <= 20);
+ } else if ((state & GnssMeasurement.STATE_2ND_CODE_LOCK)
+ == GnssMeasurement.STATE_2ND_CODE_LOCK) {
+ int maxReceivedSvTimeMs = -1;
+ if (isGpsL5OrQzssJ5I(measurement)) {
+ maxReceivedSvTimeMs = 10;
+ } else if (isGpsL5OrQzssJ5Q(measurement)) {
+ maxReceivedSvTimeMs = 20;
+ } else if (isGalileoE1C(measurement)) {
+ maxReceivedSvTimeMs = 100;
+ } else if (isGalileoE5AQ(measurement)) {
+ maxReceivedSvTimeMs = 100;
+ } else {
+ softAssert.assertTrue(
+ "Signal type does not have secondary code but has have "
+ + "STATE_2ND_CODE_LOCK state set. constellation="
+ + measurement.getConstellationType()
+ + ", carrierFrequencyHz=" + measurement.getCarrierFrequencyHz()
+ + ", codeType=" + measurement.getCodeType()
+ , false);
+ }
+
+ softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
+ "GNSS_MEASUREMENT_STATE_2ND_CODE_LOCK",
+ constellationType),
+ timeInNs,
+ "0ms >= X <= " + maxReceivedSvTimeMs + "ms",
+ String.valueOf(sv_time_ms),
+ sv_time_ms >= 0 && sv_time_ms <= maxReceivedSvTimeMs);
} else if ((state & GnssMeasurement.STATE_CODE_LOCK)
== GnssMeasurement.STATE_CODE_LOCK) {
softAssert.assertTrue(getReceivedSvTimeNsLogMessage(
@@ -1092,4 +1111,36 @@
satellitePvt.getTropoDelayMeters() < 100);
}
}
+
+ private static boolean isGpsL5OrQzssJ5I(GnssMeasurement measurement) {
+ return (measurement.getConstellationType() == GnssStatus.CONSTELLATION_GPS ||
+ measurement.getConstellationType() == GnssStatus.CONSTELLATION_QZSS)
+ && GPS_L5_QZSS_J5_FREQ_RANGE_HZ.contains(
+ (double) measurement.getCarrierFrequencyHz())
+ && measurement.hasCodeType()
+ && "I".equals(measurement.getCodeType());
+ }
+
+ private static boolean isGpsL5OrQzssJ5Q(GnssMeasurement measurement) {
+ return (measurement.getConstellationType() == GnssStatus.CONSTELLATION_GPS ||
+ measurement.getConstellationType() == GnssStatus.CONSTELLATION_QZSS)
+ && GPS_L5_QZSS_J5_FREQ_RANGE_HZ.contains(
+ (double) measurement.getCarrierFrequencyHz())
+ && measurement.hasCodeType()
+ && "Q".equals(measurement.getCodeType());
+ }
+
+ private static boolean isGalileoE1C(GnssMeasurement measurement) {
+ return measurement.getConstellationType() == GnssStatus.CONSTELLATION_GALILEO
+ && GAL_E1_FREQ_RANGE_HZ.contains((double) measurement.getCarrierFrequencyHz())
+ && measurement.hasCodeType()
+ && "C".equals(measurement.getCodeType());
+ }
+
+ private static boolean isGalileoE5AQ(GnssMeasurement measurement) {
+ return measurement.getConstellationType() == GnssStatus.CONSTELLATION_GALILEO
+ && GAL_E5_FREQ_RANGE_HZ.contains((double) measurement.getCarrierFrequencyHz())
+ && measurement.hasCodeType()
+ && "Q".equals(measurement.getCodeType());
+ }
}
diff --git a/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementValuesTest.java b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementValuesTest.java
index 74c67d4..3b99ebb 100644
--- a/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementValuesTest.java
+++ b/tests/location/location_privileged/src/android/location/cts/privileged/GnssMeasurementValuesTest.java
@@ -101,7 +101,8 @@
mLocationListener = new TestLocationListener(LOCATION_TO_COLLECT_COUNT);
mTestLocationManager.requestLocationUpdates(mLocationListener);
- mMeasurementListener = new TestGnssMeasurementListener(TAG);
+ mMeasurementListener = new TestGnssMeasurementListener(TAG, /* eventsToCollect= */
+ 0, /* filterByEventSize= */ false);
mTestLocationManager.registerGnssMeasurementCallback(
mMeasurementListener,
new GnssMeasurementRequest.Builder()
@@ -116,7 +117,25 @@
+ " Consider retrying test outdoors.",
success);
- Log.i(TAG, "Location status received = " + mLocationListener.isLocationReceived());
+ Log.i(TAG, "Location received = " + mLocationListener.isLocationReceived());
+
+ if (isSatPvtSupported) {
+ boolean foundSatellitePvt = mMeasurementListener.awaitSatellitePvt();
+ softAssert.assertTrue(
+ "SatellitPvt is supported. The test must find a measurement with SatellitePvt"
+ + " within " + TestGnssMeasurementListener.MEAS_TIMEOUT_IN_SEC
+ + " seconds.",
+ foundSatellitePvt);
+ }
+ if (isCorrVecSupported) {
+ boolean foundCorrelationVector = mMeasurementListener.awaitCorrelationVector();
+ softAssert.assertTrue(
+ "CorrelationVector is supported. The test must find a measurement with "
+ + "CorrelationVector within "
+ + TestGnssMeasurementListener.CORRELATION_VECTOR_TIMEOUT_IN_SEC
+ + " seconds.",
+ foundCorrelationVector);
+ }
List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
int eventCount = events.size();
@@ -131,8 +150,7 @@
assertNotNull("GnssMeasurementEvent cannot be null.", event);
long timeInNs = event.getClock().getTimeNanos();
for (GnssMeasurement measurement : event.getMeasurements()) {
- TestMeasurementUtil.assertAllGnssMeasurementSystemFields(mTestLocationManager,
- measurement, softAssert, timeInNs, isCorrVecSupported, isSatPvtSupported);
+ TestMeasurementUtil.assertAllGnssMeasurementSystemFields(measurement, softAssert, timeInNs);
}
}
softAssert.assertAll();
diff --git a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
index 5344686..a795e2e 100644
--- a/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
+++ b/tests/tests/car/src/android/car/cts/CarPropertyManagerTest.java
@@ -81,6 +81,9 @@
@GuardedBy("mLock")
private SparseArray<Integer> mErrorCounter = new SparseArray<>();
+ @GuardedBy("mLock")
+ private SparseArray<Integer> mErrorWithErrorCodeCounter = new SparseArray<>();
+
public int receivedEvent(int propId) {
int val;
synchronized (mLock) {
@@ -97,6 +100,14 @@
return val;
}
+ public int receivedErrorWithErrorCode(int propId) {
+ int val;
+ synchronized (mLock) {
+ val = mErrorWithErrorCodeCounter.get(propId, 0);
+ }
+ return val;
+ }
+
@Override
public void onChangeEvent(CarPropertyValue value) {
synchronized (mLock) {
@@ -114,6 +125,14 @@
}
}
+ @Override
+ public void onErrorEvent(int propId, int areaId, int errorCode) {
+ synchronized (mLock) {
+ int val = mErrorWithErrorCodeCounter.get(propId, 0) + 1;
+ mErrorWithErrorCodeCounter.put(propId, val);
+ }
+ }
+
public void resetCountDownLatch(int counter) {
mCountDownLatch = new CountDownLatch(counter);
mCounter = counter;
@@ -555,8 +574,10 @@
assertThat(speedListenerUI.receivedEvent(vehicleSpeed)).isEqualTo(NO_EVENTS);
assertThat(speedListenerUI.receivedError(vehicleSpeed)).isEqualTo(NO_EVENTS);
+ assertThat(speedListenerUI.receivedErrorWithErrorCode(vehicleSpeed)).isEqualTo(NO_EVENTS);
assertThat(speedListenerFast.receivedEvent(vehicleSpeed)).isEqualTo(NO_EVENTS);
assertThat(speedListenerFast.receivedError(vehicleSpeed)).isEqualTo(NO_EVENTS);
+ assertThat(speedListenerFast.receivedErrorWithErrorCode(vehicleSpeed)).isEqualTo(NO_EVENTS);
mCarPropertyManager.registerCallback(speedListenerUI, vehicleSpeed,
CarPropertyManager.SENSOR_RATE_UI);
@@ -567,6 +588,9 @@
assertThat(speedListenerUI.receivedEvent(vehicleSpeed)).isGreaterThan(NO_EVENTS);
assertThat(speedListenerFast.receivedEvent(vehicleSpeed)).isGreaterThan(
speedListenerUI.receivedEvent(vehicleSpeed));
+ // The test did not change property values, it should not get error with error codes.
+ assertThat(speedListenerUI.receivedErrorWithErrorCode(vehicleSpeed)).isEqualTo(NO_EVENTS);
+ assertThat(speedListenerFast.receivedErrorWithErrorCode(vehicleSpeed)).isEqualTo(NO_EVENTS);
mCarPropertyManager.unregisterCallback(speedListenerFast);
mCarPropertyManager.unregisterCallback(speedListenerUI);
diff --git a/tests/tests/car/src/android/car/cts/VehiclePropertyIdsTest.java b/tests/tests/car/src/android/car/cts/VehiclePropertyIdsTest.java
index d5520ab..49fa9dd 100644
--- a/tests/tests/car/src/android/car/cts/VehiclePropertyIdsTest.java
+++ b/tests/tests/car/src/android/car/cts/VehiclePropertyIdsTest.java
@@ -316,7 +316,8 @@
*/
@Test
public void testAllPropertiesAreMappedInToString() {
- List<Integer> systemProperties = getIntegersFromDataEnums(VehiclePropertyIds.class);
+ List<Integer> systemProperties = getIntegersFromDataEnums(
+ new VehiclePropertyIds().getClass());
for (int propertyId : systemProperties) {
String propertyString = VehiclePropertyIds.toString(propertyId);
assertThat(propertyString.startsWith("0x")).isFalse();
diff --git a/tests/tests/content/Android.bp b/tests/tests/content/Android.bp
index 7fb1863..b808b6b 100644
--- a/tests/tests/content/Android.bp
+++ b/tests/tests/content/Android.bp
@@ -51,6 +51,8 @@
"ShortcutManagerTestUtils",
"libincfs-prebuilt",
"HelloWorldResHardeningLib",
+ "Nene",
+ "Harrier",
],
// Use multi-dex as the compatibility-common-util-devicesidelib dependency
// on compatibility-device-util-axt pushes us beyond 64k methods.
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index a06403a..d334b05 100644
--- a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -99,6 +99,7 @@
assumeTrue(hasDataConnection());
assumeTrue(hasNotificationSupport());
assumeFalse(isRunningInVR());
+ assumeFalse(isWatch());
// If running in a test harness the Account Manager never denies access to an account. Hence
// the permission request will not trigger. b/72114924
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 4144f57..ada43ae 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -447,6 +447,10 @@
}
public void testRequestManageMedia() {
+ if (FeatureUtil.isAutomotive()) {
+ // Skip the test for automotive device.
+ return;
+ }
assertCanBeHandled(new Intent(Settings.ACTION_REQUEST_MANAGE_MEDIA));
}
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderTest.java b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
index b53f9f8..3a84a91 100644
--- a/tests/tests/content/src/android/content/cts/ContentProviderTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentProviderTest.java
@@ -16,6 +16,15 @@
package android.content.cts;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.testng.Assert.assertThrows;
import android.content.ContentProvider;
@@ -31,7 +40,20 @@
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
-import android.test.AndroidTestCase;
+import android.provider.MediaStore;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
+import com.android.bedstead.nene.TestApis;
+
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileNotFoundException;
@@ -40,18 +62,26 @@
/**
* Test {@link ContentProvider}.
*/
-public class ContentProviderTest extends AndroidTestCase {
+@RunWith(BedsteadJUnit4.class)
+public class ContentProviderTest {
private static final String TEST_PACKAGE_NAME = "android.content.cts";
private static final String TEST_FILE_NAME = "testFile.tmp";
private static final String TEST_DB_NAME = "test.db";
- @Override
- protected void tearDown() throws Exception {
- mContext.deleteDatabase(TEST_DB_NAME);
- mContext.deleteFile(TEST_FILE_NAME);
- super.tearDown();
+ @ClassRule
+ @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private static final Context sContext = ApplicationProvider.getApplicationContext();
+ private static final TestApis sTestApis = new TestApis();
+
+ @After
+ public void tearDown() throws Exception {
+ sContext.deleteDatabase(TEST_DB_NAME);
+ sContext.deleteFile(TEST_FILE_NAME);
}
+ @Test
public void testOpenAssetFile() throws IOException {
MockContentProvider mockContentProvider = new MockContentProvider();
Uri uri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE +
@@ -70,14 +100,15 @@
}
}
+ @Test
public void testAttachInfo() {
MockContentProvider mockContentProvider = new MockContentProvider();
ProviderInfo info1 = new ProviderInfo();
info1.readPermission = "android.permission.READ_SMS";
info1.writePermission = null; // Guarded by an app op not a permission.
- mockContentProvider.attachInfo(getContext(), info1);
- assertSame(getContext(), mockContentProvider.getContext());
+ mockContentProvider.attachInfo(sContext, info1);
+ assertSame(sContext, mockContentProvider.getContext());
assertEquals(info1.readPermission, mockContentProvider.getReadPermission());
assertEquals(info1.writePermission, mockContentProvider.getWritePermission());
@@ -85,7 +116,7 @@
info2.readPermission = "android.permission.READ_CONTACTS";
info2.writePermission = "android.permission.WRITE_CONTACTS";
mockContentProvider.attachInfo(null, info2);
- assertSame(getContext(), mockContentProvider.getContext());
+ assertSame(sContext, mockContentProvider.getContext());
assertEquals(info1.readPermission, mockContentProvider.getReadPermission());
assertEquals(info1.writePermission, mockContentProvider.getWritePermission());
@@ -100,12 +131,13 @@
assertEquals(info2.readPermission, mockContentProvider.getReadPermission());
assertEquals(info2.writePermission, mockContentProvider.getWritePermission());
- mockContentProvider.attachInfo(getContext(), info1);
- assertSame(getContext(), mockContentProvider.getContext());
+ mockContentProvider.attachInfo(sContext, info1);
+ assertSame(sContext, mockContentProvider.getContext());
assertEquals(info1.readPermission, mockContentProvider.getReadPermission());
assertEquals(info1.writePermission, mockContentProvider.getWritePermission());
}
+ @Test
public void testBulkInsert() {
MockContentProvider mockContentProvider = new MockContentProvider();
@@ -126,16 +158,18 @@
}
}
+ @Test
public void testGetContext() {
MockContentProvider mockContentProvider = new MockContentProvider();
assertNull(mockContentProvider.getContext());
- mockContentProvider.attachInfo(getContext(), null);
- assertSame(getContext(), mockContentProvider.getContext());
+ mockContentProvider.attachInfo(sContext, null);
+ assertSame(sContext, mockContentProvider.getContext());
mockContentProvider.attachInfo(null, null);
- assertSame(getContext(), mockContentProvider.getContext());
+ assertSame(sContext, mockContentProvider.getContext());
}
+ @Test
public void testAccessReadPermission() {
MockContentProvider mockContentProvider = new MockContentProvider();
assertNull(mockContentProvider.getReadPermission());
@@ -152,6 +186,7 @@
assertNull(mockContentProvider.getReadPermission());
}
+ @Test
public void testAccessWritePermission() {
MockContentProvider mockContentProvider = new MockContentProvider();
assertNull(mockContentProvider.getWritePermission());
@@ -164,11 +199,13 @@
assertNull(mockContentProvider.getWritePermission());
}
+ @Test
public void testIsTemporary() {
MockContentProvider mockContentProvider = new MockContentProvider();
assertFalse(mockContentProvider.isTemporary());
}
+ @Test
public void testOpenFile() {
MockContentProvider mockContentProvider = new MockContentProvider();
@@ -186,11 +223,12 @@
}
}
+ @Test
public void testOpenFileHelper() throws IOException {
// create a temporary File
- mContext.openFileOutput(TEST_FILE_NAME, Context.MODE_PRIVATE).close();
- File file = mContext.getFileStreamPath(TEST_FILE_NAME);
+ sContext.openFileOutput(TEST_FILE_NAME, Context.MODE_PRIVATE).close();
+ File file = sContext.getFileStreamPath(TEST_FILE_NAME);
assertTrue(file.exists());
ContentProvider cp = new OpenFileContentProvider(file.getAbsolutePath(), TEST_DB_NAME);
@@ -222,28 +260,33 @@
}
}
+ @Test
public void testOnConfigurationChanged() {
// cannot trigger this callback reliably
}
+ @Test
public void testOnLowMemory() {
// cannot trigger this callback reliably
}
+ @Test
public void testRefresh_DefaultImplReturnsFalse() {
MockContentProvider provider = new MockContentProvider();
assertFalse(provider.refresh(null, null, null));
}
+ @Test
public void testGetIContentProvider() {
MockContentProvider mockContentProvider = new MockContentProvider();
assertNotNull(mockContentProvider.getIContentProvider());
}
+ @Test
public void testClearCallingIdentity() {
final MockContentProvider provider = new MockContentProvider();
- provider.attachInfo(getContext(), new ProviderInfo());
+ provider.attachInfo(sContext, new ProviderInfo());
final CallingIdentity ident = provider.clearCallingIdentity();
try {
@@ -254,6 +297,7 @@
}
}
+ @Test
public void testCheckUriPermission() {
MockContentProvider provider = new MockContentProvider();
final Uri uri = Uri.parse("content://test");
@@ -261,12 +305,14 @@
provider.checkUriPermission(uri, android.os.Process.myUid(), 0));
}
+ @Test
public void testCreateContentUriForUser_nullUri_throwsNPE() {
assertThrows(
NullPointerException.class,
() -> ContentProvider.createContentUriForUser(null, UserHandle.of(7)));
}
+ @Test
public void testCreateContentUriForUser_nonContentUri_throwsIAE() {
final Uri uri = Uri.parse("notcontent://test");
assertThrows(
@@ -274,18 +320,63 @@
() -> ContentProvider.createContentUriForUser(uri, UserHandle.of(7)));
}
+ @Test
public void testCreateContentUriForUser_UriWithDifferentUserID_throwsIAE() {
- final Uri uri = Uri.parse("content://07@test");
+ final Uri uri = Uri.parse("content://07@Test");
assertThrows(
IllegalArgumentException.class,
() -> ContentProvider.createContentUriForUser(uri, UserHandle.of(7)));
}
+ @Test
public void testCreateContentUriForUser_UriWithUserID_unchanged() {
- final Uri uri = Uri.parse("content://7@test");
+ final Uri uri = Uri.parse("content://7@Test");
assertEquals(uri, ContentProvider.createContentUriForUser(uri, UserHandle.of(7)));
}
+ @Test
+ @EnsureHasWorkProfile
+ public void createContentUriForUser_returnsCorrectUri() {
+ final ContentResolver profileContentResolver =
+ sTestApis.context().androidContextAsUser(sDeviceState.workProfile())
+ .getContentResolver();
+ final String testContentDisplayName = "testContent.mp3";
+ final Uri workProfileUriWithoutUserId = createAndInsertTestAudioFile(
+ profileContentResolver, testContentDisplayName);
+
+ final Uri workProfileUriWithUserId = ContentProvider.createContentUriForUser(
+ workProfileUriWithoutUserId, sDeviceState.workProfile().userHandle());
+
+ assertThat(getAudioContentDisplayName(
+ sContext.getContentResolver(), workProfileUriWithUserId))
+ .isEqualTo(testContentDisplayName);
+ }
+
+ private Uri createAndInsertTestAudioFile(ContentResolver resolver, String displayName) {
+ final Uri audioCollection = MediaStore.Audio.Media.getContentUri(
+ MediaStore.VOLUME_EXTERNAL_PRIMARY);
+ final ContentValues testContent = new ContentValues();
+ testContent.put(MediaStore.Audio.Media.DISPLAY_NAME, displayName);
+ return resolver.insert(audioCollection, testContent);
+ }
+
+ private String getAudioContentDisplayName(ContentResolver resolver, Uri uri) {
+ String name = null;
+ try (Cursor cursor = resolver.query(
+ uri,
+ /* projection = */ null,
+ /* selection = */ null,
+ /* selectionArgs = */ null,
+ /* sortOrder = */ null)) {
+ final int nameColumn =
+ cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME);
+ if (cursor.moveToNext()) {
+ name = cursor.getString(nameColumn);
+ }
+ }
+ return name;
+ }
+
private class MockContentProvider extends ContentProvider {
private int mInsertCount = 0;
@@ -354,8 +445,8 @@
OpenFileContentProvider(String fileName, String dbName) {
// delete the database if it already exists
- mContext.deleteDatabase(dbName);
- mDb = mContext.openOrCreateDatabase(dbName, Context.MODE_PRIVATE, null);
+ sContext.deleteDatabase(dbName);
+ mDb = sContext.openOrCreateDatabase(dbName, Context.MODE_PRIVATE, null);
mDb.execSQL("CREATE TABLE files ( _data TEXT );");
mDb.execSQL("INSERT INTO files VALUES ( \"" + fileName + "\");");
}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
index 04fa81e..338f4ec 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -81,6 +81,7 @@
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -187,37 +188,66 @@
@LargeTest
@Test
- @Ignore("Temporary disable to unblock presubmit tests: b/186582141")
public void testSpaceAllocatedForPackage() throws Exception {
final String apk = createApkPath(TEST_APK);
final String idsig = createApkPath(TEST_APK_IDSIG);
+ final long appFileSize = new File(apk).length();
+ final AtomicBoolean firstTime = new AtomicBoolean(true);
+
+ getUiAutomation().adoptShellPermissionIdentity();
+
+ final long blockSize = Os.statvfs("/data/incremental").f_bsize;
+ final long preAllocatedBlocks = Os.statvfs("/data/incremental").f_bfree;
+
mSession =
new IncrementalInstallSession.Builder()
.addApk(Paths.get(apk), Paths.get(idsig))
.addExtraArgs("-t", "-i", CTS_PACKAGE_NAME)
.setLogger(new IncrementalDeviceConnection.Logger())
+ .setBlockFilter((block -> {
+ // Skip allocation check after first iteration.
+ if (!firstTime.getAndSet(false)) {
+ return true;
+ }
+
+ try {
+ final long postAllocatedBlocks =
+ Os.statvfs("/data/incremental").f_bfree;
+ final long freeSpaceDifference =
+ (preAllocatedBlocks - postAllocatedBlocks) * blockSize;
+ assertTrue(freeSpaceDifference
+ >= ((appFileSize * 1.015) + blockSize * 8));
+ } catch (Exception e) {
+ Log.i(TAG, "ErrnoException: ", e);
+ throw new AssertionError(e);
+ }
+ return true;
+ }))
.setBlockTransformer(new CompressingBlockTransformer())
.build();
- getUiAutomation().adoptShellPermissionIdentity();
- long preAllocatedBlocks = Os.statvfs("/data/incremental").f_bfree;
- long appFileSize = new File(apk).length();
- long blockSize = Os.statvfs("/data/incremental").f_bsize;
- //Assign pre-allocated value to fail test if allocation doesn't occur.
- long postAllocatedBlocks = preAllocatedBlocks;
+
try {
mSession.start(Executors.newSingleThreadExecutor(),
IncrementalDeviceConnection.Factory.reliable());
- Thread.sleep(50);
- postAllocatedBlocks = Os.statvfs("/data/incremental").f_bfree;
mSession.waitForInstallCompleted(30, TimeUnit.SECONDS);
} finally {
getUiAutomation().dropShellPermissionIdentity();
}
- long freeSpaceDifference = (preAllocatedBlocks - postAllocatedBlocks) * blockSize;
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
- //Check difference in free space after install starts is greater than package size.
- assertTrue(freeSpaceDifference >= appFileSize);
+
+ String installPath = executeShellCommand(String.format("pm path %s", TEST_APP_PACKAGE))
+ .replaceFirst("package:", "")
+ .trim();
+
+ // Retrieve size of APK.
+ Long apkTrimResult = Os.stat(installPath).st_size;
+
+ // Verify trim was applied. v2+ incfs version required for valid allocation results.
+ if (getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2)) {
+ assertTrue(apkTrimResult <= appFileSize);
+ }
}
@Test
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index 0a702ab..dc44bf4 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -1103,7 +1103,7 @@
private Point getVendorDisplaySize() {
String value = PropertyUtil.getProperty("vendor.display-size");
- if (value.isEmpty()) {
+ if (TextUtils.isEmpty(value)) {
return null;
}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java b/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java
index e99cc3f..7c5935d 100644
--- a/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java
+++ b/tests/tests/graphics/src/android/graphics/fonts/SystemFontsTest.java
@@ -16,6 +16,8 @@
package android.graphics.fonts;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -91,4 +93,21 @@
}
}
}
+
+ @Test
+ public void testAvailableFonts_FontAttributeGetters() {
+ // Because system fonts are configurable by device, we cannot assert specific values.
+ // Instead, we call attribute getter methods and verify if they returns valid values.
+ for (Font font : availableFonts) {
+ assertNotNull(font.getStyle());
+ assertNotNull(font.getLocaleList());
+ assertThat(font.getTtcIndex()).isAtLeast(0);
+ FontVariationAxis[] axes = font.getAxes();
+ if (axes != null) {
+ for (FontVariationAxis axis : axes) {
+ assertNotNull(axis);
+ }
+ }
+ }
+ }
}
diff --git a/tests/tests/hardware/res/raw/google_atvreferenceremote_register.json b/tests/tests/hardware/res/raw/google_atvreferenceremote_register.json
index db03a72..646bfcc 100755
--- a/tests/tests/hardware/res/raw/google_atvreferenceremote_register.json
+++ b/tests/tests/hardware/res/raw/google_atvreferenceremote_register.json
@@ -5,7 +5,7 @@
"vid": 0x0957,
"pid": 0x0001,
"bus": "bluetooth",
- "source": "KEYBOARD",
+ "source": "KEYBOARD | DPAD",
"descriptor": [
0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x85, 0x01, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00,
0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x01, 0x95, 0x05,
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java
index 7723c96..8f030d5 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java
@@ -67,6 +67,7 @@
private HidDevice mHidDevice;
private int mDeviceId;
private final int mRegisterResourceId;
+ private boolean mDelayAfterSetup = false;
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@@ -78,6 +79,22 @@
mRegisterResourceId = registerResourceId;
}
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ // Even though we already wait for all possible callbacks such as UHID_START and UHID_OPEN,
+ // and wait for the correct device to appear by specifying expected source type in the
+ // register command, some devices, perhaps due to splitting, do not produce events as soon
+ // as they are created. Adding a small delay resolves this issue.
+ if (mDelayAfterSetup) {
+ SystemClock.sleep(1000);
+ }
+ }
+
+ protected void addDelayAfterSetup() {
+ mDelayAfterSetup = true;
+ }
+
/** Check if input device has specific capability */
interface Capability {
boolean check(InputDevice inputDevice);
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/MicrosoftXboxOneSTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/MicrosoftXboxOneSTest.java
index fab8b50..0b52909 100755
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/MicrosoftXboxOneSTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/MicrosoftXboxOneSTest.java
@@ -1,45 +1,46 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.input.cts.tests;
-
-import android.hardware.cts.R;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MicrosoftXboxOneSTest extends InputHidTestCase {
-
- // Exercises the Bluetooth behavior of the Xbox One S controller
- public MicrosoftXboxOneSTest() {
- super(R.raw.microsoft_xboxones_register);
- }
-
- @Test
- public void testAllKeys() {
- testInputEvents(R.raw.microsoft_xboxones_keyeventtests);
- }
-
- @Test
- public void testAllMotions() {
- testInputEvents(R.raw.microsoft_xboxones_motioneventtests);
- }
-}
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input.cts.tests;
+
+import android.hardware.cts.R;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MicrosoftXboxOneSTest extends InputHidTestCase {
+
+ // Exercises the Bluetooth behavior of the Xbox One S controller
+ public MicrosoftXboxOneSTest() {
+ super(R.raw.microsoft_xboxones_register);
+ addDelayAfterSetup();
+ }
+
+ @Test
+ public void testAllKeys() {
+ testInputEvents(R.raw.microsoft_xboxones_keyeventtests);
+ }
+
+ @Test
+ public void testAllMotions() {
+ testInputEvents(R.raw.microsoft_xboxones_motioneventtests);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerJunglecatBluetoothTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerJunglecatBluetoothTest.java
index 5a9a8cd..c49704a 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerJunglecatBluetoothTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerJunglecatBluetoothTest.java
@@ -31,6 +31,7 @@
// Simulates the behavior of Razer Junglecat gamepad.
public RazerJunglecatBluetoothTest() {
super(R.raw.razer_junglecat_register);
+ addDelayAfterSetup();
}
@Test
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerRaijuMobileBluetoothTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerRaijuMobileBluetoothTest.java
index 210b368..a55c3b6 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerRaijuMobileBluetoothTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/RazerRaijuMobileBluetoothTest.java
@@ -33,6 +33,7 @@
// Simulates the behavior of Razer Raiju Mobile gamepad.
public RazerRaijuMobileBluetoothTest() {
super(R.raw.razer_raiju_mobile_bluetooth_register);
+ addDelayAfterSetup();
}
@Test
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java
index 889e415..09e30cf 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/UsbVoiceCommandTest.java
@@ -22,9 +22,11 @@
import android.app.UiAutomation;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.hardware.cts.R;
import android.hardware.input.cts.InputAssistantActivity;
import android.server.wm.WindowManagerStateHelper;
@@ -54,6 +56,7 @@
InstrumentationRegistry.getInstrumentation().getUiAutomation();
private final PackageManager mPackageManager =
InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
+ private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
private final Intent mVoiceIntent;
private final Intent mWebIntent;
private final List<String> mExcludedPackages = new ArrayList<String>();
@@ -126,8 +129,8 @@
// {@link PhoneWindowManager} in interceptKeyBeforeDispatching, on TVs platform,
// volume keys never go to the foreground app.
// Skip the key test for TV platform.
- assumeFalse("TV platform doesn't send volume keys to app, test should be skipped",
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK));
+ assumeFalse("TV platform and devices handling key events outside window manager "
+ + "don't send volume keys to app, test should be skipped", shouldSkipVolumeTest());
testInputEvents(R.raw.google_gamepad_keyevent_volume_tests);
}
@@ -148,4 +151,11 @@
wmStateHelper.waitForValidState(inputAssistant);
wmStateHelper.assertActivityDisplayed(inputAssistant);
}
+
+ private boolean shouldSkipVolumeTest() {
+ return mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || mContext.getResources().getBoolean(Resources.getSystem().getIdentifier(
+ "config_handleVolumeKeysInWindowManager",
+ "bool", "android"));
+ }
}
diff --git a/tests/tests/icu/CtsIcu4cTestCases.xml b/tests/tests/icu/CtsIcu4cTestCases.xml
index 3ef1629..e0c04fa 100644
--- a/tests/tests/icu/CtsIcu4cTestCases.xml
+++ b/tests/tests/icu/CtsIcu4cTestCases.xml
@@ -48,6 +48,7 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.icu4c.cts" />
<option name="runtime-hint" value="5s" />
+ <option name="device-listeners" value="com.android.modules.utils.testing.NativeCoverageHackInstrumentationListener" />
</test>
<test class="com.android.tradefed.testtype.HostTest" >
<option name="jar" value="ICU4CTestRunner.jar" />
diff --git a/tests/tests/icu/icu4c_testapp/Android.bp b/tests/tests/icu/icu4c_testapp/Android.bp
index 30fa910..f272940 100644
--- a/tests/tests/icu/icu4c_testapp/Android.bp
+++ b/tests/tests/icu/icu4c_testapp/Android.bp
@@ -26,6 +26,7 @@
static_libs: [
"compatibility-device-util-axt",
"ctstestrunner-axt",
+ "modules-utils-native-coverage-listener",
"nativetesthelper",
],
libs: [
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
index 79f87e0..010773f 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
@@ -16,11 +16,8 @@
package android.keystore.cts;
-import android.app.KeyguardManager;
-import android.content.Context;
import android.content.pm.PackageManager;
import android.security.KeyPairGeneratorSpec;
-import android.security.KeyStoreParameter;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
import android.test.AndroidTestCase;
@@ -28,8 +25,6 @@
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
-import android.keystore.cts.R;
-
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
@@ -51,6 +46,7 @@
import java.security.interfaces.ECKey;
import java.security.interfaces.RSAKey;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -60,6 +56,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
@@ -79,7 +76,10 @@
private static final String TEST_ALIAS_3 = "test3";
- private long mMaxTestDurationMillis;
+ // The maximum amount of time the "large number of keys" tests will spend on importing keys
+ // into key store. This is used as a time box so that lower-power devices don't take too long
+ // to run the tests.
+ private Duration mMaxImportDuration;
/*
* The keys and certificates below are generated with:
@@ -737,10 +737,10 @@
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
// Use a longer timeout on watches, which are generally less performant.
- mMaxTestDurationMillis =
+ mMaxImportDuration =
getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
- ? LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION_WATCH_MILLIS
- : LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION_MILLIS;
+ ? LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION_WATCH
+ : LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION;
}
@Override
@@ -1997,16 +1997,28 @@
}
private static final int MIN_SUPPORTED_KEY_COUNT = 1500;
- private static final long MINUTE_IN_MILLIS = 1000 * 60;
- private static final long LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION_MILLIS = 4 * MINUTE_IN_MILLIS;
- private static final long LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION_WATCH_MILLIS = 6 * MINUTE_IN_MILLIS;
+ private static final Duration LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION = Duration.ofMinutes(4);
+ private static final Duration LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION_WATCH
+ = Duration.ofMinutes(6);
- private static boolean isDeadlineReached(long startTimeMillis, long durationMillis) {
- long nowMillis = System.currentTimeMillis();
- if (nowMillis < startTimeMillis) {
- return true;
+ // Helper that tells callers if a given Duration has been exceeded since creation.
+ private static class TimeBox {
+ private long mStartTimeNanos = System.nanoTime();
+ private Duration mMaxDuration;
+
+ public TimeBox(Duration maxDuration) { mMaxDuration = maxDuration; }
+
+ public boolean isOutOfTime() {
+ long nowNanos = System.nanoTime();
+ if (nowNanos < mStartTimeNanos) {
+ return true;
+ }
+ return nowNanos - mStartTimeNanos > mMaxDuration.toNanos();
}
- return nowMillis - startTimeMillis > durationMillis;
+
+ public Duration elapsed() {
+ return Duration.ofNanos(System.nanoTime() - mStartTimeNanos);
+ }
}
@LargeTest
@@ -2015,12 +2027,10 @@
// key1 and key2 backed by Android Keystore work fine. The assumption is that if the
// underlying implementation has a limit on the number of keys, it'll either delete the
// oldest key (key1), or will refuse to add keys (key2).
- // The test imports as many keys as it can in a fixed amount of time instead of stopping
- // at MIN_SUPPORTED_KEY_COUNT to balance the desire to support an unlimited number of keys
- // with the constraints on how long the test can run and performance differences of hardware
- // under test.
-
- long testStartTimeMillis = System.currentTimeMillis();
+ // The test imports up MAX_NUMBER_OF_KEYS in a fixed amount of time, balancing the desire
+ // to load many keys while also limiting maximum test time. This allows fast hardware to
+ // run the test more quickly while also ensuring slower hardware loads as many keys as
+ // possible within mMaxImportDuration.
Certificate cert1 = TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key1_cert);
PrivateKey privateKey1 = TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key1_pkcs8);
@@ -2032,8 +2042,11 @@
Certificate cert3 = generateCertificate(FAKE_RSA_USER_1);
PrivateKey privateKey3 = generatePrivateKey("RSA", FAKE_RSA_KEY_1);
+ final int MAX_NUMBER_OF_KEYS = 2500;
+ final String ALIAS_PREFIX = "test_large_number_of_rsa_keys_";
+ int keyCount = 0;
+
mKeyStore.load(null);
- int latestImportedEntryNumber = 0;
try {
KeyProtection protectionParams = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
@@ -2044,30 +2057,11 @@
new KeyStore.PrivateKeyEntry(privateKey1, new Certificate[] {cert1}),
protectionParams);
- // Import key3 lots of times, under different aliases.
- while (!isDeadlineReached(testStartTimeMillis, mMaxTestDurationMillis)) {
- latestImportedEntryNumber++;
- if ((latestImportedEntryNumber % 1000) == 0) {
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- }
- String entryAlias = "test" + latestImportedEntryNumber;
- try {
- mKeyStore.setEntry(entryAlias,
- new KeyStore.PrivateKeyEntry(privateKey3, new Certificate[] {cert3}),
- protectionParams);
- } catch (Throwable e) {
- throw new RuntimeException("Entry " + entryAlias + " import failed", e);
- }
- }
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- if (latestImportedEntryNumber < MIN_SUPPORTED_KEY_COUNT) {
- fail("Failed to import " + MIN_SUPPORTED_KEY_COUNT + " keys in "
- + (System.currentTimeMillis() - testStartTimeMillis)
- + " ms. Imported: " + latestImportedEntryNumber + " keys");
- }
+ keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX,
+ new PrivateKeyEntry(privateKey3, new Certificate[] {cert3}), protectionParams);
- latestImportedEntryNumber++;
- String entryName2 = "test" + latestImportedEntryNumber;
+ keyCount++;
+ String entryName2 = "test" + keyCount;
mKeyStore.setEntry(entryName2,
new KeyStore.PrivateKeyEntry(privateKey2, new Certificate[] {cert2}),
protectionParams);
@@ -2094,16 +2088,7 @@
sig.update(message);
assertTrue(sig.verify(signature));
} finally {
- // Clean up Keystore without using KeyStore.aliases() which can't handle this many
- // entries.
- Log.i(TAG, "Deleting imported keys");
- for (int i = 0; i <= latestImportedEntryNumber; i++) {
- if ((i > 0) && ((i % 1000) == 0)) {
- Log.i(TAG, "Deleted " + i + " keys");
- }
- mKeyStore.deleteEntry("test" + i);
- }
- Log.i(TAG, "Deleted " + (latestImportedEntryNumber + 1) + " keys");
+ deleteManyTestKeys(keyCount, ALIAS_PREFIX);
}
}
@@ -2118,7 +2103,7 @@
// with the constraints on how long the test can run and performance differences of hardware
// under test.
- long testStartTimeMillis = System.currentTimeMillis();
+ TimeBox timeBox = new TimeBox(mMaxImportDuration);
Certificate cert1 = TestUtils.getRawResX509Certificate(getContext(), R.raw.ec_key1_cert);
PrivateKey privateKey1 = TestUtils.getRawResPrivateKey(getContext(), R.raw.ec_key1_pkcs8);
@@ -2130,8 +2115,11 @@
Certificate cert3 = generateCertificate(FAKE_EC_USER_1);
PrivateKey privateKey3 = generatePrivateKey("EC", FAKE_EC_KEY_1);
+ final int MAX_NUMBER_OF_KEYS = 2500;
+ final String ALIAS_PREFIX = "test_large_number_of_ec_keys_";
+ int keyCount = 0;
+
mKeyStore.load(null);
- int latestImportedEntryNumber = 0;
try {
KeyProtection protectionParams = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
@@ -2141,31 +2129,12 @@
new KeyStore.PrivateKeyEntry(privateKey1, new Certificate[] {cert1}),
protectionParams);
- // Import key3 lots of times, under different aliases.
- while (!isDeadlineReached(
- testStartTimeMillis, mMaxTestDurationMillis)) {
- latestImportedEntryNumber++;
- if ((latestImportedEntryNumber % 1000) == 0) {
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- }
- String entryAlias = "test" + latestImportedEntryNumber;
- try {
- mKeyStore.setEntry(entryAlias,
- new KeyStore.PrivateKeyEntry(privateKey3, new Certificate[] {cert3}),
- protectionParams);
- } catch (Throwable e) {
- throw new RuntimeException("Entry " + entryAlias + " import failed", e);
- }
- }
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- if (latestImportedEntryNumber < MIN_SUPPORTED_KEY_COUNT) {
- fail("Failed to import " + MIN_SUPPORTED_KEY_COUNT + " keys in "
- + (System.currentTimeMillis() - testStartTimeMillis)
- + " ms. Imported: " + latestImportedEntryNumber + " keys");
- }
+ keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX,
+ new KeyStore.PrivateKeyEntry(privateKey3, new Certificate[] {cert3}),
+ protectionParams);
- latestImportedEntryNumber++;
- String entryName2 = "test" + latestImportedEntryNumber;
+ keyCount++;
+ String entryName2 = "test" + keyCount;
mKeyStore.setEntry(entryName2,
new KeyStore.PrivateKeyEntry(privateKey2, new Certificate[] {cert2}),
protectionParams);
@@ -2192,16 +2161,7 @@
sig.update(message);
assertTrue(sig.verify(signature));
} finally {
- // Clean up Keystore without using KeyStore.aliases() which can't handle this many
- // entries.
- Log.i(TAG, "Deleting imported keys");
- for (int i = 0; i <= latestImportedEntryNumber; i++) {
- if ((i > 0) && ((i % 1000) == 0)) {
- Log.i(TAG, "Deleted " + i + " keys");
- }
- mKeyStore.deleteEntry("test" + i);
- }
- Log.i(TAG, "Deleted " + (latestImportedEntryNumber + 1) + " keys");
+ deleteManyTestKeys(keyCount, ALIAS_PREFIX);
}
}
@@ -2211,12 +2171,10 @@
// key1 and key2 backed by Android Keystore work fine. The assumption is that if the
// underlying implementation has a limit on the number of keys, it'll either delete the
// oldest key (key1), or will refuse to add keys (key2).
- // The test imports as many keys as it can in a fixed amount of time instead of stopping
- // at MIN_SUPPORTED_KEY_COUNT to balance the desire to support an unlimited number of keys
- // with the constraints on how long the test can run and performance differences of hardware
- // under test.
-
- long testStartTimeMillis = System.currentTimeMillis();
+ // The test imports up MAX_NUMBER_OF_KEYS in a fixed amount of time, balancing the desire
+ // to load many keys while also limiting maximum test time. This allows fast hardware to
+ // run the test more quickly while also ensuring slower hardware loads as many keys as
+ // possible within mMaxImportDuration.
SecretKey key1 = new TransparentSecretKey(
HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "AES");
@@ -2228,8 +2186,11 @@
SecretKey key3 = new TransparentSecretKey(
HexEncoding.decode("33333333333333333333777777777777"), "AES");
+ final int MAX_NUMBER_OF_KEYS = 10000;
+ final String ALIAS_PREFIX = "test_large_number_of_aes_keys_";
+ int keyCount = 0;
+
mKeyStore.load(null);
- int latestImportedEntryNumber = 0;
try {
KeyProtection protectionParams = new KeyProtection.Builder(
KeyProperties.PURPOSE_ENCRYPT)
@@ -2238,29 +2199,11 @@
.build();
mKeyStore.setEntry(entryName1, new KeyStore.SecretKeyEntry(key1), protectionParams);
- // Import key3 lots of times, under different aliases.
- while (!isDeadlineReached(testStartTimeMillis, mMaxTestDurationMillis)) {
- latestImportedEntryNumber++;
- if ((latestImportedEntryNumber % 1000) == 0) {
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- }
- String entryAlias = "test" + latestImportedEntryNumber;
- try {
- mKeyStore.setEntry(entryAlias,
- new KeyStore.SecretKeyEntry(key3), protectionParams);
- } catch (Throwable e) {
- throw new RuntimeException("Entry " + entryAlias + " import failed", e);
- }
- }
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- if (latestImportedEntryNumber < MIN_SUPPORTED_KEY_COUNT) {
- fail("Failed to import " + MIN_SUPPORTED_KEY_COUNT + " keys in "
- + (System.currentTimeMillis() - testStartTimeMillis)
- + " ms. Imported: " + latestImportedEntryNumber + " keys");
- }
+ keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX,
+ new KeyStore.SecretKeyEntry(key3), protectionParams);
- latestImportedEntryNumber++;
- String entryName2 = "test" + latestImportedEntryNumber;
+ ++keyCount;
+ String entryName2 = "test" + keyCount;
mKeyStore.setEntry(entryName2, new KeyStore.SecretKeyEntry(key2), protectionParams);
SecretKey keystoreKey2 = (SecretKey) mKeyStore.getKey(entryName2, null);
SecretKey keystoreKey1 = (SecretKey) mKeyStore.getKey(entryName1, null);
@@ -2282,16 +2225,7 @@
cipher.init(Cipher.DECRYPT_MODE, key2, cipherParams);
MoreAsserts.assertEquals(plaintext, cipher.doFinal(ciphertext));
} finally {
- // Clean up Keystore without using KeyStore.aliases() which can't handle this many
- // entries.
- Log.i(TAG, "Deleting imported keys");
- for (int i = 0; i <= latestImportedEntryNumber; i++) {
- if ((i > 0) && ((i % 1000) == 0)) {
- Log.i(TAG, "Deleted " + i + " keys");
- }
- mKeyStore.deleteEntry("test" + i);
- }
- Log.i(TAG, "Deleted " + (latestImportedEntryNumber + 1) + " keys");
+ deleteManyTestKeys(keyCount, ALIAS_PREFIX);
}
}
@@ -2306,7 +2240,7 @@
// with the constraints on how long the test can run and performance differences of hardware
// under test.
- long testStartTimeMillis = System.currentTimeMillis();
+ TimeBox timeBox = new TimeBox(mMaxImportDuration);
SecretKey key1 = new TransparentSecretKey(
HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "HmacSHA256");
@@ -2318,37 +2252,22 @@
SecretKey key3 = new TransparentSecretKey(
HexEncoding.decode("33333333333333333333777777777777"), "HmacSHA256");
+ final int MAX_NUMBER_OF_KEYS = 10000;
+ final String ALIAS_PREFIX = "test_large_number_of_hmac_keys_";
+ int keyCount = 0;
+
mKeyStore.load(null);
- int latestImportedEntryNumber = 0;
try {
KeyProtection protectionParams = new KeyProtection.Builder(
KeyProperties.PURPOSE_SIGN)
.build();
mKeyStore.setEntry(entryName1, new KeyStore.SecretKeyEntry(key1), protectionParams);
- // Import key3 lots of times, under different aliases.
- while (!isDeadlineReached(testStartTimeMillis, mMaxTestDurationMillis)) {
- latestImportedEntryNumber++;
- if ((latestImportedEntryNumber % 1000) == 0) {
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- }
- String entryAlias = "test" + latestImportedEntryNumber;
- try {
- mKeyStore.setEntry(entryAlias,
+ keyCount = importKeyManyTimes(MAX_NUMBER_OF_KEYS, ALIAS_PREFIX,
new KeyStore.SecretKeyEntry(key3), protectionParams);
- } catch (Throwable e) {
- throw new RuntimeException("Entry " + entryAlias + " import failed", e);
- }
- }
- Log.i(TAG, "Imported " + latestImportedEntryNumber + " keys");
- if (latestImportedEntryNumber < MIN_SUPPORTED_KEY_COUNT) {
- fail("Failed to import " + MIN_SUPPORTED_KEY_COUNT + " keys in "
- + (System.currentTimeMillis() - testStartTimeMillis)
- + " ms. Imported: " + latestImportedEntryNumber + " keys");
- }
- latestImportedEntryNumber++;
- String entryName2 = "test" + latestImportedEntryNumber;
+ keyCount++;
+ String entryName2 = "test" + keyCount;
mKeyStore.setEntry(entryName2, new KeyStore.SecretKeyEntry(key2), protectionParams);
SecretKey keystoreKey2 = (SecretKey) mKeyStore.getKey(entryName2, null);
SecretKey keystoreKey1 = (SecretKey) mKeyStore.getKey(entryName1, null);
@@ -2368,16 +2287,7 @@
"59b57e77e4e2cb36b5c7b84af198ac004327bc549de6931a1b5505372dd8c957"),
mac.doFinal(message));
} finally {
- // Clean up Keystore without using KeyStore.aliases() which can't handle this many
- // entries.
- Log.i(TAG, "Deleting imported keys");
- for (int i = 0; i <= latestImportedEntryNumber; i++) {
- if ((i > 0) && ((i % 1000) == 0)) {
- Log.i(TAG, "Deleted " + i + " keys");
- }
- mKeyStore.deleteEntry("test" + i);
- }
- Log.i(TAG, "Deleted " + (latestImportedEntryNumber + 1) + " keys");
+ deleteManyTestKeys(keyCount, ALIAS_PREFIX);
}
}
@@ -2561,4 +2471,97 @@
} catch (Exception ignored) {}
}
}
+
+ /**
+ * Import <code>key</code> up to <code>numberOfKeys</code> times, using parameters generated by
+ * <code>paramsBuilder</code>. This operation is done with multiple threads (one per logical
+ * CPU) to both stress keystore as well as improve throughput. Each key alias is prefixed with
+ * <code>aliasPrefix</code>.
+ *
+ * This method is time-bounded
+ */
+ private int importKeyManyTimes(int numberOfKeys, String aliasPrefix, Entry keyEntry,
+ KeyProtection protectionParams)
+ throws InterruptedException {
+ TimeBox timeBox = new TimeBox(mMaxImportDuration);
+ AtomicInteger keyCounter = new AtomicInteger(0);
+ ArrayList<Thread> threads = new ArrayList<>();
+ for (int i = 0; i < Runtime.getRuntime().availableProcessors(); ++i) {
+ threads.add(new Thread(() -> {
+ // Import key lots of times, under different aliases. Do this until we either run
+ // out of time or we import the key numberOfKeys times.
+ while (!timeBox.isOutOfTime()) {
+ int count = keyCounter.incrementAndGet();
+ if (count > numberOfKeys) {
+ // The loop is inherently racy, as multiple threads are simultaneously
+ // performing incrementAndGet operations. We only know if we've hit the
+ // limit _after_ we already incremented the counter. "Give the count back"
+ // before breaking so that we ensure keyCounter is accurate.
+ keyCounter.decrementAndGet();
+ break;
+ }
+ if ((count % 1000) == 0) {
+ Log.i(TAG, "Imported " + count + " keys");
+ }
+ String entryAlias = aliasPrefix + count;
+ try {
+ mKeyStore.setEntry(entryAlias, keyEntry, protectionParams);
+ } catch (Throwable e) {
+ throw new RuntimeException("Entry " + entryAlias + " import failed", e);
+ }
+ }
+ }));
+ }
+ // Start all the threads as close to one another as possible to spread the load evenly
+ for (int i = 0; i < threads.size(); ++i) {
+ threads.get(i).start();
+ }
+ for (int i = 0; i < threads.size(); ++i) {
+ threads.get(i).join();
+ }
+ Log.i(TAG, "Imported " + keyCounter.get() + " keys in " + timeBox.elapsed());
+ if (keyCounter.get() < MIN_SUPPORTED_KEY_COUNT) {
+ fail("Failed to import " + MIN_SUPPORTED_KEY_COUNT + " keys in "
+ + timeBox.elapsed() + ". Imported: " + keyCounter.get() + " keys");
+ }
+
+ return keyCounter.get();
+ }
+
+ /**
+ * Delete <code>numberOfKeys</code> keys that follow the pattern "[aliasPrefix][keyCounter]".
+ * This is done across multiple threads to both increase throughput as well as stress keystore.
+ */
+ private void deleteManyTestKeys(int numberOfKeys, String aliasPrefix)
+ throws InterruptedException {
+ // Clean up Keystore without using KeyStore.aliases() which can't handle this many
+ // entries.
+ AtomicInteger keyCounter = new AtomicInteger(numberOfKeys);
+ Log.i(TAG, "Deleting imported keys");
+ ArrayList<Thread> threads = new ArrayList<>();
+ for (int i = 0; i < Runtime.getRuntime().availableProcessors(); ++i) {
+ Log.i(TAG, "Spinning up cleanup thread " + i);
+ threads.add(new Thread(() -> {
+ for (int key = keyCounter.getAndDecrement(); key > 0;
+ key = keyCounter.getAndDecrement()) {
+ if ((key > 0) && ((key % 1000) == 0)) {
+ Log.i(TAG, "Deleted " + key + " keys");
+ }
+ try {
+ mKeyStore.deleteEntry("test" + key);
+ } catch (Exception e) {
+ fail("Unexpected exception in key cleanup: " + e);
+ }
+ }
+ }));
+ }
+ for (int i = 0; i < threads.size(); ++i) {
+ threads.get(i).start();
+ }
+ for (int i = 0; i < threads.size(); ++i) {
+ Log.i(TAG, "Joining test thread " + i);
+ threads.get(i).join();
+ }
+ Log.i(TAG, "Deleted " + numberOfKeys + " keys");
+ }
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/BackendBusyExceptionTest.java b/tests/tests/keystore/src/android/keystore/cts/BackendBusyExceptionTest.java
new file mode 100644
index 0000000..32b83dc
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/BackendBusyExceptionTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.keystore.cts;
+
+import android.security.keystore.BackendBusyException;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests basic functionality of the BackendBusyException.
+ */
+public class BackendBusyExceptionTest extends AndroidTestCase {
+ /**
+ * Tests if a BackendBusyException constructed with a given backoff hint returns
+ * that value through getBackOffHintMillis().
+ * Also the constructor must throw IllegalArgumentException if the backoff hint is negative.
+ */
+ public void testBackOffHint () {
+ BackendBusyException backendBusyException = new BackendBusyException(1);
+ assertEquals(backendBusyException.getBackOffHintMillis(), 1);
+ backendBusyException = new BackendBusyException(10);
+ assertEquals(backendBusyException.getBackOffHintMillis(), 10);
+ backendBusyException = new BackendBusyException(1, "Message");
+ assertEquals(backendBusyException.getBackOffHintMillis(), 1);
+ backendBusyException = new BackendBusyException(10, "Message");
+ assertEquals(backendBusyException.getBackOffHintMillis(), 10);
+ backendBusyException = new BackendBusyException(1, "Message", new Exception());
+ assertEquals(backendBusyException.getBackOffHintMillis(), 1);
+ backendBusyException = new BackendBusyException(10, "Message", new Exception());
+ assertEquals(backendBusyException.getBackOffHintMillis(), 10);
+
+ try {
+ new BackendBusyException(-1);
+ fail("Expected IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {}
+
+ try {
+ new BackendBusyException(-1, "Message");
+ fail("Expected IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {}
+
+ try {
+ new BackendBusyException(-1, "Message", new Exception());
+ fail("Expected IllegalArgumentException.");
+ } catch (IllegalArgumentException e) {}
+ }
+
+ /**
+ * Test that getMessage returns the message passed to the constructor.
+ */
+ public void testMessage() {
+ BackendBusyException backendBusyException = new BackendBusyException(1, "My test Message.");
+ assertTrue(backendBusyException.getMessage().equals("My test Message."));
+ backendBusyException = new BackendBusyException(1, "My test Message 2.", new Exception());
+ assertTrue(backendBusyException.getMessage().equals("My test Message 2."));
+ }
+
+ /**
+ * Test that getCause returns the cause passed to the constructor.
+ */
+ public void testCause() {
+ Exception cause = new Exception("My Cause");
+
+ BackendBusyException backendBusyException =
+ new BackendBusyException(1, "My test Message.", cause);
+ assertTrue(backendBusyException.getCause() == cause);
+ }
+
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java
index 07cf203..22c5a17 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyProtectionTest.java
@@ -53,6 +53,7 @@
assertEquals(GateKeeper.INVALID_SECURE_USER_ID, spec.getBoundToSpecificSecureUserId());
assertFalse(spec.isUnlockedDeviceRequired());
assertEquals(KeyProperties.UNRESTRICTED_USAGE_COUNT, spec.getMaxUsageCount());
+ assertEquals(spec.isStrongBoxBacked(), false);
}
public void testSettersReflectedInGetters() {
@@ -81,6 +82,7 @@
.setBoundToSpecificSecureUserId(654321)
.setUnlockedDeviceRequired(true)
.setMaxUsageCount(maxUsageCount)
+ .setIsStrongBoxBacked(true)
.build();
assertEquals(
@@ -105,6 +107,7 @@
assertEquals(654321, spec.getBoundToSpecificSecureUserId());
assertTrue(spec.isUnlockedDeviceRequired());
assertEquals(spec.getMaxUsageCount(), maxUsageCount);
+ assertEquals(spec.isStrongBoxBacked(), true);
}
public void testSetKeyValidityEndDateAppliesToBothEndDates() {
diff --git a/tests/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
index 2fe4afc..5b09a1a 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -42,10 +42,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
- <option name="run-command" value="pm revoke android.media.cts android.permission.SYSTEM_ALERT_WINDOW"/>
- </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="device" />
<option name="config-filename" value="CtsMediaTestCases" />
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index ae3969f..e31f439 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -45,6 +45,7 @@
import java.util.List;
@AppModeFull(reason = "Instant apps cannot access the SD card")
+@NonMediaMainlineTest
public class AudioPlaybackConfigurationTest extends CtsAndroidTestCase {
private final static String TAG = "AudioPlaybackConfigurationTest";
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
index 54e7142..5e5988a 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordAppOpTest.java
@@ -61,7 +61,12 @@
final int uid = Process.myUid();
final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
- final OnOpActiveChangedListener listener = mock(OnOpActiveChangedListener.class);
+ final OnOpActiveChangedListener mockListener = mock(OnOpActiveChangedListener.class);
+ final OnOpActiveChangedListener listener = new OnOpActiveChangedListener() {
+ public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
+ mockListener.onOpActiveChanged(op, uid, packageName, active);
+ }
+ };
AudioRecord recorder = null;
try {
@@ -89,7 +94,7 @@
recorder = candidateRecorder;
// The app op should start
- verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
+ verify(mockListener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
.only()).onOpActiveChanged(eq(OPSTR_RECORD_AUDIO),
eq(uid), eq(packageName), eq(true));
@@ -99,7 +104,7 @@
// Start with a clean slate
- Mockito.reset(listener);
+ Mockito.reset(mockListener);
// Stop recording
recorder.stop();
@@ -107,7 +112,7 @@
recorder = null;
// The app op should stop
- verify(listener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
+ verify(mockListener, timeout(APP_OP_CHANGE_TIMEOUT_MILLIS)
.only()).onOpActiveChanged(eq(OPSTR_RECORD_AUDIO),
eq(uid), eq(packageName), eq(false));
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 70ac29a..ab6eac5 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -32,6 +32,7 @@
import android.media.AudioRecord;
import android.media.AudioRecord.OnRecordPositionUpdateListener;
import android.media.AudioRecordingConfiguration;
+import android.media.AudioSystem;
import android.media.AudioTimestamp;
import android.media.AudioTrack;
import android.media.MediaRecorder;
@@ -389,6 +390,27 @@
// Audit buffers can run out of space with high numbers of channels,
// so keep the sample rate low.
+ // This tests the maximum reported Mixed PCM channel capability
+ // for AudioRecord and AudioTrack.
+ @Test
+ public void testAudioRecordAuditChannelIndexMax() throws Exception {
+ // We skip this test for isLowRamDevice(s).
+ // Otherwise if the build reports a high PCM channel count capability,
+ // we expect this CTS test to work at 16kHz.
+ if (isLowRamDevice()) {
+ return; // skip. FIXME: reenable when AF memory allocation is updated.
+ }
+ final int maxChannels = AudioSystem.OUT_CHANNEL_COUNT_MAX; // FCC_LIMIT
+ doTest("audit_channel_index_max", true /*localRecord*/, true /*customHandler*/,
+ 2 /*periodsPerSecond*/, 0 /*markerPeriodsPerSecond*/,
+ false /*useByteBuffer*/, false /*blocking*/,
+ true /*auditRecording*/, true /*isChannelIndex*/, 16000 /*TEST_SR*/,
+ (1 << maxChannels) - 1,
+ AudioFormat.ENCODING_PCM_16BIT);
+ }
+
+ // Audit buffers can run out of space with high numbers of channels,
+ // so keep the sample rate low.
@Test
public void testAudioRecordAuditChannelIndex3() throws Exception {
doTest("audit_channel_index_3", true /*localRecord*/, true /*customHandler*/,
diff --git a/tests/tests/media/src/android/media/cts/MediaCommunicationManagerTest.java b/tests/tests/media/src/android/media/cts/MediaCommunicationManagerTest.java
index e878bbc..9fde336 100644
--- a/tests/tests/media/src/android/media/cts/MediaCommunicationManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCommunicationManagerTest.java
@@ -16,6 +16,7 @@
package android.media.cts;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Context;
@@ -26,6 +27,7 @@
import android.os.Process;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SdkSuppress;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -47,6 +49,7 @@
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
+@SdkSuppress(minSdkVersion = 31, codeName = "S")
public class MediaCommunicationManagerTest {
private static final int TIMEOUT_MS = 5000;
private static final int WAIT_MS = 500;
@@ -62,6 +65,7 @@
@Test
public void testGetVersion() {
+ assertNotNull("Missing MediaCommunicationManager", mManager);
assertTrue(mManager.getVersion() > 0);
}
@@ -69,6 +73,7 @@
public void testGetSession2Tokens() throws Exception {
Executor executor = Executors.newSingleThreadExecutor();
+ assertNotNull("Missing MediaCommunicationManager", mManager);
ManagerSessionCallback managerCallback = new ManagerSessionCallback();
Session2Callback sessionCallback = new Session2Callback();
mManager.registerSessionCallback(executor, managerCallback);
@@ -89,6 +94,7 @@
public void testManagerSessionCallback() throws Exception {
Executor executor = Executors.newSingleThreadExecutor();
+ assertNotNull("Missing MediaCommunicationManager", mManager);
ManagerSessionCallback managerCallback = new ManagerSessionCallback();
Session2Callback sessionCallback = new Session2Callback();
mManager.registerSessionCallback(executor, managerCallback);
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmTest.java b/tests/tests/media/src/android/media/cts/MediaDrmTest.java
index c14aa30..778fe05 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmTest.java
@@ -16,6 +16,8 @@
package android.media.cts;
+import android.media.MediaCrypto;
+import android.media.MediaCryptoException;
import android.media.MediaDrm;
import android.media.NotProvisionedException;
import android.media.ResourceBusyException;
@@ -33,6 +35,8 @@
import org.junit.runner.RunWith;
import static junit.framework.Assert.assertTrue;
+
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertThrows;
@NonMediaMainlineTest
@@ -129,8 +133,10 @@
String logSessionId = "testPlaybackComponent";
component.setLogSessionId(new LogSessionId(logSessionId));
+ assertEquals(logSessionId, component.getLogSessionId().getStringId(),
+ "LogSessionId not set");
PersistableBundle metrics = drm.getMetrics();
- assertTrue("LogSessionId not set",
+ assertTrue("LogSessionId not found in metrics",
searchMetricsForValue(metrics, logSessionId));
} catch (UnsupportedOperationException | NotProvisionedException e) {
Log.w(TAG, "testPlaybackComponent: skipping scheme " + scheme, e);
@@ -141,4 +147,45 @@
}
}
}
+
+ private void testRequiresSecureDecoder(UUID scheme, MediaDrm drm)
+ throws ResourceBusyException, NotProvisionedException,
+ MediaCryptoException {
+ int[] levels = {
+ MediaDrm.SECURITY_LEVEL_SW_SECURE_CRYPTO,
+ MediaDrm.SECURITY_LEVEL_HW_SECURE_ALL,
+ MediaDrm.getMaxSecurityLevel()};
+ for (int level : levels) {
+ for (String mime : new String[]{"audio/mp4", "video/mp4"}) {
+ if (!MediaDrm.isCryptoSchemeSupported(scheme, mime, level)) {
+ continue;
+ }
+ byte[] sid = drm.openSession(level);
+ MediaCrypto crypto = new MediaCrypto(scheme, sid);
+ boolean supported1 = crypto.requiresSecureDecoderComponent(mime);
+ boolean supported2;
+ if (level == MediaDrm.getMaxSecurityLevel()) {
+ supported2 = drm.requiresSecureDecoder(mime);
+ } else {
+ supported2 = drm.requiresSecureDecoder(mime, level);
+ }
+ assertEquals(supported1, supported2, "secure decoder requirements inconsistent");
+ }
+ }
+ }
+
+ @Test
+ public void testRequiresSecureDecoder()
+ throws MediaCryptoException, UnsupportedSchemeException, ResourceBusyException {
+ for (UUID scheme: MediaDrm.getSupportedCryptoSchemes()) {
+ MediaDrm drm = new MediaDrm(scheme);
+ try {
+ testRequiresSecureDecoder(scheme, drm);
+ } catch (UnsupportedOperationException | NotProvisionedException e) {
+ Log.w(TAG, "testRequiresSecureDecoder: skipping scheme " + scheme, e);
+ } finally {
+ drm.close();
+ }
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaProjectionTest.java b/tests/tests/media/src/android/media/cts/MediaProjectionTest.java
index 7bd9c64..1b7e3d4 100644
--- a/tests/tests/media/src/android/media/cts/MediaProjectionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaProjectionTest.java
@@ -15,18 +15,24 @@
*/
package android.media.cts;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.app.ActivityManager;
import android.content.Context;
import android.media.projection.MediaProjection;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import android.provider.Settings;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
+import com.android.compatibility.common.util.ShellUtils;
+
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -60,6 +66,12 @@
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
+ runWithShellPermissionIdentity(() -> {
+ mContext.getPackageManager().revokeRuntimePermission(
+ mContext.getPackageName(),
+ android.Manifest.permission.SYSTEM_ALERT_WINDOW,
+ new UserHandle(ActivityManager.getCurrentUser()));
+ });
}
@Test
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 4312ffa..b662e81 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -40,6 +40,7 @@
import android.media.MicrophoneDirection;
import android.media.MicrophoneInfo;
import android.media.cts.AudioRecordingConfigurationTest.MyAudioRecordingCallback;
+import android.media.metrics.LogSessionId;
import android.opengl.GLES20;
import android.os.Build;
import android.os.ConditionVariable;
@@ -1815,4 +1816,15 @@
assertTrue(mMediaRecorder.isPrivacySensitive());
}
+ public void testSetGetLogSessionId() {
+ MediaRecorder recorder = new MediaRecorder();
+ assertEquals(recorder.getLogSessionId(), LogSessionId.LOG_SESSION_ID_NONE);
+
+ LogSessionId logSessionId = new LogSessionId("0123456789abcdef");
+ recorder.setLogSessionId(logSessionId);
+ assertEquals(recorder.getLogSessionId(), logSessionId);
+
+ recorder.release();
+ }
+
}
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
index 4970766..84b05ea 100644
--- a/tests/tests/media/src/android/media/cts/RoutingTest.java
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -573,6 +573,7 @@
static class AudioRoutingListener implements AudioRouting.OnRoutingChangedListener
{
private boolean mCalled;
+ private boolean mCallExpected;
private CountDownLatch mCountDownLatch;
AudioRoutingListener() {
@@ -591,6 +592,14 @@
}
}
+ void setCallExpected(boolean flag) {
+ mCallExpected = flag;
+ }
+
+ boolean isCallExpected() {
+ return mCallExpected;
+ }
+
boolean isRoutingListenerCalled() {
return mCalled;
}
@@ -598,6 +607,7 @@
void reset() {
mCountDownLatch = new CountDownLatch(1);
mCalled = false;
+ mCallExpected = true;
}
}
@@ -737,10 +747,12 @@
listener.isRoutingListenerCalled());
listener.reset();
+ listener.setCallExpected(false);
for (AudioDeviceInfo device : devices) {
if (routedDevice.getId() != device.getId() &&
device.getType() != AudioDeviceInfo.TYPE_TELEPHONY) {
mediaPlayer.setPreferredDevice(device);
+ listener.setCallExpected(true);
listener.await(WAIT_ROUTING_CHANGE_TIME_MS);
break;
}
@@ -750,8 +762,10 @@
mediaPlayer.stop();
mediaPlayer.release();
- assertTrue("Routing changed callback has not been called",
- listener.isRoutingListenerCalled());
+ if (listener.isCallExpected()) {
+ assertTrue("Routing changed callback has not been called",
+ listener.isRoutingListenerCalled());
+ }
}
public void test_mediaPlayer_incallMusicRoutingPermissions() {
diff --git a/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java b/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java
index 07d4642..69ec158 100644
--- a/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java
+++ b/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java
@@ -24,6 +24,7 @@
import android.graphics.Color;
import static android.media.MediaFormat.MIMETYPE_VIDEO_HEVC;
import android.media.ThumbnailUtils;
+import android.os.Build;
import android.platform.test.annotations.AppModeFull;
import android.util.Size;
@@ -34,6 +35,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import com.android.compatibility.common.util.ApiLevelUtil;
import com.android.compatibility.common.util.MediaUtils;
import java.io.File;
@@ -49,6 +51,9 @@
@AppModeFull(reason = "Instant apps cannot access the SD card")
@RunWith(JUnitParamsRunner.class)
public class ThumbnailUtilsTest {
+
+ private boolean mIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
+
private static final Size[] TEST_SIZES = new Size[] {
new Size(50, 50),
new Size(500, 500),
@@ -177,6 +182,7 @@
@Test
public void testCreateImageThumbnailAvif() throws Exception {
+ if (!MediaUtils.check(mIsAtLeastS, "test needs Android 12")) return;
final File file = stageFile("sample.avif", new File(mDir, "cts.avif"));
for (Size size : TEST_SIZES) {
diff --git a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
index 6fd3b77..a1edc52 100644
--- a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
+++ b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
@@ -57,11 +57,7 @@
@Test
public void testLogSessionId() {
- // TODO: Replace with Build.VERSION.SDK_INT >= Build.VERSION_CODES.S once possible.
- assumeTrue(
- Build.VERSION.SDK_INT == Build.VERSION_CODES.R
- && ("S".equals(Build.VERSION.CODENAME)
- || "T".equals(Build.VERSION.CODENAME)));
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S);
MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
assertThat(mediaParser.getLogSessionId())
.isSameInstanceAs(LogSessionId.LOG_SESSION_ID_NONE);
diff --git a/tests/tests/os/assets/platform_versions.txt b/tests/tests/os/assets/platform_versions.txt
index 3762249..48082f7 100644
--- a/tests/tests/os/assets/platform_versions.txt
+++ b/tests/tests/os/assets/platform_versions.txt
@@ -1 +1 @@
-S
+12
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 3dfc0d9..9c2c70e 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -35,13 +35,13 @@
import android.support.test.uiautomator.UiObject2
import android.support.test.uiautomator.UiObjectNotFoundException
import android.view.accessibility.AccessibilityNodeInfo
-import android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
import android.widget.Switch
import androidx.core.os.BuildCompat
import androidx.test.InstrumentationRegistry
import androidx.test.filters.SdkSuppress
import androidx.test.runner.AndroidJUnit4
import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
import com.android.compatibility.common.util.MatcherUtils.hasTextThat
import com.android.compatibility.common.util.SystemUtil
import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
@@ -52,8 +52,6 @@
import com.android.compatibility.common.util.UI_ROOT
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.textAsString
import com.android.compatibility.common.util.uiDump
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.containsStringIgnoringCase
@@ -103,6 +101,9 @@
@get:Rule
val disableAnimationRule = DisableAnimationRule()
+ @get:Rule
+ val freezeRotationRule = FreezeRotationRule()
+
@Before
fun setup() {
// Collapse notifications
@@ -135,6 +136,9 @@
@AppModeFull(reason = "Uses separate apps for testing")
@Test
fun testUnusedApp_getsPermissionRevoked() {
+ assumeFalse(
+ "Watch doesn't provide a unified way to check notifications. it depends on UX",
+ hasFeatureWatch())
withUnusedThresholdMs(3L) {
withDummyApp {
// Setup
@@ -275,12 +279,12 @@
waitFindObject(byTextIgnoreCase("Request allowlist")).click()
waitFindObject(byTextIgnoreCase("Permissions")).click()
val autoRevokeEnabledToggle = getAllowlistToggle()
- assertTrue(autoRevokeEnabledToggle.isChecked)
+ assertTrue(autoRevokeEnabledToggle.isChecked())
// Grant allowlist
autoRevokeEnabledToggle.click()
eventually {
- assertFalse(getAllowlistToggle().isChecked)
+ assertFalse(getAllowlistToggle().isChecked())
}
// Run
@@ -515,9 +519,14 @@
containsString(state.toString()))
}
- private fun getAllowlistToggle(): AccessibilityNodeInfo {
+ private fun getAllowlistToggle(): UiObject2 {
waitForIdle()
- return waitFindSwitch("Remove permissions if app isn’t used")
+ val parent = waitFindObject(
+ By.clickable(true)
+ .hasDescendant(By.text("Remove permissions if app isn’t used"))
+ .hasDescendant(By.clazz(Switch::class.java.name))
+ )
+ return parent.findObject(By.clazz(Switch::class.java.name))
}
private fun waitForIdle() {
@@ -543,21 +552,6 @@
return constToString<PackageManager>("PERMISSION_", state)
}
-fun waitFindSwitch(label: String): AccessibilityNodeInfo {
- return getEventually {
- val ui = UI_ROOT
- val node = ui.lowestCommonAncestor(
- { node -> node.textAsString == label },
- { node -> node.className == Switch::class.java.name })
- if (node == null) {
- ui.depthFirstSearch { it.isScrollable }?.performAction(ACTION_SCROLL_FORWARD)
- }
- return@getEventually node.assertNotNull {
- "Switch not found: $label in\n${uiDump(ui)}"
- }.depthFirstSearch { node -> node.className == Switch::class.java.name }!!
- }
-}
-
/**
* For some reason waitFindObject sometimes fails to find UI that is present in the view hierarchy
*/
diff --git a/tests/tests/permission/src/android/permission/cts/NfcPermissionTest.java b/tests/tests/permission/src/android/permission/cts/NfcPermissionTest.java
index b9501b9..c2eae01 100644
--- a/tests/tests/permission/src/android/permission/cts/NfcPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NfcPermissionTest.java
@@ -16,16 +16,12 @@
package android.permission.cts;
-import static android.Manifest.permission.NFC_SET_CONTROLLER_ALWAYS_ON;
-
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.ControllerAlwaysOnListener;
-import android.os.Process;
import android.platform.test.annotations.AppModeFull;
import androidx.test.InstrumentationRegistry;
@@ -35,10 +31,7 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
@RunWith(JUnit4.class)
public final class NfcPermissionTest {
@@ -57,36 +50,6 @@
}
/**
- * Verifies that there's only one dedicated app holds the NfcSetControllerAlwaysOnPermission.
- */
- @Test
- public void testNfcSetControllerAlwaysOnPermission() {
- PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
-
- List<Integer> specialUids = Arrays.asList(Process.SYSTEM_UID, Process.NFC_UID);
-
- List<PackageInfo> holding = pm.getPackagesHoldingPermissions(
- new String[] { NFC_SET_CONTROLLER_ALWAYS_ON },
- PackageManager.MATCH_DISABLED_COMPONENTS);
-
- List<Integer> nonSpecialPackages = holding.stream()
- .map(pi -> {
- try {
- return pm.getPackageUid(pi.packageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- return Process.INVALID_UID;
- }
- })
- .filter(uid -> !specialUids.contains(uid))
- .collect(Collectors.toList());
-
- if (holding.size() > 1) {
- fail("Only one app on the device is allowed to hold the "
- + "NFC_SET_CONTROLLER_ALWAYS_ON permission.");
- }
- }
-
- /**
* Verifies that isControllerAlwaysOnSupported() requires Permission.
* <p>
* Requires Permission: {@link android.Manifest.permission#NFC_SET_CONTROLLER_ALWAYS_ON}.
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 1ed08cc..bbb02de 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -1408,13 +1408,13 @@
<!-- @SystemApi Required in addition to android.permission.CAMERA to be able to access
system only camera devices.
- <p>Protection level: system|signature
+ <p>Protection level: system|signature|role
@hide -->
<permission android:name="android.permission.SYSTEM_CAMERA"
android:permissionGroup="android.permission-group.UNDEFINED"
android:label="@string/permlab_systemCamera"
android:description="@string/permdesc_systemCamera"
- android:protectionLevel="system|signature" />
+ android:protectionLevel="system|signature|role" />
<!-- @SystemApi Allows receiving the camera service notifications when a camera is opened
(by a certain application package) or closed.
diff --git a/tests/tests/permission3/UsePermissionAppWithOverlay/res/drawable/border.xml b/tests/tests/permission3/UsePermissionAppWithOverlay/res/drawable/border.xml
index 3c69373..1e637be 100644
--- a/tests/tests/permission3/UsePermissionAppWithOverlay/res/drawable/border.xml
+++ b/tests/tests/permission3/UsePermissionAppWithOverlay/res/drawable/border.xml
@@ -16,6 +16,7 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
+ <solid android:color="#FFFFFF" />
<corners
android:radius="4dp"/>
<stroke
diff --git a/tests/tests/permission3/UsePermissionAppWithOverlay/res/layout/overlay_activity.xml b/tests/tests/permission3/UsePermissionAppWithOverlay/res/layout/overlay_activity.xml
index 96b4df7..ea355f7 100644
--- a/tests/tests/permission3/UsePermissionAppWithOverlay/res/layout/overlay_activity.xml
+++ b/tests/tests/permission3/UsePermissionAppWithOverlay/res/layout/overlay_activity.xml
@@ -15,6 +15,7 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/overlay"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt
index 89bb1da..2f0b9fc 100644
--- a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt
+++ b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/OverlayActivity.kt
@@ -6,6 +6,7 @@
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
+import android.view.Gravity
import android.view.WindowManager
class OverlayActivity : Activity() {
@@ -19,6 +20,18 @@
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+ if (!intent.getBooleanExtra(EXTRA_FULL_OVERLAY, true)) {
+ params.gravity = Gravity.LEFT or Gravity.TOP
+ val left = intent.getIntExtra(DIALOG_LEFT, params.x)
+ val top = intent.getIntExtra(DIALOG_TOP, params.y)
+ val right = intent.getIntExtra(DIALOG_RIGHT, params.x + params.width)
+ val bottom = intent.getIntExtra(MESSAGE_BOTTOM, top + 1)
+ params.x = left
+ params.y = top
+ params.width = right - left
+ params.height = bottom - top
+ }
+
registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != RequestPermissionsActivity.ACTION_HIDE_OVERLAY) {
@@ -29,4 +42,13 @@
}
}, IntentFilter(RequestPermissionsActivity.ACTION_HIDE_OVERLAY))
}
+
+ companion object {
+ const val EXTRA_FULL_OVERLAY = "android.permission3.cts.usepermission.extra.FULL_OVERLAY"
+
+ const val DIALOG_LEFT = "android.permission3.cts.usepermission.extra.DIALOG_LEFT"
+ const val DIALOG_TOP = "android.permission3.cts.usepermission.extra.DIALOG_TOP"
+ const val DIALOG_RIGHT = "android.permission3.cts.usepermission.extra.DIALOG_RIGHT"
+ const val MESSAGE_BOTTOM = "android.permission3.cts.usepermission.extra.MESSAGE_BOTTOM"
+ }
}
\ No newline at end of file
diff --git a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
index 54155f6..c8ac47e 100644
--- a/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
+++ b/tests/tests/permission3/UsePermissionAppWithOverlay/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
@@ -18,6 +18,7 @@
import android.app.Activity
import android.content.BroadcastReceiver
+import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
@@ -36,7 +37,9 @@
return
}
- startActivity(Intent(context, OverlayActivity::class.java)
+ startActivity(intent
+ .setAction(null)
+ .setComponent(ComponentName(context!!, OverlayActivity::class.java))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
}
}, IntentFilter(ACTION_SHOW_OVERLAY))
diff --git a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
index b74d502..546dfb9 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt
@@ -30,6 +30,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import com.android.compatibility.common.util.DisableAnimationRule
+import com.android.compatibility.common.util.FreezeRotationRule
import com.android.compatibility.common.util.SystemUtil.runShellCommand
import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
import com.android.compatibility.common.util.UiAutomatorUtils
@@ -62,6 +63,9 @@
val disableAnimationRule = DisableAnimationRule()
@get:Rule
+ val freezeRotationRule = FreezeRotationRule()
+
+ @get:Rule
val activityRule = ActivityTestRule(StartForFutureActivity::class.java, false, false)
private var screenTimeoutBeforeTest: Long = 0L
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
index 5360f1d..9f06c2a 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt
@@ -19,6 +19,7 @@
import android.Manifest.permission.ACCESS_FINE_LOCATION
import android.content.Intent
import android.content.pm.PackageManager
+import android.graphics.Point
import android.support.test.uiautomator.By
import com.android.compatibility.common.util.SystemUtil
import org.junit.Assume.assumeFalse
@@ -36,7 +37,7 @@
}
@Test
- fun testTapjackGrantDialog() {
+ fun testTapjackGrantDialog_fullOverlay() {
// PermissionController for television uses a floating window.
assumeFalse(isTv)
@@ -47,9 +48,41 @@
getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT))).visibleCenter
// Wait for overlay to hide the dialog
- context.sendBroadcast(Intent(ACTION_SHOW_OVERLAY))
- waitFindObject(By.res("android.permission3.cts.usepermission:id/overlay_description"))
+ context.sendBroadcast(Intent(ACTION_SHOW_OVERLAY)
+ .putExtra(EXTRA_FULL_OVERLAY, true))
+ waitFindObject(By.res("android.permission3.cts.usepermission:id/overlay"))
+ tryClicking(buttonCenter)
+ }
+
+ @Test
+ fun testTapjackGrantDialog_partialOverlay() {
+ // PermissionController for television uses a floating window.
+ assumeFalse(isTv)
+
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {}
+
+ val buttonCenter = waitFindObject(By.text(
+ getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT))).visibleCenter
+ val dialogBounds = waitFindObject(By.res(
+ "com.android.permissioncontroller", "grant_dialog")).visibleBounds
+ val messageBottom = waitFindObject(By.res(
+ "com.android.permissioncontroller", "permission_message")).visibleBounds.bottom
+
+ // Wait for overlay to hide the dialog
+ context.sendBroadcast(Intent(ACTION_SHOW_OVERLAY)
+ .putExtra(EXTRA_FULL_OVERLAY, false)
+ .putExtra(DIALOG_LEFT, dialogBounds.left)
+ .putExtra(DIALOG_TOP, dialogBounds.top)
+ .putExtra(DIALOG_RIGHT, dialogBounds.right)
+ .putExtra(MESSAGE_BOTTOM, messageBottom))
+ waitFindObject(By.res("android.permission3.cts.usepermission:id/overlay"))
+
+ tryClicking(buttonCenter)
+ }
+
+ private fun tryClicking(buttonCenter: Point) {
try {
// Try to grant the permission, this should fail
SystemUtil.eventually({
@@ -84,5 +117,12 @@
companion object {
const val ACTION_SHOW_OVERLAY = "android.permission3.cts.usepermission.ACTION_SHOW_OVERLAY"
const val ACTION_HIDE_OVERLAY = "android.permission3.cts.usepermission.ACTION_HIDE_OVERLAY"
+
+ const val EXTRA_FULL_OVERLAY = "android.permission3.cts.usepermission.extra.FULL_OVERLAY"
+
+ const val DIALOG_LEFT = "android.permission3.cts.usepermission.extra.DIALOG_LEFT"
+ const val DIALOG_TOP = "android.permission3.cts.usepermission.extra.DIALOG_TOP"
+ const val DIALOG_RIGHT = "android.permission3.cts.usepermission.extra.DIALOG_RIGHT"
+ const val MESSAGE_BOTTOM = "android.permission3.cts.usepermission.extra.MESSAGE_BOTTOM"
}
}
diff --git a/tests/tests/permission5/src/android/permission5/cts/RenouncedPermissionsTest.kt b/tests/tests/permission5/src/android/permission5/cts/RenouncedPermissionsTest.kt
index 98f73af..25a73fe 100644
--- a/tests/tests/permission5/src/android/permission5/cts/RenouncedPermissionsTest.kt
+++ b/tests/tests/permission5/src/android/permission5/cts/RenouncedPermissionsTest.kt
@@ -23,6 +23,7 @@
import android.content.ContextParams
import android.content.pm.PackageManager
import android.os.Process
+import android.os.UserHandle
import android.permission.PermissionManager
import android.platform.test.annotations.AppModeFull
import android.provider.CalendarContract
@@ -122,7 +123,11 @@
val renouncedPermissionsSet = ArraySet<String>();
renouncedPermissionsSet.add(Manifest.permission.READ_CONTACTS)
renouncedPermissionsSet.add(Manifest.permission.READ_CALENDAR)
- val shellAttributionSource = AttributionSource.Builder(Process.SHELL_UID)
+
+ // Calculate the shellUid to account for running this from a secondary user.
+ val shellUid = UserHandle.getUid(Process.myUserHandle().identifier,
+ UserHandle.getAppId(Process.SHELL_UID))
+ val shellAttributionSource = AttributionSource.Builder(shellUid)
.setPackageName("com.android.shell")
.setRenouncedPermissions(renouncedPermissionsSet)
.build()
diff --git a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
index c9a78da..8d65db7 100644
--- a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
+++ b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
@@ -30,6 +30,7 @@
import android.os.Process
import android.os.RemoteCallback
import android.os.SystemClock
+import android.os.UserHandle
import android.platform.test.annotations.AppModeFull
import android.provider.CalendarContract
import android.provider.CallLog
@@ -335,15 +336,18 @@
}
val endTimeMillis = System.currentTimeMillis()
+ // Calculate the shellUid to account for running this from a secondary user.
+ val shellUid = UserHandle.getUid(Process.myUserHandle().identifier,
+ UserHandle.getAppId(Process.SHELL_UID))
// Since we use adopt the shell permission identity we need to adjust
// the permission identity to have the shell as the accessor.
assertNotRunningOpAccess(AppOpsManager.permissionToOp(permission)!!,
- beginEndMillis, endTimeMillis, AttributionSource(Process.SHELL_UID,
+ beginEndMillis, endTimeMillis, AttributionSource(shellUid,
SHELL_PACKAGE_NAME, context.attributionTag, null,
context.attributionSource.next),
/*accessorForeground*/ false, /*receiverForeground*/ false,
/*accessorTrusted*/ true, /*accessorAccessCount*/ 1,
- /*receiverAccessCount*/ 1, /*checkAccessor*/ false,
+ /*receiverAccessCount*/ 1, /*checkAccessor*/ false,
/*fromDatasource*/ false)
}
diff --git a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
index 17fec42..766a927 100644
--- a/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
+++ b/tests/tests/role/src/android/app/role/cts/RoleManagerTest.java
@@ -56,6 +56,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.DisableAnimationRule;
+import com.android.compatibility.common.util.FreezeRotationRule;
import com.android.compatibility.common.util.TestUtils;
import com.android.compatibility.common.util.ThrowingRunnable;
import com.android.compatibility.common.util.UiAutomatorUtils;
@@ -123,6 +124,9 @@
public DisableAnimationRule mDisableAnimationRule = new DisableAnimationRule();
@Rule
+ public FreezeRotationRule mFreezeRotationRule = new FreezeRotationRule();
+
+ @Rule
public ActivityTestRule<WaitForResultActivity> mActivityRule =
new ActivityTestRule<>(WaitForResultActivity.class);
diff --git a/tests/tests/security/src/android/security/cts/SQLiteTest.java b/tests/tests/security/src/android/security/cts/SQLiteTest.java
index 47407ca..55e731d 100644
--- a/tests/tests/security/src/android/security/cts/SQLiteTest.java
+++ b/tests/tests/security/src/android/security/cts/SQLiteTest.java
@@ -16,30 +16,47 @@
package android.security.cts;
-import android.content.Context;
+import static org.junit.Assert.*;
+
+import android.app.UiAutomation;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.platform.test.annotations.SecurityTest;
import android.provider.VoicemailContract;
import android.test.AndroidTestCase;
+
import androidx.test.InstrumentationRegistry;
-import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.FileInputStream;
@SecurityTest
public class SQLiteTest extends AndroidTestCase {
+ private static final String DATABASE_FILE_NAME = "database_test.db";
private ContentResolver mResolver;
private String mPackageName;
private Context mContext;
+ private SQLiteDatabase mDatabase;
+
@Override
protected void setUp() throws Exception {
super.setUp();
mResolver = getContext().getContentResolver();
mContext = InstrumentationRegistry.getTargetContext();
mPackageName = mContext.getPackageName();
+
+ mContext.deleteDatabase(DATABASE_FILE_NAME);
+ File databaseFile = getContext().getDatabasePath(DATABASE_FILE_NAME);
+ databaseFile.getParentFile().mkdirs(); // directory may not exist
+ mDatabase = SQLiteDatabase.openOrCreateDatabase(databaseFile, null);
+ assertNotNull(mDatabase);
}
/**
@@ -78,4 +95,15 @@
// do nothing
}
}
+
+ /**
+ * b/153352319
+ */
+ @SecurityTest(minPatchLevel = "2021-06")
+ public void test_android_float_to_text_conversion_overflow() {
+ String create_cmd = "select (printf('%.2147483647G',0.01));";
+ try (Cursor c = mDatabase.rawQuery(create_cmd, null)) {
+ assertEquals(c.getCount(), 1);
+ }
+ }
}
diff --git a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyMicrophoneTest.kt b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyMicrophoneTest.kt
index edb7c9a..290f5be 100644
--- a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyMicrophoneTest.kt
+++ b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyMicrophoneTest.kt
@@ -23,12 +23,14 @@
import android.telecom.TelecomManager
import com.android.compatibility.common.util.SystemUtil
import org.junit.Assume
+import org.junit.Ignore
import org.junit.Test
class SensorPrivacyMicrophoneTest : SensorPrivacyBaseTest(
MICROPHONE,
USE_MIC_EXTRA
) {
+ @Ignore
@Test
fun testMicShownOnPhoneCall() {
try {
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index 2716146..d63f8ef 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -219,6 +219,12 @@
.setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
.addSupportedUriScheme(TEST_URI_SCHEME)
.build();
+ public static final Bundle SELF_MANAGED_ACCOUNT_1_EXTRAS;
+ static {
+ SELF_MANAGED_ACCOUNT_1_EXTRAS = new Bundle();
+ SELF_MANAGED_ACCOUNT_1_EXTRAS.putBoolean(
+ PhoneAccount.EXTRA_ADD_SELF_MANAGED_CALLS_TO_INCALLSERVICE, false);
+ }
public static final Bundle SELF_MANAGED_ACCOUNT_2_EXTRAS;
static {
SELF_MANAGED_ACCOUNT_2_EXTRAS = new Bundle();
@@ -255,6 +261,7 @@
.setShortDescription(SELF_MANAGED_ACCOUNT_LABEL)
.addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
.addSupportedUriScheme(PhoneAccount.SCHEME_SIP)
+ .setExtras(SELF_MANAGED_ACCOUNT_1_EXTRAS)
.build();
public static final PhoneAccount TEST_SELF_MANAGED_PHONE_ACCOUNT_4 = PhoneAccount.builder(
TEST_SELF_MANAGED_HANDLE_4, SELF_MANAGED_ACCOUNT_LABEL)
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index 651a742..7b48fb5 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -574,7 +574,8 @@
assertTrue("getPci() out of range [0, 1007], pci = " + pci, 0 <= pci && pci <= 1007);
int tac = nr.getTac();
- assertTrue("getTac() out of range [0, 16777215], tac = " + tac, 0 <= tac && tac <= 16777215);
+ assertTrue("getTac() out of range [0, 16777215], tac = " + tac,
+ (tac == Integer.MAX_VALUE) || (0 <= tac && tac <= 16777215));
int nrArfcn = nr.getNrarfcn();
assertTrue("getNrarfcn() out of range [0, 3279165], nrarfcn = " + nrArfcn,
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index 33709f7..a1a6c0e 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -608,7 +608,15 @@
assertEquals(1, infoList.size());
assertEquals(uuid, infoList.get(0).getGroupUuid());
- List<SubscriptionInfo> availableInfoList = mSm.getAvailableSubscriptionInfoList();
+ List<SubscriptionInfo> availableInfoList;
+ try {
+ mSm.getAvailableSubscriptionInfoList();
+ fail("SecurityException should be thrown without READ_PRIVILEGED_PHONE_STATE");
+ } catch (SecurityException ex) {
+ // Ignore
+ }
+ availableInfoList = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
+ (sm) -> sm.getAvailableSubscriptionInfoList());
if (availableInfoList.size() > 1) {
List<Integer> availableSubGroup = availableInfoList.stream()
.map(info -> info.getSubscriptionId())
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
index 372301a..9a1a356 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/RcsUceAdapterTest.java
@@ -2334,6 +2334,168 @@
overrideCarrierConfig(null);
}
+ @Test
+ public void testRequestCapabilitiesWithUriFormatChanged() throws Exception {
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+ ImsManager imsManager = getContext().getSystemService(ImsManager.class);
+ RcsUceAdapter uceAdapter = imsManager.getImsRcsManager(sTestSub).getUceAdapter();
+ assertNotNull("UCE adapter should not be null!", uceAdapter);
+
+ // Remove the test contact capabilities
+ removeTestContactFromEab();
+
+ // Connect to the ImsService
+ setupTestImsService(uceAdapter, true, true /* presence cap */, false /* OPTIONS */);
+
+ TestRcsCapabilityExchangeImpl capabilityExchangeImpl = sServiceConnector
+ .getCarrierService().getRcsFeature().getRcsCapabilityExchangeImpl();
+
+ BlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
+ BlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
+ BlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
+ BlockingQueue<RcsContactUceCapability> capabilityQueue = new LinkedBlockingQueue<>();
+ RcsUceAdapter.CapabilitiesCallback callback = new RcsUceAdapter.CapabilitiesCallback() {
+ @Override
+ public void onCapabilitiesReceived(List<RcsContactUceCapability> capabilities) {
+ capabilities.forEach(c -> capabilityQueue.offer(c));
+ }
+ @Override
+ public void onComplete() {
+ completeQueue.offer(true);
+ }
+ @Override
+ public void onError(int errorCode, long retryAfterMilliseconds) {
+ errorQueue.offer(errorCode);
+ errorRetryQueue.offer(retryAfterMilliseconds);
+ }
+ };
+
+ // Prepare three contacts
+ final Uri contact1TelScheme = sTestNumberUri;
+ final Uri contact1SipScheme = Uri.fromParts(PhoneAccount.SCHEME_SIP,
+ sTestPhoneNumber + "@test.cts;user=phone", null);
+ final Uri contact2 = sTestContact2Uri;
+ final Uri contact3 = sTestContact3Uri;
+
+ Collection<Uri> contacts = new ArrayList<>(3);
+ // The first contact is using the tel scheme
+ contacts.add(contact1TelScheme);
+ contacts.add(contact2);
+ contacts.add(contact3);
+
+ ArrayList<String> pidfXmlList = new ArrayList<>(3);
+ // ImsService replies the pidf xml data with the SIP scheme
+ pidfXmlList.add(getPidfXmlData(contact1SipScheme, true, true));
+ pidfXmlList.add(getPidfXmlData(contact2, true, false));
+ pidfXmlList.add(getPidfXmlData(contact3, false, false));
+
+ // Setup the network response is 200 OK and notify capabilities update
+ int networkRespCode = 200;
+ String networkRespReason = "OK";
+ capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+ cb.onNetworkResponse(networkRespCode, networkRespReason);
+ cb.onNotifyCapabilitiesUpdate(pidfXmlList);
+ cb.onTerminated("", 0L);
+ });
+
+ requestCapabilities(uceAdapter, contacts, callback);
+
+ // Verify that all the three contact's capabilities are received
+ RcsContactUceCapability capability = waitForResult(capabilityQueue);
+ assertNotNull("Capabilities were not received for contact: " + contact1SipScheme,
+ capability);
+ verifyCapabilityResult(capability, contact1SipScheme, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_FOUND, true, true);
+
+ capability = waitForResult(capabilityQueue);
+ assertNotNull("Capabilities were not received for contact: " + contact2, capability);
+ verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+ true, false);
+
+ capability = waitForResult(capabilityQueue);
+ assertNotNull("Capabilities were not received for contact: " + contact3, capability);
+ verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_FOUND,
+ false, false);
+
+ // Verify the onCompleted is called
+ waitForResult(completeQueue);
+
+ errorQueue.clear();
+ errorRetryQueue.clear();
+ completeQueue.clear();
+ capabilityQueue.clear();
+ removeTestContactFromEab();
+
+ // Setup the callback that some of the contacts are terminated.
+ capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+ cb.onNetworkResponse(404, "NOT FOUND");
+ });
+
+ requestCapabilities(uceAdapter, contacts, callback);
+
+ // Verify the contacts are not found.
+ capability = waitForResult(capabilityQueue);
+ verifyCapabilityResult(capability, contact1TelScheme, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_NOT_FOUND, false, false);
+
+ capability = waitForResult(capabilityQueue);
+ verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+ false, false);
+
+ capability = waitForResult(capabilityQueue);
+ verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+ false, false);
+
+ int errorCode = waitForResult(errorQueue);
+ assertEquals(RcsUceAdapter.ERROR_NOT_FOUND, errorCode);
+
+ errorQueue.clear();
+ errorRetryQueue.clear();
+ completeQueue.clear();
+ capabilityQueue.clear();
+ removeTestContactFromEab();
+
+ // Setup the callback that some of the contacts are terminated.
+ capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
+ List<Uri> uriList = new ArrayList(uris);
+ cb.onNetworkResponse(networkRespCode, networkRespReason);
+ // Notify capabilities updated for the first contact
+ String pidfXml = pidfXmlList.get(0);
+ cb.onNotifyCapabilitiesUpdate(Collections.singletonList(pidfXml));
+
+ List<Pair<Uri, String>> terminatedResources = new ArrayList<>();
+ for (int i = 1; i < uriList.size(); i++) {
+ Pair<Uri, String> pair = Pair.create(uriList.get(i), "noresource");
+ terminatedResources.add(pair);
+ }
+ cb.onResourceTerminated(terminatedResources);
+ cb.onTerminated("", 0L);
+ });
+
+ requestCapabilities(uceAdapter, contacts, callback);
+
+ // Verify the first contact is found.
+ capability = waitForResult(capabilityQueue);
+ verifyCapabilityResult(capability, contact1SipScheme, SOURCE_TYPE_NETWORK,
+ REQUEST_RESULT_FOUND, true, true);
+
+ // Verify the reset contacts are not found.
+ capability = waitForResult(capabilityQueue);
+ verifyCapabilityResult(capability, contact2, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+ true, false);
+
+ capability = waitForResult(capabilityQueue);
+ verifyCapabilityResult(capability, contact3, SOURCE_TYPE_NETWORK, REQUEST_RESULT_NOT_FOUND,
+ false, false);
+
+ // Verify the onCompleted is called
+ waitForResult(completeQueue);
+
+ overrideCarrierConfig(null);
+ }
+
private void setupTestImsService(RcsUceAdapter uceAdapter, boolean presencePublishEnabled,
boolean presenceCapExchangeEnabled, boolean sipOptionsEnabled) throws Exception {
// Trigger carrier config changed
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
index 2e57cc2..657a0e9 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
@@ -182,6 +182,14 @@
TunedInfo.APP_TAG_SELF);
assertEquals(expectedInfo, returnedInfo);
+ assertEquals(expectedInfo.getAppTag(), returnedInfo.getAppTag());
+ assertEquals(expectedInfo.getAppType(), returnedInfo.getAppType());
+ assertEquals(expectedInfo.getChannelUri(), returnedInfo.getChannelUri());
+ assertEquals(expectedInfo.getInputId(), returnedInfo.getInputId());
+ assertEquals(expectedInfo.isMainSession(), returnedInfo.isMainSession());
+ assertEquals(expectedInfo.isRecordingSession(), returnedInfo.isRecordingSession());
+ assertEquals(expectedInfo.isVisible(), returnedInfo.isVisible());
+
assertEquals(1, mCallback.mTunedInfos.size());
TunedInfo callbackInfo = mCallback.mTunedInfos.get(0);
assertEquals(expectedInfo, callbackInfo);
@@ -456,6 +464,7 @@
@Override
public synchronized void onCurrentTunedInfosUpdated(
List<TunedInfo> tunedInfos) {
+ super.onCurrentTunedInfosUpdated(tunedInfos);
mTunedInfos = tunedInfos;
}
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 62845b7..02f38cd 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -440,6 +440,13 @@
</intent-filter>
</activity>
+ <activity android:name="android.view.cts.ScrollCaptureScrollViewCtsActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ </intent-filter>
+ </activity>
+
<service android:name="android.view.textclassifier.cts.CtsTextClassifierService"
android:exported="true"
android:permission="android.permission.BIND_TEXTCLASSIFIER_SERVICE">
diff --git a/tests/tests/view/src/android/view/cts/MockScrollCaptureCallback.java b/tests/tests/view/src/android/view/cts/MockScrollCaptureCallback.java
new file mode 100644
index 0000000..705908d
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/MockScrollCaptureCallback.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+import android.view.ScrollCaptureCallback;
+import android.view.ScrollCaptureSession;
+
+import androidx.annotation.NonNull;
+
+import java.util.function.Consumer;
+
+public class MockScrollCaptureCallback implements ScrollCaptureCallback {
+ private Consumer<Rect> mSearchConsumer;
+ private Runnable mStartOnReady;
+ private Consumer<Rect> mImageOnComplete;
+ private Runnable mOnEndReady;
+ private int mModCount;
+
+ @Override
+ public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+ @NonNull Consumer<Rect> onReady) {
+ mSearchConsumer = onReady;
+ mModCount++;
+ }
+
+ @Override
+ public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+ @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+ mStartOnReady = onReady;
+ mModCount++;
+ }
+
+ @Override
+ public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+ @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+ @NonNull Consumer<Rect> onComplete) {
+ mImageOnComplete = onComplete;
+ mModCount++;
+ }
+
+ @Override
+ public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+ mOnEndReady = onReady;
+ }
+
+ void completeSearchRequest(Rect scrollBounds) {
+ assertNotNull("Did not receive search request", mSearchConsumer);
+ mSearchConsumer.accept(scrollBounds);
+ mModCount++;
+ }
+
+ void verifyZeroInteractions() {
+ assertEquals("Expected zero interactions", 0, mModCount);
+ }
+
+ void completeStartRequest() {
+ assertNotNull("Did not receive start request", mStartOnReady);
+ mStartOnReady.run();
+ }
+
+ void completeImageRequest(Rect captured) {
+ assertNotNull("Did not receive image request", mImageOnComplete);
+ mImageOnComplete.accept(captured);
+ }
+
+ void completeEndRequest() {
+ assertNotNull("Did not receive end request", mOnEndReady);
+ mOnEndReady.run();
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/OWNERS b/tests/tests/view/src/android/view/cts/OWNERS
index 3da545b..1e360f7 100644
--- a/tests/tests/view/src/android/view/cts/OWNERS
+++ b/tests/tests/view/src/android/view/cts/OWNERS
@@ -1,2 +1,4 @@
per-file ASurfaceControlTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
per-file SurfaceViewSyncTest.java = file:platform/frameworks/base:/services/core/java/com/android/server/wm/OWNERS
+
+per-file *ScrollCapture*.java = file:platform/frameworks/base:/packages/SystemUI/src/com/android/systemui/screenshot/OWNERS
diff --git a/tests/tests/view/src/android/view/cts/ScrollCaptureScrollViewCtsActivity.java b/tests/tests/view/src/android/view/cts/ScrollCaptureScrollViewCtsActivity.java
new file mode 100644
index 0000000..6d39468
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ScrollCaptureScrollViewCtsActivity.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.TypedValue;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+public class ScrollCaptureScrollViewCtsActivity extends Activity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ScrollView scrollView = new ScrollView(this);
+ LinearLayout linearLayout = new LinearLayout(this);
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ TextView text = new TextView(this);
+ text.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 40);
+ text.setText(R.string.long_text);
+ linearLayout.addView(text);
+ scrollView.addView(linearLayout);
+ setContentView(scrollView);
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/ScrollCaptureSessionTest.java b/tests/tests/view/src/android/view/cts/ScrollCaptureSessionTest.java
new file mode 100644
index 0000000..0207ae1
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ScrollCaptureSessionTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import static org.junit.Assert.assertEquals;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.ScrollCaptureSession;
+import android.view.Surface;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+/**
+ * Tests of {@link ScrollCaptureSession}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ScrollCaptureSessionTest {
+
+ private Surface mSurface;
+
+ @Before
+ public void setUp() {
+ mSurface = Mockito.mock(Surface.class);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testConstructor_requiresNonNullSurface() {
+ new ScrollCaptureSession(null, new Rect(1, 2, 3, 4), new Point(5, 6));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testConstructor_requiresNonNullBounds() {
+ new ScrollCaptureSession(mSurface, null, new Point(5, 6));
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testConstructor_requiresNonNullPosition() {
+ new ScrollCaptureSession(mSurface, new Rect(1, 2, 3, 4), null);
+ }
+
+ @Test
+ public void testGetSurface() {
+ ScrollCaptureSession session = new ScrollCaptureSession(mSurface,
+ new Rect(1, 2, 3, 4), new Point(5, 6));
+ assertEquals(mSurface, session.getSurface());
+ }
+
+ @Test
+ public void testGetScrollBounds() {
+ ScrollCaptureSession session = new ScrollCaptureSession(mSurface,
+ new Rect(1, 2, 3, 4), new Point(5, 6));
+ assertEquals(new Rect(1, 2, 3, 4), session.getScrollBounds());
+ }
+
+ @Test
+ public void testGetPositionInWindow() {
+ ScrollCaptureSession session = new ScrollCaptureSession(mSurface,
+ new Rect(1, 2, 3, 4), new Point(5, 6));
+ assertEquals(new Point(5, 6), session.getPositionInWindow());
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/ScrollCaptureTargetTest.java b/tests/tests/view/src/android/view/cts/ScrollCaptureTargetTest.java
new file mode 100644
index 0000000..ce2d558
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ScrollCaptureTargetTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.view.ScrollCaptureTarget;
+import android.view.View;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests of {@link ScrollCaptureTarget}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ScrollCaptureTargetTest {
+ @Rule
+ public ExpectedException mExpectedException = ExpectedException.none();
+
+ MockView mTargetView;
+ MockScrollCaptureCallback mCallback;
+
+ @Before
+ public void setUp() {
+ mTargetView = new MockView(InstrumentationRegistry.getInstrumentation().getTargetContext());
+ mCallback = new MockScrollCaptureCallback();
+ }
+
+ @Test
+ public void testConstructor_requiresNonNullTarget() {
+ mExpectedException.expect(NullPointerException.class);
+ new ScrollCaptureTarget(null, new Rect(1, 2, 3, 4), new Point(5, 6), mCallback);
+ }
+
+ @Test
+ public void testConstructor_requiresNonNullVisibleRect() {
+ mExpectedException.expect(NullPointerException.class);
+ new ScrollCaptureTarget(mTargetView, null, new Point(5, 6), mCallback);
+ }
+
+ @Test
+ public void testConstructor_requiresNonNullPosition() {
+ mExpectedException.expect(NullPointerException.class);
+ new ScrollCaptureTarget(mTargetView, new Rect(1, 2, 3, 4), null, mCallback);
+ }
+
+ @Test
+ public void testConstructor_requiresNonNullCallback() {
+ mExpectedException.expect(NullPointerException.class);
+ new ScrollCaptureTarget(mTargetView, new Rect(1, 2, 3, 4), new Point(5, 6), null);
+ }
+
+ @Test
+ public void testScrollBounds() {
+ mTargetView.setLeftTopRightBottom(0, 0, 10, 10);
+
+ ScrollCaptureTarget target = new ScrollCaptureTarget(mTargetView,
+ new Rect(0, 0, 10, 10), new Point(5, 5), mCallback);
+
+ // null until populated during search
+ assertNull(target.getScrollBounds());
+
+ // populated during scroll capture search
+ target.setScrollBounds(new Rect(-5, -5, 15, 15));
+ // value stored is the intersection of the provided value and view's size
+ assertEquals(new Rect(0, 0, 10, 10), target.getScrollBounds());
+
+ // empty rect is valid as opt-out
+ target.setScrollBounds(new Rect());
+ assertTrue("getScrollBounds() should return an empty rect",
+ target.getScrollBounds().isEmpty());
+
+ // as is null
+ target.setScrollBounds(null);
+ assertNull(target.getScrollBounds());
+ }
+
+ @Test
+ public void testContainingView() {
+ mTargetView.setLeftTopRightBottom(1, 2, 3, 4);
+
+ ScrollCaptureTarget target = new ScrollCaptureTarget(mTargetView,
+ new Rect(1, 2, 3, 4), new Point(5, 6), mCallback);
+ assertEquals(mTargetView, target.getContainingView());
+ }
+
+ @Test
+ public void testLocalVisibleRect() {
+ mTargetView.setLeftTopRightBottom(1, 2, 3, 4);
+
+ ScrollCaptureTarget target = new ScrollCaptureTarget(mTargetView,
+ new Rect(1, 2, 3, 4), new Point(5, 6), mCallback);
+ assertEquals(new Rect(1, 2, 3, 4), target.getLocalVisibleRect());
+ }
+
+ @Test
+ public void testPositionInWindow() {
+ mTargetView.setLeftTopRightBottom(1, 2, 3, 4);
+
+ ScrollCaptureTarget target = new ScrollCaptureTarget(mTargetView,
+ new Rect(1, 2, 3, 4), new Point(5, 6), mCallback);
+ assertEquals(new Point(5, 6), target.getPositionInWindow());
+
+ mTargetView.setLeftTopRightBottom(100, 100, 200, 200);
+
+ target.updatePositionInWindow();
+ assertEquals(new Point(100, 100), target.getPositionInWindow());
+ }
+
+ @Test
+ public void testCallback() {
+ mTargetView.setLeftTopRightBottom(1, 2, 3, 4);
+
+ ScrollCaptureTarget target = new ScrollCaptureTarget(mTargetView,
+ new Rect(1, 2, 3, 4), new Point(5, 6), mCallback);
+ assertEquals(mCallback, target.getCallback());
+ }
+
+ @Test
+ public void testHint() {
+ mTargetView.setScrollCaptureHint(View.SCROLL_CAPTURE_HINT_INCLUDE);
+ mTargetView.setLeftTopRightBottom(1, 2, 3, 4);
+
+ ScrollCaptureTarget target = new ScrollCaptureTarget(mTargetView,
+ new Rect(1, 2, 3, 4), new Point(5, 6), mCallback);
+ assertEquals(View.SCROLL_CAPTURE_HINT_INCLUDE, target.getHint());
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/ViewGroup_ScrollCaptureTest.java b/tests/tests/view/src/android/view/cts/ViewGroup_ScrollCaptureTest.java
new file mode 100644
index 0000000..e82eae8
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ViewGroup_ScrollCaptureTest.java
@@ -0,0 +1,502 @@
+/*
+ * 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;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.CancellationSignal;
+import android.platform.test.annotations.Presubmit;
+import android.view.ScrollCaptureCallback;
+import android.view.ScrollCaptureSession;
+import android.view.ScrollCaptureTarget;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.test.filters.MediumTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Exercises Scroll Capture (long screenshot) APIs in {@link ViewGroup}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(MockitoJUnitRunner.class)
+public class ViewGroup_ScrollCaptureTest {
+
+ private static class Receiver<T> implements Consumer<T> {
+ private final List<T> mValues = new ArrayList<>();
+
+ @Override
+ public void accept(T target) {
+ mValues.add(target);
+ }
+
+ public List<T> getAllValues() {
+ return mValues;
+ }
+
+ public T getValue() {
+ if (mValues.isEmpty()) {
+ throw new IllegalStateException("No values received");
+ }
+ return mValues.get(mValues.size() - 1);
+ }
+
+ public boolean hasValue() {
+ return !mValues.isEmpty();
+ }
+ }
+
+ /** Make sure the hint flags are saved and loaded correctly. */
+ @Test
+ public void testSetScrollCaptureHint() {
+ final Context context = getInstrumentation().getContext();
+ final MockViewGroup viewGroup = new MockViewGroup(context);
+
+ assertNotNull(viewGroup);
+ assertEquals("Default scroll capture hint flags should be [SCROLL_CAPTURE_HINT_AUTO]",
+ ViewGroup.SCROLL_CAPTURE_HINT_AUTO, viewGroup.getScrollCaptureHint());
+
+ viewGroup.setScrollCaptureHint(View.SCROLL_CAPTURE_HINT_INCLUDE);
+ assertEquals("The scroll capture hint was not stored correctly.",
+ ViewGroup.SCROLL_CAPTURE_HINT_INCLUDE, viewGroup.getScrollCaptureHint());
+
+ viewGroup.setScrollCaptureHint(ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE);
+ assertEquals("The scroll capture hint was not stored correctly.",
+ ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE, viewGroup.getScrollCaptureHint());
+
+ viewGroup.setScrollCaptureHint(ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS);
+ assertEquals("The scroll capture hint was not stored correctly.",
+ ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS,
+ viewGroup.getScrollCaptureHint());
+
+ viewGroup.setScrollCaptureHint(ViewGroup.SCROLL_CAPTURE_HINT_INCLUDE
+ | ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS);
+ assertEquals("The scroll capture hint was not stored correctly.",
+ ViewGroup.SCROLL_CAPTURE_HINT_INCLUDE
+ | ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS,
+ viewGroup.getScrollCaptureHint());
+
+ viewGroup.setScrollCaptureHint(ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE
+ | ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS);
+ assertEquals("The scroll capture hint was not stored correctly.",
+ ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE
+ | ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE_DESCENDANTS,
+ viewGroup.getScrollCaptureHint());
+ }
+
+ /** Make sure the hint flags are saved and loaded correctly. */
+ @Test
+ public void testSetScrollCaptureHint_mutuallyExclusiveFlags() {
+ final Context context = getInstrumentation().getContext();
+ final MockViewGroup viewGroup = new MockViewGroup(context);
+
+ viewGroup.setScrollCaptureHint(
+ View.SCROLL_CAPTURE_HINT_INCLUDE | View.SCROLL_CAPTURE_HINT_EXCLUDE);
+ assertEquals("Mutually exclusive flags were not resolved correctly",
+ ViewGroup.SCROLL_CAPTURE_HINT_EXCLUDE, viewGroup.getScrollCaptureHint());
+ }
+
+ /**
+ * No target is returned because MockViewGroup does not emulate a scrolling container.
+ */
+ @SmallTest
+ @Test
+ public void testDispatchScrollCaptureSearch() {
+ final Context context = getInstrumentation().getContext();
+ final MockViewGroup viewGroup =
+ new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_AUTO);
+
+ Rect localVisibleRect = new Rect(0, 0, 200, 200);
+ Point windowOffset = new Point();
+
+ Receiver<ScrollCaptureTarget> receiver = new Receiver<>();
+ viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, receiver);
+ assertFalse("No target was expected", receiver.hasValue());
+ }
+
+ /**
+ * Ensure that a ViewGroup with 'scrollCaptureHint=auto', and a scroll capture callback
+ * produces a correct target for that handler.
+ */
+ @MediumTest
+ @Test
+ public void testDispatchScrollCaptureSearch_withCallback() {
+ final Context context = getInstrumentation().getContext();
+ MockViewGroup viewGroup =
+ new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_AUTO);
+
+ MockScrollCaptureCallback callback = new MockScrollCaptureCallback();
+ viewGroup.setScrollCaptureCallback(callback);
+
+ Rect localVisibleRect = new Rect(0, 0, 200, 200);
+ Point windowOffset = new Point();
+
+ Receiver<ScrollCaptureTarget> receiver = new Receiver<>();
+ viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, receiver);
+ callOnScrollCaptureSearch(receiver);
+ callback.completeSearchRequest(new Rect(1, 2, 3, 4));
+ assertTrue("A target was expected", receiver.hasValue());
+
+ ScrollCaptureTarget target = receiver.getValue();
+ assertNotNull("Target not found", target);
+ assertSame("Target has the wrong callback", callback, target.getCallback());
+ assertEquals("Target has the wrong bounds", new Rect(1, 2, 3, 4), target.getScrollBounds());
+
+ assertSame("Target has the wrong View", viewGroup, target.getContainingView());
+ assertEquals("Target hint is incorrect", View.SCROLL_CAPTURE_HINT_AUTO,
+ target.getContainingView().getScrollCaptureHint());
+ }
+
+ /**
+ * Dispatch skips this view entirely due to the exclude hint, despite a callback being set.
+ * Exclude takes precedence.
+ */
+ @MediumTest
+ @Test
+ public void testDispatchScrollCaptureSearch_withCallback_hintExclude() {
+ final Context context = getInstrumentation().getContext();
+ final MockViewGroup viewGroup =
+ new MockViewGroup(context, 0, 0, 200, 200, View.SCROLL_CAPTURE_HINT_EXCLUDE);
+
+ MockScrollCaptureCallback callback = new MockScrollCaptureCallback();
+ viewGroup.setScrollCaptureCallback(callback);
+
+ Rect localVisibleRect = new Rect(0, 0, 200, 200);
+ Point windowOffset = new Point();
+
+ Receiver<ScrollCaptureTarget> receiver = new Receiver<>();
+ viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, receiver);
+ callback.verifyZeroInteractions();
+ assertFalse("No target expected.", receiver.hasValue());
+ }
+
+ private static boolean nullOrEmpty(Rect r) {
+ return r == null || r.isEmpty();
+ }
+
+ /**
+ * Test scroll capture search dispatch to child views.
+ * <p>
+ * Verifies computation of child visible bounds.
+ * TODO: with scrollX / scrollY, split up into discrete tests
+ */
+ @MediumTest
+ @Test
+ public void testDispatchScrollCaptureSearch_toChildren() throws Exception {
+ final Context context = getInstrumentation().getContext();
+ final MockViewGroup viewGroup = new MockViewGroup(context, 0, 0, 200, 200);
+
+ Rect localVisibleRect = new Rect(25, 50, 175, 150);
+ Point windowOffset = new Point(0, 0);
+
+ // visible area
+ // |<- l=25, |
+ // | r=175 ->|
+ // +--------------------------+
+ // | view1 (0, 0, 200, 25) |
+ // +---------------+----------+
+ // | | |
+ // | view2 | view4 | --+
+ // | (0, 25, | (inv) | | visible area
+ // | 150, 100)| | |
+ // +---------------+----------+ | t=50, b=150
+ // | view3 | view5 | |
+ // | (0, 100 |(150, 100 | --+
+ // | 200, 200) | 200, 200)|
+ // | | |
+ // | | |
+ // +---------------+----------+ (200,200)
+
+ // View 1 is fully clipped and not visible.
+ final MockView view1 = new MockView(context, 0, 0, 200, 25);
+ viewGroup.addView(view1);
+
+ // View 2 is partially visible. (75x75), but not scrollable
+ final MockView view2 = new MockView(context, 0, 25, 150, 100);
+ viewGroup.addView(view2);
+
+ // View 3 is partially visible (175x50)
+ // Pretend View3 can scroll by providing a callback to handle it here
+ MockScrollCaptureCallback view3Callback = new MockScrollCaptureCallback();
+ final MockView view3 = new MockView(context, 0, 100, 200, 200);
+ view3.setScrollCaptureCallback(view3Callback);
+ viewGroup.addView(view3);
+
+ // View 4 is invisible and should be ignored.
+ final MockView view4 = new MockView(context, 150, 25, 200, 100, View.INVISIBLE);
+ viewGroup.addView(view4);
+
+ MockScrollCaptureCallback view5Callback = new MockScrollCaptureCallback();
+
+ // View 5 is partially visible and explicitly included via flag. (25x50)
+ final MockView view5 = new MockView(context, 150, 100, 200, 200);
+ view5.setScrollCaptureCallback(view5Callback);
+ view5.setScrollCaptureHint(View.SCROLL_CAPTURE_HINT_INCLUDE);
+ viewGroup.addView(view5);
+
+ Receiver<ScrollCaptureTarget> receiver = new Receiver<>();
+
+ // Dispatch to the ViewGroup
+ viewGroup.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, receiver);
+ callOnScrollCaptureSearch(receiver);
+ view3Callback.completeSearchRequest(new Rect(0, 0, 200, 100));
+ view5Callback.completeSearchRequest(new Rect(0, 0, 50, 100));
+
+ // View 1 is entirely clipped by the parent and not visible, dispatch
+ // skips this view entirely.
+ view1.assertDispatchScrollCaptureSearchCount(0);
+
+ // View 2, verify the computed localVisibleRect and windowOffset are correctly transformed
+ // to the child coordinate space
+ view2.assertDispatchScrollCaptureSearchCount(1);
+ view2.assertDispatchScrollCaptureSearchLastArgs(
+ new Rect(25, 25, 150, 75), new Point(0, 25));
+
+ // View 3, verify the computed localVisibleRect and windowOffset are correctly transformed
+ // to the child coordinate space
+ view3.assertDispatchScrollCaptureSearchCount(1);
+ view3.assertDispatchScrollCaptureSearchLastArgs(
+ new Rect(25, 0, 175, 50), new Point(0, 100));
+
+ // view4 is invisible, so it should be skipped entirely.
+ view4.assertDispatchScrollCaptureSearchCount(0);
+
+ // view5 is partially visible
+ view5.assertDispatchScrollCaptureSearchCount(1);
+ view5.assertDispatchScrollCaptureSearchLastArgs(
+ new Rect(0, 0, 25, 50), new Point(150, 100));
+
+ assertTrue(receiver.hasValue());
+ assertEquals("expected two targets", 2, receiver.getAllValues().size());
+ }
+
+ /**
+ * Test stand-in for ScrollCaptureSearchResults which is not part the public API. This
+ * dispatches a request each potential target's handler and collects the results
+ * synchronously on the calling thread. Use with caution!
+ *
+ * @param receiver the result consumer
+ */
+ private void callOnScrollCaptureSearch(Receiver<ScrollCaptureTarget> receiver) {
+ CancellationSignal signal = new CancellationSignal();
+ receiver.getAllValues().forEach(target ->
+ target.getCallback().onScrollCaptureSearch(signal, (scrollBounds) -> {
+ if (!nullOrEmpty(scrollBounds)) {
+ target.setScrollBounds(scrollBounds);
+ target.updatePositionInWindow();
+ }
+ }));
+ }
+
+ /**
+ * Tests the effect of padding on scroll capture search dispatch.
+ * <p>
+ * Verifies computation of child visible bounds with padding.
+ */
+ @MediumTest
+ @Test
+ public void testOnScrollCaptureSearch_withPadding() {
+ final Context context = getInstrumentation().getContext();
+
+ Rect windowBounds = new Rect(0, 0, 200, 200);
+ Point windowOffset = new Point(0, 0);
+
+ final MockViewGroup parent = new MockViewGroup(context, 0, 0, 200, 200);
+ parent.setPadding(25, 50, 25, 50);
+ parent.setClipToPadding(true); // (default)
+
+ final MockView view1 = new MockView(context, 0, -100, 200, 100);
+ parent.addView(view1);
+
+ final MockView view2 = new MockView(context, 0, 0, 200, 200);
+ parent.addView(view2);
+
+ final MockViewGroup view3 = new MockViewGroup(context, 0, 100, 200, 300);
+ parent.addView(view3);
+ view3.setPadding(25, 25, 25, 25);
+ view3.setClipToPadding(true);
+
+ // Where targets are added
+ Receiver<ScrollCaptureTarget> receiver = new Receiver<>();
+
+ // Dispatch to the ViewGroup
+ parent.dispatchScrollCaptureSearch(windowBounds, windowOffset, receiver);
+
+ // Verify padding (with clipToPadding) is subtracted from visibleBounds
+ parent.assertOnScrollCaptureSearchLastArgs(new Rect(25, 50, 175, 150), new Point(0, 0));
+
+ view1.assertOnScrollCaptureSearchLastArgs(
+ new Rect(25, 150, 175, 200), new Point(0, -100));
+
+ view2.assertOnScrollCaptureSearchLastArgs(
+ new Rect(25, 50, 175, 150), new Point(0, 0));
+
+ // Account for padding on view3 as well (top == 25px)
+ view3.assertOnScrollCaptureSearchLastArgs(
+ new Rect(25, 25, 175, 50), new Point(0, 100));
+ }
+
+ public static final class MockView extends View {
+
+ private int mDispatchScrollCaptureSearchNumCalls;
+ private Rect mDispatchScrollCaptureSearchLastLocalVisibleRect;
+ private Point mDispatchScrollCaptureSearchLastWindowOffset;
+ private int mCreateScrollCaptureCallbackInternalCount;
+ private Rect mOnScrollCaptureSearchLastLocalVisibleRect;
+ private Point mOnScrollCaptureSearchLastWindowOffset;
+
+ MockView(Context context) {
+ this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0);
+ }
+
+ MockView(Context context, int left, int top, int right, int bottom) {
+ this(context, left, top, right, bottom, View.VISIBLE);
+ }
+
+ MockView(Context context, int left, int top, int right, int bottom, int visibility) {
+ super(context);
+ setVisibility(visibility);
+ setLeftTopRightBottom(left, top, right, bottom);
+ }
+
+ void assertDispatchScrollCaptureSearchCount(int count) {
+ assertEquals("Unexpected number of calls to dispatchScrollCaptureSearch",
+ count, mDispatchScrollCaptureSearchNumCalls);
+ }
+
+ void assertDispatchScrollCaptureSearchLastArgs(Rect localVisibleRect, Point windowOffset) {
+ assertEquals("arg localVisibleRect was incorrect.",
+ localVisibleRect, mDispatchScrollCaptureSearchLastLocalVisibleRect);
+ assertEquals("arg windowOffset was incorrect.",
+ windowOffset, mDispatchScrollCaptureSearchLastWindowOffset);
+ }
+
+ void reset() {
+ mDispatchScrollCaptureSearchNumCalls = 0;
+ mDispatchScrollCaptureSearchLastWindowOffset = null;
+ mDispatchScrollCaptureSearchLastLocalVisibleRect = null;
+ mCreateScrollCaptureCallbackInternalCount = 0;
+
+ }
+
+ @Override
+ public void onScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
+ Consumer<ScrollCaptureTarget> targets) {
+ super.onScrollCaptureSearch(localVisibleRect, windowOffset, targets);
+ mOnScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
+ mOnScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
+ }
+
+ void assertOnScrollCaptureSearchLastArgs(Rect localVisibleRect, Point windowOffset) {
+ assertEquals("arg localVisibleRect was incorrect.",
+ localVisibleRect, mOnScrollCaptureSearchLastLocalVisibleRect);
+ assertEquals("arg windowOffset was incorrect.",
+ windowOffset, mOnScrollCaptureSearchLastWindowOffset);
+ }
+
+ @Override
+ public void dispatchScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
+ Consumer<ScrollCaptureTarget> results) {
+ mDispatchScrollCaptureSearchNumCalls++;
+ mDispatchScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
+ mDispatchScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
+ super.dispatchScrollCaptureSearch(localVisibleRect, windowOffset, results);
+ }
+ }
+
+ static class CallbackStub implements ScrollCaptureCallback {
+
+ @Override
+ public void onScrollCaptureSearch(@NonNull CancellationSignal signal,
+ @NonNull Consumer<Rect> onReady) {
+ }
+
+ @Override
+ public void onScrollCaptureStart(@NonNull ScrollCaptureSession session,
+ @NonNull CancellationSignal signal, @NonNull Runnable onReady) {
+ }
+
+ @Override
+ public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session,
+ @NonNull CancellationSignal signal, @NonNull Rect captureArea,
+ Consumer<Rect> onComplete) {
+ }
+
+ @Override
+ public void onScrollCaptureEnd(@NonNull Runnable onReady) {
+ }
+ };
+
+ public static final class MockViewGroup extends ViewGroup {
+ private Rect mOnScrollCaptureSearchLastLocalVisibleRect;
+ private Point mOnScrollCaptureSearchLastWindowOffset;
+
+ MockViewGroup(Context context) {
+ this(context, /* left */ 0, /* top */0, /* right */ 0, /* bottom */0);
+ }
+
+ MockViewGroup(Context context, int left, int top, int right, int bottom) {
+ this(context, left, top, right, bottom, View.SCROLL_CAPTURE_HINT_AUTO);
+ }
+
+ MockViewGroup(Context context, int left, int top, int right, int bottom,
+ int scrollCaptureHint) {
+ super(context);
+ setScrollCaptureHint(scrollCaptureHint);
+ setLeftTopRightBottom(left, top, right, bottom);
+ }
+
+ @Override
+ public void onScrollCaptureSearch(Rect localVisibleRect, Point windowOffset,
+ Consumer<ScrollCaptureTarget> targets) {
+ super.onScrollCaptureSearch(localVisibleRect, windowOffset, targets);
+ mOnScrollCaptureSearchLastLocalVisibleRect = new Rect(localVisibleRect);
+ mOnScrollCaptureSearchLastWindowOffset = new Point(windowOffset);
+ }
+
+ void assertOnScrollCaptureSearchLastArgs(Rect localVisibleRect, Point windowOffset) {
+ assertEquals("arg localVisibleRect was incorrect.",
+ localVisibleRect, mOnScrollCaptureSearchLastLocalVisibleRect);
+ assertEquals("arg windowOffset was incorrect.",
+ windowOffset, mOnScrollCaptureSearchLastWindowOffset);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // We don't layout this view.
+ }
+ }
+}
diff --git a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
index 61519a5..2aaf0cc 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -18,7 +18,6 @@
import android.app.VoiceInteractor.PickOptionRequest.Option;
import android.content.LocusId;
import android.os.Bundle;
-import android.os.Parcelable;
import android.util.Log;
import java.util.ArrayList;
@@ -67,14 +66,16 @@
public static final int HOTWORD_DETECTION_SERVICE_FROM_SOFTWARE_TRIGGER_TEST = 103;
public static final int HOTWORD_DETECTION_SERVICE_MIC_ONDETECT_TEST = 104;
public static final int HOTWORD_DETECTION_SERVICE_DSP_ONREJECT_TEST = 105;
+ public static final int HOTWORD_DETECTION_SERVICE_PROCESS_DIED_TEST = 106;
public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS = 1;
public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_ILLEGAL_STATE_EXCEPTION = 2;
public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_SECURITY_EXCEPTION = 3;
public static final int HOTWORD_DETECTION_SERVICE_TRIGGER_SHARED_MEMORY_NOT_READ_ONLY = 4;
+ public static final int HOTWORD_DETECTION_SERVICE_GET_ERROR = 5;
- public static final int HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS = 1;
- public static final int HOTWORD_DETECTION_SERVICE_ONDETECT_REJECTION = 2;
+ /** Indicate which test scenario for testing. */
+ public static final int HOTWORD_DETECTION_SERVICE_ON_UPDATE_STATE_CRASH = 1;
public static final String TESTCASE_TYPE = "testcase_type";
public static final String TESTINFO = "testinfo";
@@ -155,6 +156,7 @@
public static final String KEY_SERVICE_TYPE = "serviceType";
public static final String KEY_TEST_EVENT = "testEvent";
public static final String KEY_TEST_RESULT = "testResult";
+ public static final String KEY_TEST_SCENARIO = "testScenario";
public static final String toBundleString(Bundle bundle) {
if (bundle == null) {
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java
index ee003d5..bf9f167 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/BasicVoiceInteractionService.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.media.AudioFormat;
import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.SharedMemory;
import android.service.voice.AlwaysOnHotwordDetector;
@@ -127,6 +128,17 @@
mSoftwareHotwordDetector.startRecognition();
}
});
+ } else if (testEvent == Utils.HOTWORD_DETECTION_SERVICE_PROCESS_DIED_TEST) {
+ runWithShellPermissionIdentity(() -> {
+ if (mAlwaysOnHotwordDetector != null) {
+ PersistableBundle persistableBundle = new PersistableBundle();
+ persistableBundle.putInt(Utils.KEY_TEST_SCENARIO,
+ Utils.HOTWORD_DETECTION_SERVICE_ON_UPDATE_STATE_CRASH);
+ mAlwaysOnHotwordDetector.updateState(
+ persistableBundle,
+ createFakeSharedMemoryData());
+ }
+ }, Manifest.permission.MANAGE_HOTWORD_DETECTION);
}
return START_NOT_STICKY;
@@ -156,7 +168,7 @@
Log.i(TAG, "onDetected");
broadcastIntentWithResult(
Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS);
+ eventPayload.getHotwordDetectedResult());
}
@Override
@@ -164,12 +176,15 @@
Log.i(TAG, "onRejected");
broadcastIntentWithResult(
Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_REJECTION);
+ result);
}
@Override
public void onError() {
Log.i(TAG, "onError");
+ broadcastIntentWithResult(
+ Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
+ Utils.HOTWORD_DETECTION_SERVICE_GET_ERROR);
}
@Override
@@ -213,12 +228,15 @@
Log.i(TAG, "onDetected");
broadcastIntentWithResult(
Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS);
+ eventPayload.getHotwordDetectedResult());
}
@Override
public void onError() {
Log.i(TAG, "onError");
+ broadcastIntentWithResult(
+ Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
+ Utils.HOTWORD_DETECTION_SERVICE_GET_ERROR);
}
@Override
@@ -260,6 +278,14 @@
sendBroadcast(intent);
}
+ private void broadcastIntentWithResult(String intentName, Parcelable result) {
+ Intent intent = new Intent(intentName)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+ .putExtra(Utils.KEY_TEST_RESULT, result);
+ Log.d(TAG, "broadcast intent = " + intent + ", result = " + result);
+ sendBroadcast(intent);
+ }
+
private SharedMemory createFakeSharedMemoryData() {
try {
SharedMemory sharedMemory = SharedMemory.create("SharedMemory", 3);
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
index afef782..da8c6e8 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java
@@ -19,6 +19,7 @@
import android.media.AudioFormat;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
+import android.os.Process;
import android.os.SharedMemory;
import android.service.voice.AlwaysOnHotwordDetector;
import android.service.voice.HotwordDetectedResult;
@@ -27,6 +28,7 @@
import android.system.ErrnoException;
import android.text.TextUtils;
import android.util.Log;
+import android.voiceinteraction.common.Utils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -38,6 +40,16 @@
public class MainHotwordDetectionService extends HotwordDetectionService {
static final String TAG = "MainHotwordDetectionService";
+ // TODO: Fill in the remaining fields.
+ public static final HotwordDetectedResult DETECTED_RESULT =
+ new HotwordDetectedResult.Builder()
+ .setConfidenceLevel(HotwordDetectedResult.CONFIDENCE_LEVEL_HIGH)
+ .build();
+ public static final HotwordRejectedResult REJECTED_RESULT =
+ new HotwordRejectedResult.Builder()
+ .setConfidenceLevel(HotwordRejectedResult.CONFIDENCE_LEVEL_MEDIUM)
+ .build();
+
@Override
public void onDetect(@NonNull AlwaysOnHotwordDetector.EventPayload eventPayload,
long timeoutMillis, @NonNull Callback callback) {
@@ -46,9 +58,9 @@
// TODO: Check the capture session (needs to be reflectively accessed).
byte[] data = eventPayload.getTriggerAudio();
if (data != null && data.length > 0) {
- callback.onDetected(new HotwordDetectedResult.Builder().build());
+ callback.onDetected(DETECTED_RESULT);
} else {
- callback.onRejected(new HotwordRejectedResult.Builder().build());
+ callback.onRejected(REJECTED_RESULT);
}
}
@@ -93,7 +105,7 @@
if(isSame(buffer, BasicVoiceInteractionService.FAKE_HOTWORD_AUDIO_DATA,
buffer.length)) {
Log.d(TAG, "call callback.onDetected");
- callback.onDetected(new HotwordDetectedResult.Builder().build());
+ callback.onDetected(DETECTED_RESULT);
}
} catch (IOException e) {
Log.w(TAG, "Failed to read data : ", e);
@@ -103,7 +115,7 @@
@Override
public void onDetect(@NonNull Callback callback) {
Log.d(TAG, "onDetect for Mic source");
- callback.onDetected(new HotwordDetectedResult.Builder().build());
+ callback.onDetected(DETECTED_RESULT);
}
@Override
@@ -115,6 +127,12 @@
Log.d(TAG, "onUpdateState");
if (options != null) {
+ if (options.getInt(Utils.KEY_TEST_SCENARIO, -1)
+ == Utils.HOTWORD_DETECTION_SERVICE_ON_UPDATE_STATE_CRASH) {
+ Log.d(TAG, "Crash itself. Pid: " + Process.myPid());
+ Process.killProcess(Process.myPid());
+ return;
+ }
String fakeData = options.getString(BasicVoiceInteractionService.KEY_FAKE_DATA);
if (!TextUtils.equals(fakeData, BasicVoiceInteractionService.VALUE_FAKE_DATA)) {
Log.d(TAG, "options : data is not the same");
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
index 5919e50..513fee8 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java
@@ -19,10 +19,14 @@
import static com.google.common.truth.Truth.assertThat;
import android.content.Intent;
+import android.os.Parcelable;
import android.platform.test.annotations.AppModeFull;
+import android.service.voice.HotwordDetectedResult;
import android.service.voice.HotwordDetectionService;
import android.voiceinteraction.common.Utils;
+import android.voiceinteraction.service.MainHotwordDetectionService;
+import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
@@ -77,10 +81,7 @@
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS);
- // Use AlwaysOnHotwordDetector to test the onDetect function of HotwordDetectionService
- testHotwordDetection(Utils.HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_TEST,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS);
+ verifyDetectedResult(performOnDetect(Utils.HOTWORD_DETECTION_SERVICE_DSP_ONDETECT_TEST));
}
@Test
@@ -91,10 +92,8 @@
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS);
- // Use AlwaysOnHotwordDetector to test the onReject callback function
- testHotwordDetection(Utils.HOTWORD_DETECTION_SERVICE_DSP_ONREJECT_TEST,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_REJECTION);
+ assertThat(performOnDetect(Utils.HOTWORD_DETECTION_SERVICE_DSP_ONREJECT_TEST))
+ .isEqualTo(MainHotwordDetectionService.REJECTED_RESULT);
}
@Test
@@ -105,11 +104,8 @@
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS);
- // Use AlwaysOnHotwordDetector to test the external source function of
- // HotwordDetectionService
- testHotwordDetection(Utils.HOTWORD_DETECTION_SERVICE_EXTERNAL_SOURCE_ONDETECT_TEST,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS);
+ verifyDetectedResult(
+ performOnDetect(Utils.HOTWORD_DETECTION_SERVICE_EXTERNAL_SOURCE_ONDETECT_TEST));
}
@Test
@@ -120,11 +116,21 @@
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS);
- // Use SoftwareHotwordDetector to test the mic source function of
- // HotwordDetectionService
- testHotwordDetection(Utils.HOTWORD_DETECTION_SERVICE_MIC_ONDETECT_TEST,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT,
- Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_SUCCESS);
+ verifyDetectedResult(performOnDetect(Utils.HOTWORD_DETECTION_SERVICE_MIC_ONDETECT_TEST));
+ }
+
+ @Test
+ public void testHotwordDetectionService_processDied_triggerOnError()
+ throws Throwable {
+ // Create AlwaysOnHotwordDetector and wait the HotwordDetectionService ready
+ testHotwordDetection(Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_TEST,
+ Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
+ Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_SUCCESS);
+
+ // Use AlwaysOnHotwordDetector to test process died of HotwordDetectionService
+ testHotwordDetection(Utils.HOTWORD_DETECTION_SERVICE_PROCESS_DIED_TEST,
+ Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT,
+ Utils.HOTWORD_DETECTION_SERVICE_GET_ERROR);
}
private void testHotwordDetection(int testType, String expectedIntent, int expectedResult) {
@@ -145,6 +151,35 @@
assertThat(intent.getIntExtra(Utils.KEY_TEST_RESULT, -1)).isEqualTo(expectedResult);
}
+ @NonNull
+ private Parcelable performOnDetect(int testType) {
+ final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+ Utils.HOTWORD_DETECTION_SERVICE_ONDETECT_RESULT_INTENT);
+ receiver.register();
+
+ mActivityTestRule.getScenario().onActivity(activity -> {
+ activity.triggerHotwordDetectionServiceTest(
+ Utils.HOTWORD_DETECTION_SERVICE_BASIC,
+ testType);
+ });
+
+ final Intent intent = receiver.awaitForBroadcast(TIMEOUT_MS);
+ receiver.unregisterQuietly();
+
+ assertThat(intent).isNotNull();
+ final Parcelable result = intent.getParcelableExtra(Utils.KEY_TEST_RESULT);
+ assertThat(result).isNotNull();
+ return result;
+ }
+
+ // TODO: Implement HotwordDetectedResult#equals to override the Bundle equality check; then
+ // simply check that the HotwordDetectedResults are equal.
+ private void verifyDetectedResult(Parcelable result) {
+ assertThat(result).isInstanceOf(HotwordDetectedResult.class);
+ assertThat(((HotwordDetectedResult) result).getConfidenceLevel())
+ .isEqualTo(MainHotwordDetectionService.DETECTED_RESULT.getConfidenceLevel());
+ }
+
@Override
public String getVoiceInteractionService() {
return "android.voiceinteraction.cts/"
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java
index 50bff42..14c74cf 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsFixedCollectionAdapterTest.java
@@ -347,6 +347,46 @@
}
@Test
+ public void testSetRemoteAdapter_clickFillListener() throws Throwable {
+ String action = "my-action";
+ MockBroadcastReceiver receiver = new MockBroadcastReceiver();
+ mActivity.registerReceiver(receiver, new IntentFilter(action));
+
+ Intent intent = new Intent(action).setPackage(mActivity.getPackageName());
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(
+ mActivity,
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
+ mRemoteViews.setPendingIntentTemplate(R.id.remoteView_list, pendingIntent);
+
+ ListView listView = mView.findViewById(R.id.remoteView_list);
+
+ RemoteViews item0 = new RemoteViews(PACKAGE_NAME, R.layout.listitemfixed_layout);
+ item0.setTextViewText(android.R.id.text1, "Hello");
+ item0.setOnClickFillInIntent(android.R.id.text1, new Intent().putExtra("my-extra", 42));
+
+ RemoteViews item1 = new RemoteViews(PACKAGE_NAME, R.layout.listitemfixed_layout);
+ item1.setTextViewText(android.R.id.text1, "World");
+
+ RemoteCollectionItems items = new RemoteCollectionItems.Builder()
+ .setHasStableIds(true)
+ .addItem(10 /* id= */, item0)
+ .addItem(11 /* id= */, item1)
+ .build();
+
+ mRemoteViews.setRemoteAdapter(R.id.remoteView_list, items);
+ WidgetTestUtils.runOnMainAndLayoutSync(mActivityRule,
+ () -> mRemoteViews.reapply(mActivity, mView), true);
+
+ mActivityRule.runOnUiThread(() -> listView.performItemClick(listView.getChildAt(0), 0, 10));
+ mInstrumentation.waitForIdleSync();
+ assertNotNull(receiver.mIntent);
+ assertEquals(42, receiver.mIntent.getIntExtra("my-extra", 0));
+ }
+
+ @Test
public void testSetRemoteAdapter_newViewTypeAddedCoveredByViewTypeCount() {
ListView listView = mView.findViewById(R.id.remoteView_list);
diff --git a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
index 82c4e71..bc19edd 100644
--- a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
+++ b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
@@ -182,7 +182,8 @@
SimpleActivity.HELLO_TEXT_ID);
assertThat(helloText).isNotNull();
// Set response
- sTranslationReplier.addResponse(createViewsTranslationResponse(views, translatedText));
+ final TranslationResponse response = createViewsTranslationResponse(views, translatedText);
+ sTranslationReplier.addResponse(response);
runWithShellPermissionIdentity(() -> {
// Call startTranslation API
@@ -206,6 +207,8 @@
SystemClock.sleep(UI_WAIT_TIMEOUT);
assertThat(helloText.getText().toString()).isEqualTo(translatedText);
+ assertThat(mTextView.getViewTranslationResponse())
+ .isEqualTo(response.getViewTranslationResponses().get(0));
// Call pauseTranslation API
manager.pauseTranslation(contentCaptureContext.getActivityId());
@@ -230,6 +233,25 @@
mTranslationServiceServiceWatcher.getService();
translationService.awaitSessionDestroyed();
});
+
+ // Test re-translating.
+ sTranslationReplier.addResponse(createViewsTranslationResponse(views, translatedText));
+ runWithShellPermissionIdentity(() -> {
+ manager.startTranslation(
+ new TranslationSpec(ULocale.ENGLISH,
+ TranslationSpec.DATA_FORMAT_TEXT),
+ new TranslationSpec(ULocale.FRENCH,
+ TranslationSpec.DATA_FORMAT_TEXT),
+ views, contentCaptureContext.getActivityId(),
+ new UiTranslationSpec.Builder().build());
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+ assertThat(helloText.getText()).isEqualTo(translatedText);
+
+ // Also make sure pausing still works.
+ manager.pauseTranslation(contentCaptureContext.getActivityId());
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+ assertThat(helloText.getText()).isEqualTo(originalText.toString());
+ });
}
@Test
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index ad28ed9..de90e52 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -219,7 +219,4 @@
<!-- b/135533962 -->
<option name="compatibility:exclude-filter" value="arm64-v8a CtsWrapWrapDebugMallocDebugTestCases" />
-
- <!-- b/175319005 -->
- <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.ManagedProfileTest#testAppLinks_verificationStatus" />
</configuration>