Merge "setAutoTimeRequired disabled on managed profile" into rvc-dev
diff --git a/apps/CameraITS/tests/scene2_b/test_auto_per_frame_control.py b/apps/CameraITS/tests/scene2_b/test_auto_per_frame_control.py
index 95ff8b8..4af6543 100644
--- a/apps/CameraITS/tests/scene2_b/test_auto_per_frame_control.py
+++ b/apps/CameraITS/tests/scene2_b/test_auto_per_frame_control.py
@@ -28,6 +28,7 @@
DELTA_GAIN_THRESH = 0.03 # >3% gain change --> luma change in same dir
DELTA_LUMA_THRESH = 0.03 # 3% frame-to-frame noise test_burst_sameness_manual
DELTA_NO_GAIN_THRESH = 0.01 # <1% gain change --> min luma change
+LSC_TOL = 0.005 # allow <0.5% change in lens shading correction
NAME = os.path.basename(__file__).split('.')[0]
NUM_CAPS = 1
NUM_FRAMES = 30
@@ -35,6 +36,46 @@
VALID_STABLE_LUMA_MAX = 0.9
+def lsc_unchanged(lsc_avlb, lsc, idx):
+ """Determine if lens shading correction unchanged.
+
+ Args:
+ lsc_avlb: bool; True if lens shading correction available
+ lsc: list; lens shading correction matrix
+ idx: int; frame index
+ Returns:
+ boolean
+ """
+ if lsc_avlb:
+ diff = list((np.array(lsc[idx]) - np.array(lsc[idx-1])) /
+ np.array(lsc[idx-1]))
+ diff = map(abs, diff)
+ max_abs_diff = max(diff)
+ if max_abs_diff > LSC_TOL:
+ print ' max abs(LSC) change:', round(max_abs_diff, 4)
+ return False
+ else:
+ return True
+ else:
+ return True
+
+
+def tonemap_unchanged(raw_cap, tonemap_g, idx):
+ """Determine if tonemap unchanged.
+
+ Args:
+ raw_cap: bool; True if RAW capture
+ tonemap_g: list; green tonemap
+ idx: int; frame index
+ Returns:
+ boolean
+ """
+ if not raw_cap:
+ return tonemap_g[idx-1] == tonemap_g[idx]
+ else:
+ return True
+
+
def is_awb_af_stable(cap_info, i):
awb_gains_0 = cap_info[i-1]['awb_gains']
awb_gains_1 = cap_info[i]['awb_gains']
@@ -80,16 +121,21 @@
ae_states = []
lumas = []
total_gains = []
+ tonemap_g = []
+ lsc = []
num_caps = NUM_CAPS
num_frames = NUM_FRAMES
raw_cap = f == 0 and raw_avlb and debug
+ lsc_avlb = its.caps.lsc_map(props) and not raw_cap
+ print 'lens shading correction available:', lsc_avlb
+ if lsc_avlb:
+ req['android.statistics.lensShadingMapMode'] = 1
name_suffix = 'YUV'
if raw_cap:
name_suffix = 'RAW'
# break up caps if RAW to reduce load
num_caps = NUM_CAPS * 6
num_frames = NUM_FRAMES / 6
-
for j in range(num_caps):
caps = cam.do_capture([req]*num_frames, fmt)
for i, cap in enumerate(caps):
@@ -127,8 +173,12 @@
print 'AWB state: %d, AWB gains: %s\n AWB matrix: %s' % (
awb_state, str(frame['awb_gains']),
str(awb_ccm))
- if debug:
- print 'Tonemap curve:', cap['metadata']['android.tonemap.curve']
+ if not raw_cap:
+ tonemap = cap['metadata']['android.tonemap.curve']
+ tonemap_g.append(tonemap['green'])
+ print 'G tonemap curve:', tonemap_g[idx]
+ if lsc_avlb:
+ lsc.append(cap['metadata']['android.statistics.lensShadingCorrectionMap']['map'])
img = its.image.convert_capture_to_rgb_image(
cap, props=props)
@@ -179,32 +229,37 @@
# Threshold change to trigger check. Small delta_gain might
# not be enough to generate a reliable delta_luma to
# overcome frame-to-frame variation.
- if abs(delta_gain_rel) > DELTA_GAIN_THRESH:
- print 'frame %d: %.2f%% delta gain,' % (
- i, delta_gain_rel*100),
- print '%.2f%% delta luma' % (delta_luma_rel*100)
- if delta_gain * delta_luma < 0.0:
- failed.append(msg)
- elif abs(delta_gain_rel) < DELTA_NO_GAIN_THRESH:
- print 'frame %d: <|%.1f%%| delta gain,' % (
- i, DELTA_NO_GAIN_THRESH*100),
- print '%.2f%% delta luma' % (delta_luma_rel*100)
- msg = '%s: ' % fmt['format']
- msg += 'frame %d: gain %.1f -> %.1f (%.1f%%), ' % (
- i, prev_total_gain, total_gain,
- delta_gain_rel*100)
- msg += 'luma %f -> %f (%.1f%%) ' % (
- prev_luma, luma, delta_luma_rel*100)
- msg += '<|%.1f%%| GAIN, >|%.f%%| LUMA DELTA' % (
- DELTA_NO_GAIN_THRESH*100, DELTA_LUMA_THRESH*100)
- if abs(delta_luma_rel) > DELTA_LUMA_THRESH:
- failed.append(msg)
+ if (tonemap_unchanged(raw_cap, tonemap_g, i) and
+ lsc_unchanged(lsc_avlb, lsc, i)):
+ if abs(delta_gain_rel) > DELTA_GAIN_THRESH:
+ print ' frame %d: %.2f%% delta gain,' % (
+ i, delta_gain_rel*100),
+ print '%.2f%% delta luma' % (delta_luma_rel*100)
+ if delta_gain * delta_luma < 0.0:
+ failed.append(msg)
+ elif abs(delta_gain_rel) < DELTA_NO_GAIN_THRESH:
+ print ' frame %d: <|%.1f%%| delta gain,' % (
+ i, DELTA_NO_GAIN_THRESH*100),
+ print '%.2f%% delta luma' % (delta_luma_rel*100)
+ msg = '%s: ' % fmt['format']
+ msg += 'frame %d: gain %.1f -> %.1f (%.1f%%), ' % (
+ i, prev_total_gain, total_gain,
+ delta_gain_rel*100)
+ msg += 'luma %f -> %f (%.1f%%) ' % (
+ prev_luma, luma, delta_luma_rel*100)
+ msg += '<|%.1f%%| GAIN, >|%.f%%| LUMA DELTA' % (
+ DELTA_NO_GAIN_THRESH*100, DELTA_LUMA_THRESH*100)
+ if abs(delta_luma_rel) > DELTA_LUMA_THRESH:
+ failed.append(msg)
+ else:
+ print ' frame %d: %.1f%% delta gain,' % (
+ i, delta_gain_rel*100),
+ print '%.2f%% delta luma' % (delta_luma_rel*100)
else:
- print 'frame %d: %.1f%% delta gain,' % (
- i, delta_gain_rel*100),
- print '%.2f%% delta luma' % (delta_luma_rel*100)
+ print ' frame %d -> %d: tonemap' % (i-1, i),
+ print 'or lens shading correction changed'
else:
- print 'frame %d -> %d: AWB/AF changed' % (i-1, i)
+ print ' frame %d -> %d: AWB/AF changed' % (i-1, i)
for i in range(len(lumas)):
luma = lumas[i]
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 0a0226f..fb7cb0e 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -60,6 +60,7 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
<uses-feature android:name="android.hardware.usb.accessory" />
<uses-permission android:name="android.permission.CALL_PHONE" />
@@ -1982,7 +1983,8 @@
<meta-data android:name="android.nfc.cardemulation.host_nfcf_service" android:resource="@xml/felicaservice"/>
</service>
<!-- Service used for Camera ITS tests -->
- <service android:name=".camera.its.ItsService" >
+ <service android:name=".camera.its.ItsService"
+ android:foregroundServiceType="camera">
<intent-filter>
<action android:name="com.android.cts.verifier.camera.its.START"/>
<category android:name="android.intent.category.DEFAULT" />
@@ -3564,8 +3566,6 @@
<meta-data android:name="test_category" android:value="@string/test_category_tv" />
<meta-data android:name="test_required_features"
android:value="android.software.leanback" />
- <meta-data android:name="test_required_configs"
- android:value="config_hdmi_source"/>
</activity>
<activity android:name=".tv.display.DisplayModesTestActivity"
android:label="@string/tv_display_modes_test"
diff --git a/apps/CtsVerifier/jni/midi/Android.bp b/apps/CtsVerifier/jni/midi/Android.bp
index 4e707eb..f437360 100644
--- a/apps/CtsVerifier/jni/midi/Android.bp
+++ b/apps/CtsVerifier/jni/midi/Android.bp
@@ -26,8 +26,7 @@
"system/core/include/cutils",
],
sdk_version: "current",
- //stl: "c++_static"
- stl: "system",
+ stl: "libc++_static",
shared_libs: [
"liblog",
"libamidi",
diff --git a/apps/CtsVerifier/jni/verifier/Android.bp b/apps/CtsVerifier/jni/verifier/Android.bp
index 2bdb74c..e34e86f 100644
--- a/apps/CtsVerifier/jni/verifier/Android.bp
+++ b/apps/CtsVerifier/jni/verifier/Android.bp
@@ -21,7 +21,7 @@
"com_android_cts_verifier_camera_StatsImage.cpp",
],
sdk_version: "current",
- stl: "system",
+ stl: "libc++_static",
shared_libs: ["liblog"],
cflags: [
"-Wall",
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 51a4810..d1a8c1a 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -61,6 +61,10 @@
<string name="runtime_permissions_error">Please grant runtime permissions, otherwise, tests might fail.</string>
<string name="export">Export</string>
<string name="no_storage">Cannot save report to external storage, see log for details.</string>
+ <string name="no_storage_io_parser_exception">Cannot save report to external
+ storage, see log for details. Or try running the following command if
+ you haven\'t yet.\n\"adb shell appops set com.android.cts.verifier
+ MANAGE_EXTERNAL_STORAGE 0\"</string>
<string name="report_saved">Report saved to: %s</string>
<!-- Strings for IntentDrivenTestActivity -->
@@ -4041,20 +4045,22 @@
Please do the following:\n
1) You should have received NotificationBot.apk together with the CTS verifier. If you built the CTS verifier yourself, build the NotificationBot.apk by issuing the following command on the host:\n
make NotificationBot\n
- 2) Upload the NotificationBot.apk to your device by issuing the following command on the host:\n
+ 2) Grant MANAGE_EXTERNAL_STORAGE by issuing the following command on the host:\n
+ adb shell appops set com.android.cts.verifier MANAGE_EXTERNAL_STORAGE 0\n
+ 3) Upload the NotificationBot.apk to your device by issuing the following command on the host:\n
adb push /path/to/NotificationBot.apk /sdcard\n
- 3) Press the Uninstall button.\n
- 4) Press the Open Settings button.\n
- 5) In the screen that opens, verify that you are not told that your administrator installed any apps.\n
- 6) Use the Back button to return to this page.\n
- 7) Press the Install button.\n
- 8) Press the Open Settings button.\n
- 9) In the screen that opens, verify that you are told now that your administrator installed at least one app.\n
- 10) Tap on that information. Verify that a list of apps installed shows.\n
- 11) Verify that the list contains the CTS Robot app.\n
- 12) Use the Back button to return to this page.\n
- 13) Press the Uninstall button.\n
- 14) Issue the following command on the host:\n
+ 4) Press the Uninstall button.\n
+ 5) Press the Open Settings button.\n
+ 6) In the screen that opens, verify that you are not told that your administrator installed any apps.\n
+ 7) Use the Back button to return to this page.\n
+ 8) Press the Install button.\n
+ 9) Press the Open Settings button.\n
+ 10) In the screen that opens, verify that you are told now that your administrator installed at least one app.\n
+ 11) Tap on that information. Verify that a list of apps installed shows.\n
+ 12) Verify that the list contains the CTS Robot app.\n
+ 13) Use the Back button to return to this page.\n
+ 14) Press the Uninstall button.\n
+ 15) Issue the following command on the host:\n
adb shell rm /sdcard/NotificationBot.apk
</string>
<string name="enterprise_privacy_install">Install</string>
@@ -4446,6 +4452,7 @@
<string name="tv_yes">Yes</string>
<string name="tv_no">No</string>
+ <string name="tv_none">None</string>
<string name="tv_launch_tv_app">Launch TV app</string>
<string name="tv_launch_epg">Launch EPG</string>
<string name="tv_launch_setup">Launch setup</string>
@@ -4578,6 +4585,12 @@
and disconnect the display within %2$d seconds. Wait at least %3$d seconds and then
reconnect the display.
</string>
+ <string name="tv_panel_hdr_types_reported_are_supported">
+ The supported HDR types are: %s\nAre all of them supported by the hardware?
+ </string>
+ <string name="tv_panel_hdr_types_supported_are_reported">
+ Are there other HDR types which are supported by the hardware, but are not listed above?
+ </string>
<!-- Display Modes Test -->
<string name="tv_display_modes_test">Display Modes Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
index f0200a6..f39a409 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/ReportExporter.java
@@ -108,7 +108,7 @@
ZipUtil.createZip(tempDir, reportZipFile);
} catch (IOException | XmlPullParserException e) {
LOG.log(Level.WARNING, "I/O exception writing report to storage.", e);
- return mContext.getString(R.string.no_storage);
+ return mContext.getString(R.string.no_storage_io_parser_exception);
} finally {
// delete the temporary directory and its files made for the report
FileUtil.recursiveDelete(tempDir);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
index ebd2e6f..09d705f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/RingerModeActivity.java
@@ -246,8 +246,17 @@
@Override
protected void test() {
+ boolean touchSoundEnabled =
+ Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SOUND_EFFECTS_ENABLED, 1) == 1;
if (mUserVerified) {
- status = PASS;
+ if (touchSoundEnabled) {
+ status = PASS;
+ return;
+ } else {
+ setFailed();
+ return;
+ }
} else {
status = WAIT_FOR_USER;
}
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 e843ce7..83a6724 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
@@ -22,6 +22,7 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ServiceInfo;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.camera2.CameraCaptureSession;
@@ -332,7 +333,8 @@
.setContentText("CameraITS Service is running")
.setSmallIcon(R.drawable.icon)
.setOngoing(true).build();
- startForeground(SERVICE_NOTIFICATION_ID, notification);
+ startForeground(SERVICE_NOTIFICATION_ID, notification,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA);
} catch (java.lang.InterruptedException e) {
Logt.e(TAG, "Error starting ItsService (interrupted)", e);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/CACertWriter.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/CACertWriter.java
new file mode 100644
index 0000000..5495273
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/CACertWriter.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.security;
+
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.res.AssetManager;
+import android.net.Uri;
+import android.provider.MediaStore;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class CACertWriter {
+ static final String TAG = CACertWriter.class.getSimpleName();
+
+ private static final String CERT_ASSET_NAME = "myCA.cer";
+
+ public static boolean extractCertToDownloads(
+ Context applicationContext, AssetManager assetManager) {
+ // Use MediaStore API to write the CA cert to the Download folder
+ final ContentValues downloadValues = new ContentValues();
+ downloadValues.put(MediaStore.MediaColumns.DISPLAY_NAME, CERT_ASSET_NAME);
+
+ Uri targetCollection = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
+ ContentResolver resolver = applicationContext.getContentResolver();
+ Uri toOpen = resolver.insert(targetCollection, downloadValues);
+ Log.i(TAG, String.format("Writing CA cert to %s", toOpen));
+
+ InputStream is = null;
+ OutputStream os = null;
+ try {
+ try {
+ is = assetManager.open(CERT_ASSET_NAME);
+ os = resolver.openOutputStream(toOpen);
+ byte[] buffer = new byte[1024];
+ int length;
+ while ((length = is.read(buffer)) > 0) {
+ os.write(buffer, 0, length);
+ }
+ } finally {
+ if (is != null) is.close();
+ if (os != null) os.close();
+ }
+ } catch (IOException ioe) {
+ Log.w(TAG, String.format("Problem moving cert file to %s", toOpen), ioe);
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/CAInstallNotificationVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/CAInstallNotificationVerifierActivity.java
index ee992e9..13c4244 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/CAInstallNotificationVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/CAInstallNotificationVerifierActivity.java
@@ -27,11 +27,6 @@
import com.android.cts.verifier.R;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
import com.android.cts.verifier.ArrayTestListAdapter;
import com.android.cts.verifier.DialogTestListActivity;
import com.android.cts.verifier.TestListActivity;
@@ -41,8 +36,6 @@
public class CAInstallNotificationVerifierActivity extends DialogTestListActivity {
static final String TAG = CAInstallNotificationVerifierActivity.class.getSimpleName();
- private static final String CERT_ASSET_NAME = "myCA.cer";
-
// From @hidden field in android.provider.Settings
private static final String ACTION_TRUSTED_CREDENTIALS_USER
= "com.android.settings.TRUSTED_CREDENTIALS_USER";
@@ -93,25 +86,7 @@
@Override
public void performTest(DialogTestListActivity activity) {
- final File certStagingFile = new File("/sdcard/", CERT_ASSET_NAME);
- InputStream is = null;
- FileOutputStream os = null;
- try {
- try {
- is = getAssets().open(CERT_ASSET_NAME);
- os = new FileOutputStream(certStagingFile);
- byte[] buffer = new byte[1024];
- int length;
- while ((length = is.read(buffer)) > 0) {
- os.write(buffer, 0, length);
- }
- } finally {
- if (is != null) is.close();
- if (os != null) os.close();
- certStagingFile.setReadable(true, false);
- }
- } catch (IOException ioe) {
- Log.w(TAG, "Problem moving cert file to /sdcard/", ioe);
+ if (!CACertWriter.extractCertToDownloads(getApplicationContext(), getAssets())) {
return;
}
super.performTest(activity);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/CANotifyOnBootActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/CANotifyOnBootActivity.java
index 9c799ab..c3c41ea 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/security/CANotifyOnBootActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/CANotifyOnBootActivity.java
@@ -14,16 +14,9 @@
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-
public class CANotifyOnBootActivity extends PassFailButtons.Activity {
private static final String TAG = CANotifyOnBootActivity.class.getSimpleName();
- private static final String CERT_ASSET_NAME = "myCA.cer";
- private File certStagingFile = new File("/sdcard/", CERT_ASSET_NAME);
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -60,24 +53,7 @@
class InstallCert implements OnClickListener {
@Override
public void onClick(View v) {
- InputStream is = null;
- FileOutputStream os = null;
- try {
- try {
- is = getAssets().open(CERT_ASSET_NAME);
- os = new FileOutputStream(certStagingFile);
- byte[] buffer = new byte[1024];
- int length;
- while ((length = is.read(buffer)) > 0) {
- os.write(buffer, 0, length);
- }
- } finally {
- if (is != null) is.close();
- if (os != null) os.close();
- certStagingFile.setReadable(true, false);
- }
- } catch (IOException ioe) {
- Log.w(TAG, "Problem moving cert file to /sdcard/", ioe);
+ if (!CACertWriter.extractCertToDownloads(getApplicationContext(), getAssets())) {
return;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
index b86c58b..1fd4bdd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayHdrCapabilitiesTestActivity.java
@@ -18,12 +18,14 @@
import android.content.Context;
import android.hardware.display.DisplayManager;
+import android.util.Log;
import android.view.Display;
import androidx.annotation.StringRes;
import com.android.cts.verifier.R;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
+import com.android.cts.verifier.tv.TvUtil;
import com.google.common.base.Throwables;
import com.google.common.collect.Range;
@@ -32,6 +34,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Test to verify the HDR Capabilities API is correctly implemented.
@@ -44,6 +47,7 @@
* no display is connected.
*/
public class DisplayHdrCapabilitiesTestActivity extends TvAppVerifierActivity {
+ private static final String LOG_TAG = "HdrCapabilitiesTest";
private static final float MAX_EXPECTED_LUMINANCE = 10_000f;
private static final int DISPLAY_DISCONNECT_WAIT_TIME_SECONDS = 5;
@@ -63,10 +67,16 @@
@Override
protected void createTestItems() {
List<TestStepBase> testSteps = new ArrayList<>();
- testSteps.add(new NonHdrDisplayTestStep(this));
- testSteps.add(new HdrDisplayTestStep(this));
- testSteps.add(new NoDisplayTestStep(this));
-
+ if (TvUtil.isHdmiSourceDevice()) {
+ // The device is a set-top box or a TV dongle
+ testSteps.add(new NonHdrDisplayTestStep(this));
+ testSteps.add(new HdrDisplayTestStep(this));
+ testSteps.add(new NoDisplayTestStep(this));
+ } else {
+ // The device is a TV Panel
+ testSteps.add(new TvPanelReportedTypesAreSupportedTestStep(this));
+ testSteps.add(new TvPanelSupportedTypesAreReportedTestStep(this));
+ }
mTestSequence = new TestSequence(this, testSteps);
mTestSequence.init();
}
@@ -207,4 +217,56 @@
done();
}
}
+
+ private static class TvPanelReportedTypesAreSupportedTestStep extends YesNoTestStep {
+ public TvPanelReportedTypesAreSupportedTestStep(TvAppVerifierActivity context) {
+ super(context, getInstructionText(context));
+ }
+
+ private static String getInstructionText(Context context) {
+ DisplayManager displayManager =
+ (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+
+ int[] hdrTypes = display.getHdrCapabilities().getSupportedHdrTypes();
+ String hdrTypesString;
+ if (hdrTypes.length == 0) {
+ hdrTypesString = context.getString(R.string.tv_none);
+ } else {
+ hdrTypesString =
+ Arrays.stream(hdrTypes)
+ .mapToObj(DisplayHdrCapabilitiesTestActivity::hdrTypeToString)
+ .collect(Collectors.joining(", "));
+ }
+
+ return context.getString(
+ R.string.tv_panel_hdr_types_reported_are_supported, hdrTypesString);
+ }
+ }
+
+ private static class TvPanelSupportedTypesAreReportedTestStep extends YesNoTestStep {
+ public TvPanelSupportedTypesAreReportedTestStep(TvAppVerifierActivity context) {
+ super(context, getInstructionText(context));
+ }
+
+ private static String getInstructionText(Context context) {
+ return context.getString(R.string.tv_panel_hdr_types_supported_are_reported);
+ }
+ }
+
+ private static String hdrTypeToString(@Display.HdrCapabilities.HdrType int type) {
+ switch (type) {
+ case Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION:
+ return "DOLBY_VISION";
+ case Display.HdrCapabilities.HDR_TYPE_HDR10:
+ return "HDR10";
+ case Display.HdrCapabilities.HDR_TYPE_HLG:
+ return "HLG";
+ case Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS:
+ return "HDR10_PLUS";
+ default:
+ Log.e(LOG_TAG, "Unknown HDR type " + type);
+ return "UNKNOWN";
+ }
+ }
}
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
index df3109f..10364af 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java
@@ -100,9 +100,12 @@
store.addResult(IS_ACTIVE_ADMIN, deviceAdminPackages.contains(pkg.packageName));
- final boolean isDefaultAccessibilityComponent = pkg.packageName.equals(
- defaultAccessibilityComponent.getPackageName()
- );
+ boolean isDefaultAccessibilityComponent = false;
+ if (defaultAccessibilityComponent != null) {
+ isDefaultAccessibilityComponent = pkg.packageName.equals(
+ defaultAccessibilityComponent.getPackageName()
+ );
+ }
store.addResult(DEFAULT_ACCESSIBILITY_SERVICE, isDefaultAccessibilityComponent);
String sha256_cert = PackageUtil.computePackageSignatureDigest(pkg.packageName);
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/AnrMonitor.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/AmMonitor.java
similarity index 67%
rename from common/device-side/util-axt/src/com/android/compatibility/common/util/AnrMonitor.java
rename to common/device-side/util-axt/src/com/android/compatibility/common/util/AmMonitor.java
index ac4879a..93bd1f1 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/AnrMonitor.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/AmMonitor.java
@@ -20,6 +20,7 @@
import android.os.ParcelFileDescriptor;
import android.os.SystemClock;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import java.io.BufferedOutputStream;
@@ -30,19 +31,26 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* A utility class interact with "am monitor"
*/
-public final class AnrMonitor {
- private static final String TAG = "AnrMonitor";
- private static final String WAIT_FOR_ANR = "Waiting after early ANR... available commands:";
- private static final String MONITOR_READY = "Monitoring activity manager... available commands:";
+public final class AmMonitor {
+ private static final String TAG = "AmMonitor";
+ public static final String WAIT_FOR_EARLY_ANR =
+ "Waiting after early ANR... available commands:";
+ public static final String WAIT_FOR_ANR =
+ "Waiting after ANR... available commands:";
+ public static final String WAIT_FOR_CRASHED =
+ "Waiting after crash... available commands:";
+ public static final String MONITOR_READY =
+ "Monitoring activity manager... available commands:";
/**
* Command for the {@link #sendCommand}: continue the process
*/
- public static final String CMD_CONTINUE = "k";
+ public static final String CMD_CONTINUE = "c";
/**
* Command for the {@link #sendCommand}: kill the process
@@ -63,12 +71,13 @@
private final PrintWriter mWritePrinter;
private final Thread mReaderThread;
+ private final ArraySet<String> mNotExpected = new ArraySet<>();
private final ArrayList<String> mPendingLines = new ArrayList<>();
/**
* Construct an instance of this class.
*/
- public AnrMonitor(final Instrumentation instrumentation) {
+ public AmMonitor(final Instrumentation instrumentation, final String[] notExpected) {
mInstrumentation = instrumentation;
ParcelFileDescriptor[] pfds = instrumentation.getUiAutomation()
.executeShellCommandRw("am monitor");
@@ -78,18 +87,12 @@
mWriteFd = pfds[1];
mWriteStream = new ParcelFileDescriptor.AutoCloseOutputStream(mWriteFd);
mWritePrinter = new PrintWriter(new BufferedOutputStream(mWriteStream));
+ if (notExpected != null) {
+ mNotExpected.addAll(Arrays.asList(notExpected));
+ }
mReaderThread = new ReaderThread();
mReaderThread.start();
- waitFor(3600000L, MONITOR_READY);
- }
-
- /**
- * Wait for the ANR.
- *
- * @return true if it was successful, false if it got a timeout.
- */
- public boolean waitFor(final long timeout) {
- return waitFor(timeout, WAIT_FOR_ANR);
+ waitFor(MONITOR_READY, 3600000L);
}
/**
@@ -97,15 +100,16 @@
*
* @return true if it was successful, false if it got a timeout.
*/
- private boolean waitFor(final long timeout, final String expected) {
+ public boolean waitFor(final String expected, final long timeout) {
final long waitUntil = SystemClock.uptimeMillis() + timeout;
synchronized (mPendingLines) {
while (true) {
while (mPendingLines.size() == 0) {
final long now = SystemClock.uptimeMillis();
if (now >= waitUntil) {
- Log.d(TAG, "Timed out waiting for next line: expected=" + expected);
- return false;
+ String msg = "Timed out waiting for next line: expected=" + expected;
+ Log.d(TAG, msg);
+ throw new IllegalStateException(msg);
}
try {
mPendingLines.wait(waitUntil - now);
@@ -115,6 +119,13 @@
final String line = mPendingLines.remove(0);
if (TextUtils.equals(line, expected)) {
return true;
+ } else if (TextUtils.equals(line, WAIT_FOR_EARLY_ANR)
+ || TextUtils.equals(line, WAIT_FOR_ANR)
+ || TextUtils.equals(line, WAIT_FOR_CRASHED)) {
+ // If we are getting any of the unexpected state,
+ // for example, get a crash while waiting for an ANR,
+ // it could be from another unrelated process, kill it directly.
+ sendCommand(CMD_KILL);
}
}
}
@@ -141,8 +152,10 @@
* @param cmd could be {@link #CMD_KILL}, {@link #CMD_QUIT} or {@link #CMD_CONTINUE}.
*/
public void sendCommand(final String cmd) {
- mWritePrinter.println(cmd);
- mWritePrinter.flush();
+ synchronized (mPendingLines) {
+ mWritePrinter.println(cmd);
+ mWritePrinter.flush();
+ }
}
private final class ReaderThread extends Thread {
@@ -152,6 +165,13 @@
String line;
while ((line = mReadReader.readLine()) != null) {
Log.i(TAG, "debug: " + line);
+ if (mNotExpected.contains(line)) {
+ // If we are getting any of the unexpected state,
+ // for example, get a crash while waiting for an ANR,
+ // it could be from another unrelated process, kill it directly.
+ sendCommand(CMD_KILL);
+ continue;
+ }
synchronized (mPendingLines) {
mPendingLines.add(line);
mPendingLines.notifyAll();
diff --git a/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java b/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
index f388cc3..29d718f 100644
--- a/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
+++ b/hostsidetests/adb/src/android/adb/cts/AdbHostTest.java
@@ -47,8 +47,8 @@
File check_ms_os_desc = copyResourceToTempFile("/check_ms_os_desc");
check_ms_os_desc.setExecutable(true);
- // ANDROID_SERIAL is already set correctly in our environment.
ProcessBuilder pb = new ProcessBuilder(check_ms_os_desc.getAbsolutePath());
+ pb.environment().put("ANDROID_SERIAL", getDevice().getSerialNumber());
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
index 699af1a..2f8790f 100644
--- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
+++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/CompatChangesSystemApiTest.java
@@ -43,6 +43,11 @@
installPackage(TEST_APK, true);
}
+ @Override
+ protected void tearDown() throws Exception {
+ uninstallPackage(TEST_PKG, true);
+ }
+
public void testIsChangeEnabled() throws Exception {
runDeviceCompatTest(TEST_PKG, ".CompatChangesTest", "isChangeEnabled_changeEnabled",
/*enabledChanges*/ImmutableSet.of(CTS_SYSTEM_API_CHANGEID),
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/README.md b/hostsidetests/appsecurity/res/pkgsigverify/README.md
new file mode 100644
index 0000000..278a960
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/README.md
@@ -0,0 +1,35 @@
+# pkgsigverify
+
+## Valid cases
+
+APKs in this directory are used by `PkgInstallSignatureVerificationTest`.
+See that class for additional information about their use.
+
+In general, they are differently signed forms of the tinyapp APK and follow
+the following naming scheme:
+
+`${version}-with-${signing-algorithm}-${OID-params}-${keysize}-${extra_info}`
+
+where some fields may not be present, but have the following meaning:
+
+- version: denotes which APK Signature Scheme (v1, v2, and/or v3) was used
+- signing-algorithm: indicates how the signature was generated
+- OID-params: PKI object identifiers indicating how to use the provided key
+ to generate the signature. See, e.g. rfc3279 and rfc4055.
+- keysize: the size of the key used for signing
+- extra_info: additional notes to distinguish packages. Currently this denotes
+ which test certificates were used to generate a proof-of-rotation object,
+ if one exists, included in the signing block of the APK, as well as the
+ capabilities of those certificates according to APK Signature Scheme v3.
+
+Generation of these apks was performed using the `apksigner` command-line tool,
+which lives at `tools/apksig/src/apksigner/java/com/android/apksigner/` in the
+android source tree. Please refer to the usage instructions there for how to
+sign APKs using different keystores, providers, etc.
+
+## Invalid cases
+
+Some of the APKs in this directory were generated by modifying the apksig library (see
+README in tools/apksig/) to create invalid or unsupported outcomes. When possible, their usage is
+preceded by a description of how `apksig` was modified, and the commit should explicitly show how
+`apksig` was modified.
\ No newline at end of file
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/README.txt b/hostsidetests/appsecurity/res/pkgsigverify/README.txt
deleted file mode 100644
index f30eec5..0000000
--- a/hostsidetests/appsecurity/res/pkgsigverify/README.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-APKs in this directory are used by PkgInstallSignatureVerificationTest.
-See that class for additional information about their use.
-
-In general, they are differently signed forms of the tinyapp APK and follow
-the following naming scheme:
-
-${version}-with-${signing-algorithm}-${OID-params}-${keysize}-${extra_info}
-
-where some fields may not be present, but have the following meaning:
-
-version: denotes which APK Signature Scheme (v1, v2, and/or v3) was used
-signing-algorithm: indicates how the signature was generated
-OID-params: PKI object identifiers indicating how to use the provided key
- to generate the signature. See, e.g. rfc3279 and rfc4055.
-keysize: the size of the key used for signing
-extra_info: additional notes to distinguish packages. Currently this denotes
- which test certificates were used to generate a proof-of-rotation object,
- if one exists, included in the signing block of the APK, as well as the
- capabilities of those certificates according to APK Signature Scheme v3.
-
-Generation of these apks was performed using the apksigner command-line tool,
-which lives at tools/apksig/src/apksigner/java/com/android/apksigner/ in the
-android source tree. Please refer to the usage instructions there for how to
-sign APKs using different keystores, providers, etc. In particular, some of
-the APKs in this directory were generated by modifying the apksig library (see
-README in tools/apksig/) to create invalid or unsupported outcomes.
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv2digest.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv2digest.apk
new file mode 100644
index 0000000..9839f78
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv2digest.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv2digest.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv2digest.apk.idsig
new file mode 100644
index 0000000..07f2f0d
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv2digest.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv4signature.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv4signature.apk
new file mode 100644
index 0000000..9839f78
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv4signature.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv4signature.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv4signature.apk.idsig
new file mode 100644
index 0000000..b00c87a
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2-badv4signature.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv2v3digest.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv2v3digest.apk
new file mode 100644
index 0000000..ead1ef8
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv2v3digest.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv2v3digest.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv2v3digest.apk.idsig
new file mode 100644
index 0000000..5632589
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv2v3digest.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv4signature.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv4signature.apk
new file mode 100644
index 0000000..ead1ef8
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv4signature.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv4signature.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv4signature.apk.idsig
new file mode 100644
index 0000000..3de76f9
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v2v3-badv4signature.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv3digest.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv3digest.apk
new file mode 100644
index 0000000..710c7e7
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv3digest.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv3digest.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv3digest.apk.idsig
new file mode 100644
index 0000000..83061cb
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv3digest.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv4signature.apk b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv4signature.apk
new file mode 100644
index 0000000..710c7e7
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv4signature.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv4signature.apk.idsig b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv4signature.apk.idsig
new file mode 100644
index 0000000..e0a68db
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v4-digest-v3-badv4signature.apk.idsig
Binary files differ
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableFeatureConsistentTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableFeatureConsistentTest.java
deleted file mode 100644
index 48fddc7..0000000
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableFeatureConsistentTest.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.appsecurity.cts;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
-import static android.appsecurity.cts.AdoptableHostTest.FEATURE_ADOPTABLE_STORAGE;
-import android.platform.test.annotations.AppModeFull;
-
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Set of tests that verify behavior of adopted storage media's consistency between the feature
- * flag and what we sniffed from the underlying fstab.
- */
-@RunWith(DeviceJUnit4ClassRunner.class)
-@AppModeFull(reason = "Instant applications can only be installed on internal storage")
-public class AdoptableFeatureConsistentTest extends BaseHostJUnit4Test {
-
- private String mHasAdoptable;
-
- @Before
- public void setUp() throws Exception {
- // Caches the initial state of adoptable feature to restore after the tests
- mHasAdoptable = getDevice().executeShellCommand("sm has-adoptable").trim();
- }
-
- @After
- public void tearDown() throws Exception {
- // Restores the initial cache value
- getDevice().executeShellCommand("sm set-force-adoptable" + mHasAdoptable);
- }
-
- @Test
- public void testFeatureTrue() throws Exception {
- getDevice().executeShellCommand("sm set-force-adoptable true");
- checkConsistency();
- }
-
- @Test
- public void testFeatureFalse() throws Exception {
- getDevice().executeShellCommand("sm set-force-adoptable false");
- checkConsistency();
- }
-
- private void checkConsistency() throws Exception {
- // Reboots the device and blocks until the boot complete flag is set.
- getDevice().rebootUntilOnline();
- assertTrue("Device failed to boot", getDevice().waitForBootComplete(120000));
-
- final boolean hasFeature = getDevice().hasFeature(FEATURE_ADOPTABLE_STORAGE);
- final boolean hasFstab = Boolean.parseBoolean(getDevice()
- .executeShellCommand("sm has-adoptable").trim());
- if (hasFeature != hasFstab) {
- fail("Inconsistent adoptable storage status; feature claims " + hasFeature
- + " but fstab claims " + hasFstab);
- }
- }
-}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index 7717623..927f9f5 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -51,7 +51,6 @@
public static final String FEATURE_ADOPTABLE_STORAGE = "feature:android.software.adoptable_storage";
- private boolean mHasAdoptableInitialState;
private String mListVolumesInitialState;
@Before
@@ -59,16 +58,12 @@
// Start all possible users to make sure their storage is unlocked
Utils.prepareMultipleUsers(getDevice(), Integer.MAX_VALUE);
+ // Users are starting, wait for all volumes are ready
+ waitForVolumeReady();
+
// Initial state of all volumes
mListVolumesInitialState = getDevice().executeShellCommand("sm list-volumes");
- // TODO(b/146491109): Revert this change before shipping and find long-term solution.
- // Caches the initial state of adoptable feature and sets it to true (if not already set)
- mHasAdoptableInitialState = Boolean.parseBoolean(
- getDevice().executeShellCommand("sm has-adoptable").trim());
- if (!mHasAdoptableInitialState) {
- setForceAdoptable();
- }
getDevice().uninstallPackage(PKG);
// Enable a virtual disk to give us the best shot at being able to pass
@@ -123,9 +118,19 @@
CLog.w("Volume state is not recovered: " + result);
}
}
- // Restores the initial cache value (if it is different)
- if (!mHasAdoptableInitialState) {
- getDevice().executeShellCommand("sm set-force-adoptable false");
+ }
+
+ /**
+ * Ensure that we have consistency between the feature flag and what we
+ * sniffed from the underlying fstab.
+ */
+ @Test
+ public void testFeatureConsistent() throws Exception {
+ final boolean hasFeature = hasFeature();
+ final boolean hasFstab = hasFstab();
+ if (hasFeature != hasFstab) {
+ fail("Inconsistent adoptable storage status; feature claims " + hasFeature
+ + " but fstab claims " + hasFstab);
}
}
@@ -134,7 +139,7 @@
int attempt = 0;
boolean noCheckingEjecting = false;
String result = "";
- while (!noCheckingEjecting && attempt++ < 20) {
+ while (!noCheckingEjecting && attempt++ < 60) {
result = getDevice().executeShellCommand("sm list-volumes");
noCheckingEjecting = !result.contains("ejecting") && !result.contains("checking");
Thread.sleep(100);
@@ -216,18 +221,6 @@
}
}
- private void setForceAdoptable() throws Exception {
- getDevice().executeShellCommand("sm set-force-adoptable true");
- int attempt = 0;
- boolean hasAdoptable = false;
- while (!hasAdoptable && attempt++ < 5) {
- Thread.sleep(1000);
- hasAdoptable = Boolean.parseBoolean(getDevice()
- .executeShellCommand("sm has-adoptable").trim());
- }
- assertTrue(hasAdoptable);
- }
-
private void verifyPrimaryInternal(String diskId) throws Exception {
// Write some data to shared storage
new InstallMultiple().addFile(APK).run();
@@ -255,6 +248,7 @@
runDeviceTests(PKG, CLASS, "testPrimaryUnmounted");
getDevice().executeShellCommand("sm mount " + vol.volId);
waitForInstrumentationReady();
+ waitForVolumeReady();
runDeviceTests(PKG, CLASS, "testPrimaryAdopted");
runDeviceTests(PKG, CLASS, "testPrimaryDataRead");
@@ -305,6 +299,7 @@
runDeviceTests(PKG, CLASS, "testPrimaryUnmounted");
getDevice().executeShellCommand("sm mount " + vol.volId);
waitForInstrumentationReady();
+ waitForVolumeReady();
runDeviceTests(PKG, CLASS, "testPrimaryAdopted");
runDeviceTests(PKG, CLASS, "testPrimaryDataRead");
@@ -375,6 +370,7 @@
// Kick through a remount cycle, which should purge the adopted app
getDevice().executeShellCommand("sm mount " + vol.volId);
waitForInstrumentationReady();
+ waitForVolumeReady();
runDeviceTests(PKG, CLASS, "testDataInternal");
boolean didThrow = false;
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index 2a356ea..fb8a4f2 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -39,7 +39,6 @@
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -215,7 +214,6 @@
* Verify that app without REQUEST_INSTALL_PACKAGES can't access obb
* directories belonging to other apps.
*/
- @Ignore("Re-enable as part of b/148918640")
@Test
public void testCantAccessOtherObbDirs() throws Exception {
try {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
index 05a76b4..23e95f1 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
@@ -241,7 +241,7 @@
assertInstallSucceedsForEach(
"v2-only-with-rsa-pss-sha512-%s.apk",
RSA_KEY_NAMES_2048_AND_LARGER // 1024-bit key is too short for PSS with SHA-512
- );
+ );
}
public void testInstallV1SignatureOnlyDoesNotVerify() throws Exception {
@@ -828,6 +828,54 @@
assertInstallV4Succeeds("v4-digest-v2v3.apk");
}
+ public void testInstallV4WithV2SignerDoesNotVerify() throws Exception {
+ // V4 is only enabled on devices with Incremental feature
+ if (!hasIncrementalFeature()) {
+ return;
+ }
+
+ // APKs generated with:
+ // apksigner sign -v2-signing-enabled true --v3-signing-enabled false --v4-signing-enabled
+
+ // Malformed v4 signature - first byte of v4 signing_info.signature is flipped
+ assertInstallV4FailsWithError("v4-digest-v2-badv4signature.apk", "did not verify");
+ // Malformed digest - first byte of v4 signing_info.apk_digest is flipped
+ assertInstallV4FailsWithError("v4-digest-v2-badv2digest.apk", "did not verify");
+ }
+
+ public void testInstallV4WithV3SignerDoesNotVerify() throws Exception {
+ // V4 is only enabled on devices with Incremental feature
+ if (!hasIncrementalFeature()) {
+ return;
+ }
+
+ // APKs generated with:
+ // apksigner sign -v2-signing-enabled false --v3-signing-enabled true --v4-signing-enabled
+
+ // Malformed v4 signature - first byte of v4 signing_info.signature is flipped
+ assertInstallV4FailsWithError("v4-digest-v3-badv4signature.apk", "did not verify");
+
+ // Malformed digest - first byte of v4 signing_info.apk_digest is flipped
+ assertInstallV4FailsWithError("v4-digest-v3-badv3digest.apk", "did not verify");
+
+ }
+
+ public void testInstallV4WithV2V3SignerDoesNotVerify() throws Exception {
+ // V4 is only enabled on devices with Incremental feature
+ if (!hasIncrementalFeature()) {
+ return;
+ }
+
+ // APKs generated with:
+ // apksigner sign -v2-signing-enabled true --v3-signing-enabled true --v4-signing-enabled
+
+ // Malformed v4 signature - first byte of v4 signing_info.signature is flipped
+ assertInstallV4FailsWithError("v4-digest-v2v3-badv4signature.apk", "did not verify");
+
+ // Malformed digest - first byte of v4 signing_info.apk_digest is flipped
+ assertInstallV4FailsWithError("v4-digest-v2v3-badv2v3digest.apk", "did not verify");
+ }
+
private boolean hasIncrementalFeature() throws DeviceNotAvailableException {
return getDevice().hasFeature("android.software.incremental_delivery");
}
@@ -871,6 +919,19 @@
}
}
+ private void assertInstallV4FailsWithError(String apkFilenameInResources, String errorSubstring)
+ throws Exception {
+ String installResult = installV4PackageFromResource(apkFilenameInResources);
+ if (installResult.equals("Success\n")) {
+ fail("Install of " + apkFilenameInResources + " succeeded but was expected to fail"
+ + " with \"" + errorSubstring + "\"");
+ }
+ assertContains(
+ "Install failure message of " + apkFilenameInResources,
+ errorSubstring,
+ installResult);
+ }
+
private void assertInstallFailsWithError(
String apkFilenameInResources, String errorSubstring) throws Exception {
String installResult = installPackageFromResource(apkFilenameInResources);
@@ -961,7 +1022,7 @@
}
}
- private String pushFileToRemote(File localFile) throws DeviceNotAvailableException{
+ private String pushFileToRemote(File localFile) throws DeviceNotAvailableException {
String remotePath = "/data/local/tmp/pkginstalltest-" + localFile.getName();
getDevice().pushFile(localFile, remotePath);
return remotePath;
diff --git a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java
index 620fed1..ee3a683 100644
--- a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java
+++ b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppA/src/com/android/cts/appdataisolation/appa/AppATests.java
@@ -188,6 +188,18 @@
mDevice.waitForIdle();
}
+ private String replacePackageAWithPackageB(String path) {
+ return path.replace(mContext.getPackageName(), APPB_PKG);
+ }
+
+ private void testCanNotAccessAppBExternalDirs() {
+ String appBExternalDir = replacePackageAWithPackageB(
+ mContext.getExternalFilesDir("").getParentFile().getAbsolutePath());
+ String appBObbDir = replacePackageAWithPackageB(mContext.getObbDir().getAbsolutePath());
+ assertDirDoesNotExist(appBExternalDir);
+ assertDirDoesNotExist(appBObbDir);
+ }
+
@Test
public void testAppAUnlockDeviceAndVerifyCeDeExternalDataExist() throws Exception {
@@ -221,5 +233,9 @@
testAppAExternalDirsDoExist();
testAppACurProfileDataAccessible();
testAppARefProfileDataNotAccessible();
+
+ // Verify after unlocking device, app a has still no access to app b dir.
+ testCannotAccessAppBDataDir();
+ testCanNotAccessAppBExternalDirs();
}
}
diff --git a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java
index 0f069af..c1e3a53 100644
--- a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java
+++ b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/AppB/src/com/android/cts/appdataisolation/appb/AppBTests.java
@@ -65,7 +65,7 @@
@Test
public void testCanNotAccessAppAExternalDirs() {
String appAExternalDir = replacePackageBWithPackageA(
- mContext.getExternalFilesDir("").getAbsolutePath());
+ mContext.getExternalFilesDir("").getParentFile().getAbsolutePath());
String appAObbDir = replacePackageBWithPackageA(mContext.getObbDir().getAbsolutePath());
assertDirDoesNotExist(appAExternalDir);
assertDirDoesNotExist(appAObbDir);
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/AndroidManifest.xml
index 09bac4c..d3c5dcc 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/UserAppTest/AndroidManifest.xml
@@ -17,6 +17,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.userapptest" >
+ <queries>
+ <intent>
+ <action android:name="com.android.cts.instantappusertest.QUERY" />
+ </intent>
+ </queries>
+
<application
android:label="@string/app_name" >
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/appsecurity/test-apps/PackageAccessApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/PackageAccessApp/AndroidManifest.xml
index 0e416d9..a8a6cad 100644
--- a/hostsidetests/appsecurity/test-apps/PackageAccessApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/PackageAccessApp/AndroidManifest.xml
@@ -16,6 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.packageaccessapp">
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
+
<application>
<uses-library android:name="android.test.runner" />
</application>
diff --git a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/AndroidManifest.xml
index d8bc983..243fa23 100644
--- a/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/CrossProfileTestApps/CrossProfileEnabledNoPermsApp/AndroidManifest.xml
@@ -20,6 +20,9 @@
<uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29"/>
+ <!-- We need to request the permission, which is denied in the test. -->
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
+
<application
android:crossProfile="true">
<receiver android:name=".CrossProfileEnabledNoPermsAppReceiver">
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DeviceFeatureUtils.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DeviceFeatureUtils.java
new file mode 100644
index 0000000..797bf97
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DeviceFeatureUtils.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceandprofileowner;
+
+import static com.google.common.truth.Truth.assertThat;
+
+public class DeviceFeatureUtils extends BaseDeviceAdminTest {
+
+ public void testHasFactoryResetProtectionPolicy() {
+ assertThat(mDevicePolicyManager.isFactoryResetProtectionPolicySupported()).isTrue();
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/DummyApps/dummyapp1/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DummyApps/dummyapp1/AndroidManifest.xml
index a757984..00fe83d 100644
--- a/hostsidetests/devicepolicy/app/DummyApps/dummyapp1/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DummyApps/dummyapp1/AndroidManifest.xml
@@ -30,5 +30,8 @@
<action android:name="android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED" />
</intent-filter>
</receiver>
+ <activity
+ android:name="android.app.Activity">
+ </activity>
</application>
</manifest>
diff --git a/hostsidetests/devicepolicy/app/DummyApps/dummyapp2/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DummyApps/dummyapp2/AndroidManifest.xml
index feb0cb7..97c7d76 100644
--- a/hostsidetests/devicepolicy/app/DummyApps/dummyapp2/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DummyApps/dummyapp2/AndroidManifest.xml
@@ -30,5 +30,8 @@
<action android:name="android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED" />
</intent-filter>
</receiver>
+ <activity
+ android:name="android.app.Activity">
+ </activity>
</application>
</manifest>
diff --git a/hostsidetests/devicepolicy/app/DummyApps/dummyapp3/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DummyApps/dummyapp3/AndroidManifest.xml
index a6f8648..dbd0778 100644
--- a/hostsidetests/devicepolicy/app/DummyApps/dummyapp3/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DummyApps/dummyapp3/AndroidManifest.xml
@@ -30,5 +30,8 @@
<action android:name="android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED" />
</intent-filter>
</receiver>
+ <activity
+ android:name="android.app.Activity">
+ </activity>
</application>
</manifest>
diff --git a/hostsidetests/devicepolicy/app/DummyApps/dummyapp4/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DummyApps/dummyapp4/AndroidManifest.xml
index 19f62c9..7001a87 100644
--- a/hostsidetests/devicepolicy/app/DummyApps/dummyapp4/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DummyApps/dummyapp4/AndroidManifest.xml
@@ -30,5 +30,8 @@
<action android:name="android.content.pm.action.CAN_INTERACT_ACROSS_PROFILES_CHANGED" />
</intent-filter>
</receiver>
+ <activity
+ android:name="android.app.Activity">
+ </activity>
</application>
</manifest>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/UserManagerTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/UserManagerTest.java
index 8ae404d..6673533 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/UserManagerTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/UserManagerTest.java
@@ -16,17 +16,35 @@
package com.android.cts.managedprofile;
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.UiAutomation;
+import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import android.test.AndroidTestCase;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.util.HashSet;
+import java.util.List;
+
public class UserManagerTest extends AndroidTestCase {
private UserManager mUserManager;
+ private UiAutomation mUiAutomation;
@Override
protected void setUp() throws Exception {
super.setUp();
mUserManager = mContext.getSystemService(UserManager.class);
+ mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mUiAutomation.dropShellPermissionIdentity();
+ super.tearDown();
}
public void testIsManagedProfileReturnsTrue() {
@@ -36,4 +54,37 @@
public void testIsManagedProfileReturnsFalse() {
assertFalse(mUserManager.isManagedProfile());
}
+
+ public void testGetAllProfiles() {
+ List<UserHandle> profiles = mUserManager.getAllProfiles();
+ assertThat(profiles).hasSize(2);
+ assertThat(profiles).contains(Process.myUserHandle());
+ }
+
+ public void testCreateProfile_managedProfile() {
+ mUiAutomation.adoptShellPermissionIdentity("android.permission.CREATE_USERS");
+
+ UserHandle newProfile = mUserManager.createProfile("testProfile1",
+ UserManager.USER_TYPE_PROFILE_MANAGED, new HashSet<String>());
+ assertThat(newProfile).isNotNull();
+
+ List<UserHandle> profiles = mUserManager.getAllProfiles();
+ assertThat(profiles).contains(newProfile);
+ }
+
+ /** This test should be run as the managed profile
+ * by com.android.cts.devicepolicy.ManagedProfileTest
+ */
+ public void testIsProfileReturnsTrue_runAsProfile() {
+ mUiAutomation.adoptShellPermissionIdentity("android.permission.INTERACT_ACROSS_USERS");
+ assertThat(mUserManager.isProfile()).isTrue();
+ }
+
+ /** This test should be run as the parent profile
+ * by com.android.cts.devicepolicy.ManagedProfileTest
+ */
+ public void testIsProfileReturnsFalse_runAsPrimary() {
+ mUiAutomation.adoptShellPermissionIdentity("android.permission.INTERACT_ACROSS_USERS");
+ assertThat(mUserManager.isProfile()).isFalse();
+ }
}
diff --git a/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp b/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
index ab82993..bf67cc0 100644
--- a/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
+++ b/hostsidetests/devicepolicy/app/SimpleSmsApp/Android.bp
@@ -15,13 +15,10 @@
android_test {
name: "SimpleSmsApp",
sdk_version: "test_current",
-
- srcs: ["src/**/*.kt", "src/**/*.java"],
-
+ srcs: ["src/**/*.java"],
static_libs: [
- "compatibility-device-util-axt",
+ "cts-devicepolicy-suspensionchecker",
],
-
test_suites: [
"cts",
],
diff --git a/hostsidetests/devicepolicy/app/SimpleSmsApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SimpleSmsApp/AndroidManifest.xml
index 6cd7616..7c38b6d 100644
--- a/hostsidetests/devicepolicy/app/SimpleSmsApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/SimpleSmsApp/AndroidManifest.xml
@@ -21,10 +21,6 @@
<uses-permission android:name="android.permission.READ_SMS"/>
<application android:label="SimpleSmsApp">
- <activity
- android:name="android.app.Activity"
- android:exported="true"/>
-
<!-- BroadcastReceiver that listens for incoming SMS messages -->
<receiver android:name="android.telephony.cts.sms.SmsReceiver"
android:permission="android.permission.BROADCAST_SMS">
@@ -43,7 +39,8 @@
</receiver>
<!-- Activity that allows the user to send new SMS/MMS messages -->
- <activity android:name="android.app.Activity" >
+ <activity android:name="android.app.Activity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />
@@ -71,5 +68,13 @@
</service>
</application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.telephony.cts.sms.simplesmsapp">
+ <meta-data
+ android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener"/>
+ </instrumentation>
</manifest>
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index d0e2deb..fd6109c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -1059,7 +1059,7 @@
@Test
public void testSetMeteredDataDisabledPackages() throws Exception {
- if (!mHasFeature) {
+ if (!mHasFeature || !hasDeviceFeature("android.hardware.wifi")) {
return;
}
installAppAsUser(METERED_DATA_APP_APK, mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 9b313a0..93cc6eb 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -66,8 +66,8 @@
private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp";
private static final String SIMPLE_APP_ACTIVITY = SIMPLE_APP_PKG + ".SimpleActivity";
- protected static final String SIMPLE_SMS_APP_PKG = "android.telephony.cts.sms.simplesmsapp";
- protected static final String SIMPLE_SMS_APP_APK = "SimpleSmsApp.apk";
+ private static final String SIMPLE_SMS_APP_PKG = "android.telephony.cts.sms.simplesmsapp";
+ private static final String SIMPLE_SMS_APP_APK = "SimpleSmsApp.apk";
private static final String WIFI_CONFIG_CREATOR_PKG =
"com.android.cts.deviceowner.wificonfigcreator";
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
index 4fe211f..639c6a3 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileCrossProfileTest.java
@@ -26,6 +26,8 @@
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.assertMetricsLogged;
import static com.android.cts.devicepolicy.metrics.DevicePolicyEventLogVerifier.isStatsdEnabled;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
@@ -42,7 +44,12 @@
import org.junit.Test;
+import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
public class ManagedProfileCrossProfileTest extends BaseManagedProfileTest {
@@ -56,6 +63,16 @@
/** From {@code android.app.AppOpsManager#MODE_DEFAULT}. */
private static final int MODE_DEFAULT = 3;
+ // The apps whose app-ops are maintained and unset are defined by the device-side test.
+ private static final Set<String> UNSET_CROSS_PROFILE_PACKAGES =
+ Sets.newHashSet(
+ DUMMY_APP_3_PKG,
+ DUMMY_APP_4_PKG);
+ private static final Set<String> MAINTAINED_CROSS_PROFILE_PACKAGES =
+ Sets.newHashSet(
+ DUMMY_APP_1_PKG,
+ DUMMY_APP_2_PKG);
+
@LargeTest
@Test
public void testCrossProfileIntentFilters() throws Exception {
@@ -500,19 +517,10 @@
"testSetCrossProfilePackages_sendsBroadcastWhenResettingAppOps_noAsserts");
waitForBroadcastIdle();
- // The apps whose app-ops are maintained and unset are defined by the device-side test.
- final Set<String> unsetCrossProfilePackages =
- Sets.newHashSet(
- DUMMY_APP_3_PKG,
- DUMMY_APP_4_PKG);
- final Set<String> maintainedCrossProfilePackages =
- Sets.newHashSet(
- DUMMY_APP_1_PKG,
- DUMMY_APP_2_PKG);
assertDummyAppsReceivedCanInteractAcrossProfilesChangedBroadcast(
- unsetCrossProfilePackages);
+ UNSET_CROSS_PROFILE_PACKAGES);
assertDummyAppsDidNotReceiveCanInteractAcrossProfilesChangedBroadcast(
- maintainedCrossProfilePackages);
+ MAINTAINED_CROSS_PROFILE_PACKAGES);
}
/** Assumes that logcat is clear before running the test. */
@@ -572,6 +580,68 @@
.build());
}
+ @Test
+ public void testSetCrossProfilePackages_killsApps() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAllDummyApps();
+ launchAllDummyAppsInBothProfiles();
+ Map<String, List<String>> maintainedPackagesPids = getPackagesPids(
+ MAINTAINED_CROSS_PROFILE_PACKAGES);
+ Map<String, List<String>> unsetPackagesPids = getPackagesPids(UNSET_CROSS_PROFILE_PACKAGES);
+
+ runWorkProfileDeviceTest(
+ ".CrossProfileTest",
+ "testSetCrossProfilePackages_resetsAppOps_noAsserts");
+
+ for (String packageName : MAINTAINED_CROSS_PROFILE_PACKAGES) {
+ assertAppRunningInBothProfiles(packageName, maintainedPackagesPids.get(packageName));
+ }
+ for (String packageName : UNSET_CROSS_PROFILE_PACKAGES) {
+ assertAppKilledInBothProfiles(packageName, unsetPackagesPids.get(packageName));
+ }
+ }
+
+ private Map<String, List<String>> getPackagesPids(Set<String> packages) throws Exception {
+ Map<String, List<String>> pids = new HashMap<>();
+ for (String packageName : packages) {
+ pids.put(packageName, Arrays.asList(getAppPid(packageName).split(" ")));
+ }
+ return pids;
+ }
+
+ private void launchAllDummyAppsInBothProfiles() throws Exception {
+ launchAllDummyAppsForUser(mParentUserId);
+ launchAllDummyAppsForUser(mProfileUserId);
+ }
+
+ private void launchAllDummyAppsForUser(int userId) throws Exception {
+ final String dummyActivity = "android.app.Activity";
+ startActivityAsUser(userId, DUMMY_APP_1_PKG, dummyActivity);
+ startActivityAsUser(userId, DUMMY_APP_2_PKG, dummyActivity);
+ startActivityAsUser(userId, DUMMY_APP_3_PKG, dummyActivity);
+ startActivityAsUser(userId, DUMMY_APP_4_PKG, dummyActivity);
+ }
+
+ private void assertAppRunningInBothProfiles(String packageName, List<String> pids)
+ throws Exception {
+ Set<String> currentPids = new HashSet<>(
+ Arrays.asList(getAppPid(packageName).split(" ")));
+ assertThat(currentPids).containsAllIn(pids);
+ }
+
+ private void assertAppKilledInBothProfiles(String packageName, List<String> pids)
+ throws Exception {
+ Set<String> currentPids = new HashSet<>(
+ Arrays.asList(getAppPid(packageName).split(" ")));
+ assertThat(currentPids).containsNoneIn(pids);
+ }
+
+ private String getAppPid(String packageName) throws Exception {
+ return getDevice().executeShellCommand(String.format("pidof %s", packageName)).trim();
+ }
+
private void runCrossProfileCalendarTestsWhenWhitelistedAndEnabled() throws Exception {
try {
// Setup. Add the test package into cross-profile calendar whitelist, enable
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 55a552b..e452368 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -673,6 +673,40 @@
}
}
+ @Test
+ public void testCanGetProfiles() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ // getAllProfiles should contain both the primary and profile
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".UserManagerTest",
+ "testGetAllProfiles", mPrimaryUserId);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".UserManagerTest",
+ "testGetAllProfiles", mProfileUserId);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".UserManagerTest",
+ "testIsProfileReturnsFalse_runAsPrimary", mPrimaryUserId);
+
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".UserManagerTest",
+ "testIsProfileReturnsTrue_runAsProfile", mProfileUserId);
+ }
+
+ @Test
+ public void testCanCreateProfile() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ // remove pre-created profile
+ removeUser(mProfileUserId);
+
+ // create profile from installed app
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".UserManagerTest",
+ "testCreateProfile_managedProfile", mPrimaryUserId);
+ }
+
private void changeUserRestrictionOrFail(String key, boolean value, int userId)
throws DeviceNotAvailableException {
changeUserRestrictionOrFail(key, value, userId, MANAGED_PROFILE_PKG);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 0b3b46c..98e1c59 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -250,9 +250,18 @@
return;
}
+ try {
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".DeviceFeatureUtils",
+ "testHasFactoryResetProtectionPolicy", mUserId);
+ } catch (Exception e) {
+ // Unable to continue running tests because factory reset protection policy is not
+ // supported on the device
+ return;
+ }
+
assertMetricsLogged(getDevice(), () -> {
- runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".FactoryResetProtectionPolicyTest", mUserId);
- }, new DevicePolicyEventWrapper.Builder(EventId.SET_FACTORY_RESET_PROTECTION_VALUE)
+ runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".FactoryResetProtectionPolicyTest", mUserId);
+ }, new DevicePolicyEventWrapper.Builder(EventId.SET_FACTORY_RESET_PROTECTION_VALUE)
.setAdminPackageName(DEVICE_ADMIN_PKG)
.build());
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index 7f6398c..ea90988 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -52,6 +52,8 @@
private static final String DUMMY_IME_APK = "DummyIme.apk";
private static final String DUMMY_IME_PKG = "com.android.cts.dummyime";
private static final String DUMMY_IME_COMPONENT = DUMMY_IME_PKG + "/.DummyIme";
+ private static final String SIMPLE_SMS_APP_PKG = "android.telephony.cts.sms.simplesmsapp";
+ private static final String SIMPLE_SMS_APP_APK = "SimpleSmsApp.apk";
private static final String DUMMY_LAUNCHER_APK = "DummyLauncher.apk";
private static final String DUMMY_LAUNCHER_COMPONENT =
"com.android.cts.dummylauncher/android.app.Activity";
@@ -442,6 +444,35 @@
}
@Test
+ public void testPersonalAppsSuspensionSms() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ // Install an SMS app and make it the default.
+ installAppAsUser(SIMPLE_SMS_APP_APK, mPrimaryUserId);
+ addSmsRole(SIMPLE_SMS_APP_PKG, mPrimaryUserId);
+ try {
+ setPersonalAppsSuspended(true);
+ // Default sms app should not be suspended.
+ assertCanStartPersonalApp(SIMPLE_SMS_APP_PKG, true);
+ setPersonalAppsSuspended(false);
+ } finally {
+ removeSmsRole(SIMPLE_SMS_APP_PKG, mPrimaryUserId);
+ }
+ }
+
+ private void addSmsRole(String app, int userId) throws Exception {
+ executeShellCommand(String.format(
+ "cmd role add-role-holder --user %d android.app.role.SMS %s", userId, app));
+ }
+
+ private void removeSmsRole(String app, int userId) throws Exception {
+ executeShellCommand(String.format(
+ "cmd role remove-role-holder --user %d android.app.role.SMS %s", userId, app));
+ }
+
+ @Test
public void testPersonalAppsSuspensionIme() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
index 11a7aec..9069e1e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java
@@ -235,33 +235,31 @@
private void installCrossProfileApps()
throws FileNotFoundException, DeviceNotAvailableException {
- installCrossProfileApp(ENABLED_TEST_APK);
- installCrossProfileApp(USER_ENABLED_TEST_APK);
- installCrossProfileApp(NOT_ENABLED_TEST_APK);
- installCrossProfileApp(ENABLED_NO_PERMS_TEST_APK);
+ installCrossProfileApp(ENABLED_TEST_APK, /* grantPermissions= */ true);
+ installCrossProfileApp(USER_ENABLED_TEST_APK, /* grantPermissions= */ true);
+ installCrossProfileApp(NOT_ENABLED_TEST_APK, /* grantPermissions= */ true);
+ installCrossProfileApp(ENABLED_NO_PERMS_TEST_APK, /* grantPermissions= */ false);
}
private void enableCrossProfileAppsOp() throws DeviceNotAvailableException {
enableCrossProfileAppsOp(ENABLED_TEST_PACKAGE, mPrimaryUserId);
- enableCrossProfileAppsOp(USER_ENABLED_TEST_PACKAGE, mPrimaryUserId);
- enableCrossProfileAppsOp(NOT_ENABLED_TEST_PACKAGE, mPrimaryUserId);
enableCrossProfileAppsOp(ENABLED_NO_PERMS_TEST_PACKAGE, mPrimaryUserId);
}
- private void installCrossProfileApp(String apkName)
+ private void installCrossProfileApp(String apkName, boolean grantPermissions)
throws FileNotFoundException, DeviceNotAvailableException {
- installAppAsUser(apkName, mPrimaryUserId);
- installAppAsUser(apkName, mProfileId);
+ installAppAsUser(apkName, grantPermissions, mPrimaryUserId);
+ installAppAsUser(apkName, grantPermissions, mProfileId);
}
private void enableCrossProfileAppsOp(String packageName, int userId)
throws DeviceNotAvailableException {
getDevice().executeShellCommand(
String.format("appops set --user %s %s android:interact_across_profiles 0",
- userId, packageName));
+ userId, packageName));
assertThat(getDevice().executeShellCommand(
String.format("appops get --user %s %s android:interact_across_profiles",
- userId, packageName))).contains("INTERACT_ACROSS_PROFILES: allow");
+ userId, packageName))).contains("INTERACT_ACROSS_PROFILES: allow");
}
private Map<String, String> createParams(int targetUserId) throws Exception {
diff --git a/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java b/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
index b0facec..23aca24 100644
--- a/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
+++ b/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
@@ -23,6 +23,7 @@
public class NetworkPolicyTestsPreparer implements ITargetPreparer {
private ITestDevice mDevice;
+ private boolean mOriginalAirplaneModeEnabled;
private String mOriginalAppStandbyEnabled;
private String mOriginalBatteryStatsConstants;
private final static String KEY_STABLE_CHARGING_DELAY_MS = "battery_charged_delay_ms";
@@ -39,15 +40,29 @@
setBatteryStatsConstants(
KEY_STABLE_CHARGING_DELAY_MS + "=" + DESIRED_STABLE_CHARGING_DELAY_MS);
LogUtil.CLog.d("Original battery_saver_constants: " + mOriginalBatteryStatsConstants);
+
+ mOriginalAirplaneModeEnabled = getAirplaneModeEnabled();
+ // Turn off airplane mode in case another test left the device in that state.
+ setAirplaneModeEnabled(false);
+ LogUtil.CLog.d("Original airplane mode state: " + mOriginalAirplaneModeEnabled);
}
@Override
public void tearDown(TestInformation testInformation, Throwable e)
throws DeviceNotAvailableException {
+ setAirplaneModeEnabled(mOriginalAirplaneModeEnabled);
setAppStandbyEnabled(mOriginalAppStandbyEnabled);
setBatteryStatsConstants(mOriginalBatteryStatsConstants);
}
+ private void setAirplaneModeEnabled(boolean enable) throws DeviceNotAvailableException {
+ executeCmd("cmd connectivity airplane-mode " + (enable ? "enable" : "disable"));
+ }
+
+ private boolean getAirplaneModeEnabled() throws DeviceNotAvailableException {
+ return "enabled".equals(executeCmd("cmd connectivity airplane-mode").trim());
+ }
+
private void setAppStandbyEnabled(String appStandbyEnabled) throws DeviceNotAvailableException {
if ("null".equals(appStandbyEnabled)) {
executeCmd("settings delete global app_standby_enabled");
diff --git a/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java b/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java
index b33bfdc..689a095 100644
--- a/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java
@@ -518,6 +518,42 @@
}
}
+ @AppModeFull(
+ reason = "getDeclaredSharedLibraries() requires ACCESS_SHARED_LIBRARIES permission")
+ public void testGetDeclaredSharedLibraries() throws Exception {
+ getDevice().uninstallPackage(STATIC_LIB_CONSUMER1_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_CONSUMER2_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER1_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER2_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER4_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER_RECURSIVE_PKG);
+ try {
+ // Install library dependency
+ assertNull(install(STATIC_LIB_PROVIDER_RECURSIVE_APK));
+ // Install the first library
+ assertNull(install(STATIC_LIB_PROVIDER1_APK));
+ // Install the second library
+ assertNull(install(STATIC_LIB_PROVIDER2_APK));
+ // Install the third library
+ assertNull(install(STATIC_LIB_PROVIDER4_APK));
+ // Install the first client
+ assertNull(install(STATIC_LIB_CONSUMER1_APK));
+ // Install the second client
+ assertNull(install(STATIC_LIB_CONSUMER2_APK));
+ // Ensure declared libraries are properly reported
+ runDeviceTests(STATIC_LIB_CONSUMER1_PKG,
+ "android.os.lib.consumer1.UseSharedLibraryTest",
+ "testDeclaredSharedLibrariesProperlyReported");
+ } finally {
+ getDevice().uninstallPackage(STATIC_LIB_CONSUMER1_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_CONSUMER2_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER1_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER2_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER4_PKG);
+ getDevice().uninstallPackage(STATIC_LIB_PROVIDER_RECURSIVE_PKG);
+ }
+ }
+
@AppModeInstant
public void testAppCanSeeOnlyLibrariesItDependOnInstantMode() throws Exception {
mInstantMode = true;
diff --git a/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java b/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java
index 3637da0c..24ffae9 100644
--- a/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java
+++ b/hostsidetests/os/test-apps/StaticSharedLibConsumerApp1/src/android/os/lib/consumer1/UseSharedLibraryTest.java
@@ -25,6 +25,7 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VersionedPackage;
import android.os.lib.provider.R;
@@ -161,6 +162,52 @@
}
@Test
+ public void testDeclaredSharedLibrariesProperlyReported() throws Exception {
+ PackageManager packageManager = InstrumentationRegistry.getContext().getPackageManager();
+ List<SharedLibraryInfo> sharedLibs = SystemUtil.runWithShellPermissionIdentity(() ->
+ packageManager.getDeclaredSharedLibraries(STATIC_LIB_PROVIDER_PKG, 0));
+
+ assertNotNull(sharedLibs);
+
+ boolean firstLibFound = false;
+ boolean secondLibFound = false;
+ boolean thirdLibFound = false;
+
+ for (SharedLibraryInfo sharedLib : sharedLibs) {
+ assertEquals(LIB_NAME, sharedLib.getName());
+ VersionedPackage declaringPackage = sharedLib.getDeclaringPackage();
+ assertEquals(STATIC_LIB_PROVIDER_PKG, declaringPackage.getPackageName());
+ assertSame(SharedLibraryInfo.TYPE_STATIC, sharedLib.getType());
+
+ List<VersionedPackage> dependentPackages = sharedLib.getDependentPackages();
+ final long versionCode = sharedLib.getLongVersion();
+ if (versionCode == 1) {
+ firstLibFound = true;
+ assertSame(1L, declaringPackage.getLongVersionCode());
+ assertSame(1, dependentPackages.size());
+ VersionedPackage dependentPackage = dependentPackages.get(0);
+ assertEquals(STATIC_LIB_CONSUMER1_PKG, dependentPackage.getPackageName());
+ assertSame(1L, dependentPackage.getLongVersionCode());
+ } else if (versionCode == 2) {
+ secondLibFound = true;
+ assertSame(4L, declaringPackage.getLongVersionCode());
+ assertTrue(dependentPackages.isEmpty());
+ } else if (versionCode == 5) {
+ thirdLibFound = true;
+ assertSame(5L, declaringPackage.getLongVersionCode());
+ assertSame(1, dependentPackages.size());
+ VersionedPackage dependentPackage = dependentPackages.get(0);
+ assertEquals(STATIC_LIB_CONSUMER2_PKG, dependentPackage.getPackageName());
+ assertSame(2L, dependentPackage.getLongVersionCode());
+ }
+ }
+
+ assertTrue("Did not find lib " + LIB_NAME + " version 1", firstLibFound);
+ assertTrue("Did not find lib " + LIB_NAME + " version 4", secondLibFound);
+ assertTrue("Did not find lib " + LIB_NAME + " version 5", thirdLibFound);
+ }
+
+ @Test
public void testAppCanSeeOnlyLibrariesItDependOn() throws Exception {
// Make sure we see only the lib we depend on via getting its package info
PackageInfo libPackageInfo = InstrumentationRegistry.getInstrumentation()
diff --git a/hostsidetests/security/src/android/security/cts/MetadataEncryptionTest.java b/hostsidetests/security/src/android/security/cts/MetadataEncryptionTest.java
new file mode 100644
index 0000000..92eafe3
--- /dev/null
+++ b/hostsidetests/security/src/android/security/cts/MetadataEncryptionTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.compatibility.common.util.CddTest;
+import com.android.compatibility.common.util.PropertyUtil;
+
+/**
+ * Host-side test for metadata encryption. This is a host-side test because
+ * the "ro.crypto.metadata.enabled" property is not exposed to apps.
+ */
+public class MetadataEncryptionTest extends DeviceTestCase implements IDeviceTest {
+ private ITestDevice mDevice;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setDevice(ITestDevice device) {
+ super.setDevice(device);
+ mDevice = device;
+ }
+
+ /**
+ * Test that metadata encryption is enabled.
+ * To enable metadata encryption, see
+ * https://source.android.com/security/encryption/metadata
+ *
+ * @throws Exception
+ */
+ @CddTest(requirement="9.9.3/C-1-5")
+ public void testMetadataEncryptionIsEnabled() throws Exception {
+ if (PropertyUtil.getFirstApiLevel(mDevice) <= 29) {
+ return; // Requirement does not apply to devices running Q or earlier
+ }
+ assertTrue("Metadata encryption must be enabled",
+ mDevice.getBooleanProperty("ro.crypto.metadata.enabled", false));
+ }
+}
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
index 07313bb..c41493b 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/ApexShimValidationTest.java
@@ -23,7 +23,6 @@
import android.platform.test.annotations.LargeTest;
-import com.android.tradefed.device.PackageInfo;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
@@ -33,6 +32,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
/**
* Tests to validate that only what is considered a correct shim apex can be installed.
*
@@ -49,8 +52,10 @@
public class ApexShimValidationTest extends BaseHostJUnit4Test {
private static final String SHIM_APEX_PACKAGE_NAME = "com.android.apex.cts.shim";
- private static final String SHIM_APK_PACKAGE_NAME = "com.android.cts.ctsshim";
- private static final String SHIM_PRIV_APK_PACKAGE_NAME = "com.android.cts.priv.ctsshim";
+ private static final String SHIM_APK_CODE_PATH_PREFIX = "/apex/" + SHIM_APEX_PACKAGE_NAME + "/";
+
+ private static final List<String> ALLOWED_SHIM_PACKAGE_NAMES = Arrays.asList(
+ "com.android.cts.ctsshim", "com.android.cts.priv.ctsshim");
/**
* Runs the given phase of a test by calling into the device.
@@ -92,23 +97,12 @@
}
@Test
- public void testShimApkIsPreInstalledInShimApex() throws Exception {
- PackageInfo shimApkPackageInfo = getDevice().getAppPackageInfo(SHIM_APK_PACKAGE_NAME);
- assertWithMessage("CTSShim APK is not pre-installed").that(
- shimApkPackageInfo).isNotNull();
- boolean isShimApkInShimApex = shimApkPackageInfo.getCodePath()
- .startsWith("/apex/" + SHIM_APEX_PACKAGE_NAME + "/app/");
- assertWithMessage("The active version of CTSShim APK does not come from "
- + "Shim APEX").that(isShimApkInShimApex).isTrue();
-
- PackageInfo shimPrivApkPackageInfo = getDevice()
- .getAppPackageInfo(SHIM_PRIV_APK_PACKAGE_NAME);
- assertWithMessage("CTSPrivShim APK is not pre-installed").that(
- shimPrivApkPackageInfo).isNotNull();
- boolean isPrivShimApkInShimApex = shimPrivApkPackageInfo.getCodePath()
- .startsWith("/apex/" + SHIM_APEX_PACKAGE_NAME + "/priv-app/");
- assertWithMessage("The active version of CTSPrivShim APK does not come "
- + "from Shim APEX").that(isPrivShimApkInShimApex).isTrue();
+ public void testPackageNameOfShimApkIsAllowed() throws Exception {
+ final List<String> shimPackages = getDevice().getAppPackageInfos().stream()
+ .filter(pkg -> pkg.getCodePath().startsWith(SHIM_APK_CODE_PATH_PREFIX))
+ .map(pkg -> pkg.getPackageName()).collect(Collectors.toList());
+ assertWithMessage("Packages in the shim apex are not allowed")
+ .that(shimPackages).containsExactlyElementsIn(ALLOWED_SHIM_PACKAGE_NAMES);
}
@Test
diff --git a/hostsidetests/stagedinstall/testdata/apex/corrupted_b146895998.apex b/hostsidetests/stagedinstall/testdata/apex/corrupted_b146895998.apex
index fc6b474..1d02697 100644
--- a/hostsidetests/stagedinstall/testdata/apex/corrupted_b146895998.apex
+++ b/hostsidetests/stagedinstall/testdata/apex/corrupted_b146895998.apex
Binary files differ
diff --git a/hostsidetests/statsd/AndroidTest.xml b/hostsidetests/statsd/AndroidTest.xml
index 2da1662..4598f33 100644
--- a/hostsidetests/statsd/AndroidTest.xml
+++ b/hostsidetests/statsd/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Config for CTS Statsd host test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="statsd" />
+ <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
<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" />
diff --git a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 25727f1..6b3b981 100644
--- a/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/hostsidetests/statsd/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -709,6 +709,49 @@
}
@Test
+ public void testSliceByWakelockState() {
+ int uid = Process.myUid();
+ int whatAtomId = 9_998;
+ int wakelockType = PowerManager.PARTIAL_WAKE_LOCK;
+ String tag = "StatsdPartialWakelock";
+
+ Context context = InstrumentationRegistry.getContext();
+ PowerManager pm = context.getSystemService(PowerManager.class);
+ PowerManager.WakeLock wl = pm.newWakeLock(wakelockType, tag);
+
+ wl.acquire();
+ sleep(500);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ wl.acquire();
+ sleep(500);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ wl.release();
+ sleep(500);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ wl.release();
+ sleep(500);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ writeSliceByWakelockStateChangedAtom(whatAtomId, uid, wakelockType, tag);
+ }
+
+ private static void writeSliceByWakelockStateChangedAtom(int atomId, int firstUid,
+ int field2, String field3) {
+ final StatsEvent.Builder builder = StatsEvent.newBuilder()
+ .setAtomId(atomId)
+ .writeAttributionChain(new int[] {firstUid}, new String[] {"tag1"})
+ .writeInt(field2)
+ .writeString(field3)
+ .usePooledBuffer();
+
+ StatsLog.write(builder.build());
+ }
+
+
+ @Test
public void testWakelockLoad() {
final int NUM_THREADS = 16;
CountDownLatch latch = new CountDownLatch(NUM_THREADS);
@@ -839,9 +882,6 @@
private static final int NETWORK_TIMEOUT_MILLIS = 15000;
private static final String HTTPS_HOST_URL =
"https://connectivitycheck.gstatic.com/generate_204";
- // Minimum and Maximum of iterations of exercise host, @see #doGenerateNetworkTraffic.
- private static final int MIN_EXERCISE_HOST_ITERATIONS = 1;
- private static final int MAX_EXERCISE_HOST_ITERATIONS = 19;
private void doGenerateNetworkTraffic(@NonNull Context context,
@NetworkCapabilities.Transport int transport) throws InterruptedException {
@@ -862,45 +902,9 @@
final long startTime = SystemClock.elapsedRealtime();
try {
- // Since history of network stats only have 2 hours of resolution, when it is
- // being queried, service will assume that history network stats has uniform
- // distribution and return a fraction of network stats that is originally
- // subject to 2 hours. To be specific:
- // <returned network stats> = <total network stats> * <duration> / 2 hour,
- // assuming the duration can fit in a 2 hours bucket.
- // In the other hand, in statsd, the network stats is queried since boot,
- // that means in order to assert non-zero packet counts, either the test should
- // be run after enough time since boot, or the packet counts generated here
- // should be enough. That is to say:
- // <total packet counts> * <up time> / 2 hour >= 1,
- // or
- // iterations >= 2 hour / (<up time> * <packets per iteration>)
- // Thus, iterations can be chosen based on the factors above to make this
- // function generate enough packets in each direction to accommodate enough
- // packet counts for a fraction of history bucket.
- final double iterations = (TimeUnit.HOURS.toMillis(2) / startTime / 7);
- // While just enough iterations are going to make the test flaky, add a 20%
- // buffer to stabilize it and make sure it's in a reasonable range, so it won't
- // consumes more than 100kb of traffic, or generates 0 byte of traffic.
- final int augmentedIterations =
- (int) Math.max(iterations * 1.2, MIN_EXERCISE_HOST_ITERATIONS);
- if (augmentedIterations > MAX_EXERCISE_HOST_ITERATIONS) {
- throw new IllegalStateException("Exceeded max allowed iterations"
- + ", iterations=" + augmentedIterations
- + ", uptime=" + TimeUnit.MILLISECONDS.toSeconds(startTime) + "s");
- }
-
- for (int i = 0; i < augmentedIterations; i++) {
- // By observing results of "dumpsys netstats --uid", typically the single
- // run of the https request below generates 4200/1080 rx/tx bytes with
- // around 7/9 rx/tx packets.
- // This blocks the thread of NetworkCallback, thus no other event
- // can be processed before return.
- exerciseRemoteHost(cm, network, new URL(HTTPS_HOST_URL));
- }
+ exerciseRemoteHost(cm, network, new URL(HTTPS_HOST_URL));
Log.i(TAG, "exerciseRemoteHost successful in " + (SystemClock.elapsedRealtime()
- - startTime) + " ms with iterations=" + augmentedIterations
- + ", uptime=" + TimeUnit.MILLISECONDS.toSeconds(startTime) + "s");
+ - startTime) + " ms");
} catch (Exception e) {
Log.e(TAG, "exerciseRemoteHost failed in " + (SystemClock.elapsedRealtime()
- startTime) + " ms: " + e);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
index e49f3dd..c6cd042 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/AtomTestCase.java
@@ -68,9 +68,15 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+import java.util.Queue;
import java.util.Set;
import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@@ -112,6 +118,11 @@
public static final String FEATURE_INCREMENTAL_DELIVERY =
"android.software.incremental_delivery";
+ // Telephony phone types
+ public static final int PHONE_TYPE_GSM = 1;
+ public static final int PHONE_TYPE_CDMA = 2;
+ public static final int PHONE_TYPE_CDMA_LTE = 6;
+
protected static final int WAIT_TIME_SHORT = 500;
protected static final int WAIT_TIME_LONG = 2_000;
@@ -223,9 +234,11 @@
// TODO(b/134091167): Fix bluetooth source name issue in Auto platform.
.addAllowedLogSource("com.android.bluetooth")
.addAllowedLogSource("AID_LMKD")
+ .addAllowedLogSource("AID_RADIO")
.addAllowedLogSource("AID_ROOT")
.addAllowedLogSource("AID_STATSD")
.addAllowedLogSource(DeviceAtomTestCase.DEVICE_SIDE_TEST_PACKAGE)
+ .addDefaultPullPackages("AID_RADIO")
.addDefaultPullPackages("AID_SYSTEM");
}
@@ -1006,6 +1019,105 @@
getDevice().executeShellCommand("cmd connectivity airplane-mode disable");
}
+ /**
+ * Returns a list of fields and values for {@code className} from {@link TelephonyDebugService}
+ * output.
+ *
+ * <p>Telephony dumpsys output does not support proto at the moment. This method provides
+ * limited support for parsing its output. Specifically, it does not support arrays or
+ * multi-line values.
+ */
+ private List<Map<String, String>> getTelephonyDumpEntries(String className) throws Exception {
+ // Matches any line with indentation, except for lines with only spaces
+ Pattern indentPattern = Pattern.compile("^(\\s*)[^ ].*$");
+ // Matches pattern for class, e.g. " Phone:"
+ Pattern classNamePattern = Pattern.compile("^(\\s*)" + Pattern.quote(className) + ":.*$");
+ // Matches pattern for key-value pairs, e.g. " mPhoneId=1"
+ Pattern keyValuePattern = Pattern.compile("^(\\s*)([a-zA-Z]+[a-zA-Z0-9_]*)\\=(.+)$");
+ String response =
+ getDevice().executeShellCommand("dumpsys activity service TelephonyDebugService");
+ Queue<String> responseLines = new LinkedList<>(Arrays.asList(response.split("[\\r\\n]+")));
+
+ List<Map<String, String>> results = new ArrayList<>();
+ while (responseLines.peek() != null) {
+ Matcher matcher = classNamePattern.matcher(responseLines.poll());
+ if (matcher.matches()) {
+ final int classIndentLevel = matcher.group(1).length();
+ final Map<String, String> instanceEntries = new HashMap<>();
+ while (responseLines.peek() != null) {
+ // Skip blank lines
+ matcher = indentPattern.matcher(responseLines.peek());
+ if (responseLines.peek().length() == 0 || !matcher.matches()) {
+ responseLines.poll();
+ continue;
+ }
+ // Finish (without consuming the line) if already parsed past this instance
+ final int indentLevel = matcher.group(1).length();
+ if (indentLevel <= classIndentLevel) {
+ break;
+ }
+ // Parse key-value pair if it belongs to the instance directly
+ matcher = keyValuePattern.matcher(responseLines.poll());
+ if (indentLevel == classIndentLevel + 1 && matcher.matches()) {
+ instanceEntries.put(matcher.group(2), matcher.group(3));
+ }
+ }
+ results.add(instanceEntries);
+ }
+ }
+ return results;
+ }
+
+ protected int getActiveSimSlotCount() throws Exception {
+ List<Map<String, String>> slots = getTelephonyDumpEntries("UiccSlot");
+ long count = slots.stream().filter(slot -> "true".equals(slot.get("mActive"))).count();
+ return Math.toIntExact(count);
+ }
+
+ /**
+ * Returns the upper bound of active SIM profile count.
+ *
+ * <p>The value is an upper bound as eSIMs without profiles are also counted in.
+ */
+ protected int getActiveSimCountUpperBound() throws Exception {
+ List<Map<String, String>> slots = getTelephonyDumpEntries("UiccSlot");
+ long count = slots.stream().filter(slot ->
+ "true".equals(slot.get("mActive"))
+ && "CARDSTATE_PRESENT".equals(slot.get("mCardState"))).count();
+ return Math.toIntExact(count);
+ }
+
+ /**
+ * Returns the upper bound of active eSIM profile count.
+ *
+ * <p>The value is an upper bound as eSIMs without profiles are also counted in.
+ */
+ protected int getActiveEsimCountUpperBound() throws Exception {
+ List<Map<String, String>> slots = getTelephonyDumpEntries("UiccSlot");
+ long count = slots.stream().filter(slot ->
+ "true".equals(slot.get("mActive"))
+ && "CARDSTATE_PRESENT".equals(slot.get("mCardState"))
+ && "true".equals(slot.get("mIsEuicc"))).count();
+ return Math.toIntExact(count);
+ }
+
+ protected boolean hasGsmPhone() throws Exception {
+ // Not using log entries or ServiceState in the dump since they may or may not be present,
+ // which can make the test flaky
+ return getTelephonyDumpEntries("Phone").stream()
+ .anyMatch(phone ->
+ String.format("%d", PHONE_TYPE_GSM).equals(phone.get("getPhoneType()")));
+ }
+
+ protected boolean hasCdmaPhone() throws Exception {
+ // Not using log entries or ServiceState in the dump due to the same reason as hasGsmPhone()
+ return getTelephonyDumpEntries("Phone").stream()
+ .anyMatch(phone ->
+ String.format("%d", PHONE_TYPE_CDMA).equals(phone.get("getPhoneType()"))
+ || String.format("%d", PHONE_TYPE_CDMA_LTE)
+ .equals(phone.get("getPhoneType()")));
+ }
+
// Checks that a timestamp has been truncated to be a multiple of 5 min
protected void assertTimestampIsTruncated(long timestampNs) {
long fiveMinutesInNs = NS_PER_SEC * 5 * 60;
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
index 0221852..7c83515 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/HostAtomTests.java
@@ -23,6 +23,7 @@
import android.platform.test.annotations.RestrictedBuildTest;
import android.server.DeviceIdleModeEnum;
import android.view.DisplayStateEnum;
+import android.telephony.NetworkTypeEnum;
import com.android.internal.os.StatsdConfigProto.StatsdConfig;
import com.android.os.AtomsProto.AppBreadcrumbReported;
@@ -30,6 +31,8 @@
import com.android.os.AtomsProto.BatterySaverModeStateChanged;
import com.android.os.AtomsProto.BuildInformation;
import com.android.os.AtomsProto.ConnectivityStateChanged;
+import com.android.os.AtomsProto.SimSlotState;
+import com.android.os.AtomsProto.SupportedRadioAccessFamily;
import com.android.os.StatsLog.ConfigMetricsReportList;
import com.android.os.StatsLog.EventMetricData;
@@ -51,6 +54,28 @@
private static final String WAKE_LOCK_FILE = "/proc/wakelocks";
private static final String WAKE_SOURCES_FILE = "/d/wakeup_sources";
+ // Bitmask of radio access technologies that all GSM phones should at least partially support
+ protected static final long NETWORK_TYPE_BITMASK_GSM_ALL =
+ (1 << (NetworkTypeEnum.NETWORK_TYPE_GSM_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_GPRS_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_EDGE_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_UMTS_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSDPA_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSUPA_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSPA_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSPAP_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_TD_SCDMA_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_LTE_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_LTE_CA_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_NR_VALUE - 1));
+ // Bitmask of radio access technologies that all CDMA phones should at least partially support
+ protected static final long NETWORK_TYPE_BITMASK_CDMA_ALL =
+ (1 << (NetworkTypeEnum.NETWORK_TYPE_CDMA_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_1XRTT_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_EVDO_0_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_EVDO_A_VALUE - 1))
+ | (1 << (NetworkTypeEnum.NETWORK_TYPE_EHRPD_VALUE - 1));
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -665,4 +690,66 @@
assertThat(foundConnectEvent).isTrue();
assertThat(foundDisconnectEvent).isTrue();
}
+
+ public void testSimSlotState() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+ if (!hasFeature(FEATURE_TELEPHONY, true)) {
+ return;
+ }
+
+ StatsdConfig.Builder config = createConfigBuilder();
+ addGaugeAtomWithDimensions(config, Atom.SIM_SLOT_STATE_FIELD_NUMBER, null);
+ uploadConfig(config);
+
+ Thread.sleep(WAIT_TIME_LONG);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
+
+ List<Atom> data = getGaugeMetricDataList();
+ assertThat(data).isNotEmpty();
+ SimSlotState atom = data.get(0).getSimSlotState();
+ // NOTE: it is possible for devices with telephony support to have no SIM at all
+ assertThat(atom.getActiveSlotCount()).isEqualTo(getActiveSimSlotCount());
+ assertThat(atom.getSimCount()).isAtMost(getActiveSimCountUpperBound());
+ assertThat(atom.getEsimCount()).isAtMost(getActiveEsimCountUpperBound());
+ // Above assertions do no necessarily enforce the following, since some are upper bounds
+ assertThat(atom.getActiveSlotCount()).isAtLeast(atom.getSimCount());
+ assertThat(atom.getSimCount()).isAtLeast(atom.getEsimCount());
+ assertThat(atom.getEsimCount()).isAtLeast(0);
+ // For GSM phones, at least one slot should be active even if there is no card
+ if (hasGsmPhone()) {
+ assertThat(atom.getActiveSlotCount()).isAtLeast(1);
+ }
+ }
+
+ public void testSupportedRadioAccessFamily() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+ if (!hasFeature(FEATURE_TELEPHONY, true)) {
+ return;
+ }
+
+ StatsdConfig.Builder config = createConfigBuilder();
+ addGaugeAtomWithDimensions(config, Atom.SUPPORTED_RADIO_ACCESS_FAMILY_FIELD_NUMBER, null);
+ uploadConfig(config);
+
+ Thread.sleep(WAIT_TIME_LONG);
+ setAppBreadcrumbPredicate();
+ Thread.sleep(WAIT_TIME_LONG);
+
+ List<Atom> data = getGaugeMetricDataList();
+ assertThat(data).isNotEmpty();
+ SupportedRadioAccessFamily atom = data.get(0).getSupportedRadioAccessFamily();
+ if (hasGsmPhone()) {
+ assertThat(atom.getNetworkTypeBitmask() & NETWORK_TYPE_BITMASK_GSM_ALL)
+ .isNotEqualTo(0L);
+ }
+ if (hasCdmaPhone()) {
+ assertThat(atom.getNetworkTypeBitmask() & NETWORK_TYPE_BITMASK_CDMA_ALL)
+ .isNotEqualTo(0L);
+ }
+ }
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index fb08d2d..2d980aa 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -35,6 +35,7 @@
import com.android.os.AtomsProto.AppCrashOccurred;
import com.android.os.AtomsProto.AppOps;
import com.android.os.AtomsProto.AppStartOccurred;
+import com.android.os.AtomsProto.AppUsageEventOccurred;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.AttributedAppOps;
import com.android.os.AtomsProto.AttributionNode;
@@ -87,6 +88,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Function;
/**
* Statsd atom tests that are done via app, for atoms that report a uid.
@@ -108,6 +110,11 @@
private static final String TEST_INSTALL_PACKAGE =
"com.android.cts.device.statsd.emptyapp";
private static final String TEST_REMOTE_DIR = "/data/local/tmp/statsd";
+ private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
+
+ private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
+ private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when app starting/stopping.
+ private static final int STATSD_REPORT_WAIT_TIME_MS = 500; // make sure statsd finishes log.
@Override
protected void setUp() throws Exception {
@@ -2104,6 +2111,32 @@
getDevice().uninstallPackage(TEST_INSTALL_PACKAGE);
}
+ public void testAppForegroundBackground() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+ Set<Integer> onStates = new HashSet<>(Arrays.asList(
+ AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
+ Set<Integer> offStates = new HashSet<>(Arrays.asList(
+ AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
+
+ List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
+ createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false); // False: does not use attribution.
+ Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
+
+ getDevice().executeShellCommand(String.format(
+ "am start -n '%s' -e %s %s",
+ "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
+ "action", ACTION_SHOW_APPLICATION_OVERLAY));
+ final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
+ Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
+
+ List<EventMetricData> data = getEventMetricDataList();
+ Function<Atom, Integer> appUsageStateFunction = atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
+ popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.
+ assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
+ }
+
private AtomsProto.PackageInstallerV2Reported installPackageUsingV2AndGetReport(
String[] apkNames) throws Exception {
createAndUploadConfig(Atom.PACKAGE_INSTALLER_V2_REPORTED_FIELD_NUMBER);
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
index aaf94af..81eb4f5 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -27,6 +27,7 @@
import com.android.os.AtomsProto.AppBreadcrumbReported;
import com.android.os.AtomsProto.AttributionNode;
import com.android.os.AtomsProto.BleScanStateChanged;
+import com.android.os.AtomsProto.WakelockStateChanged;
import com.android.os.StatsLog;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
@@ -38,6 +39,8 @@
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
public class CountMetricsTests extends DeviceAtomTestCase {
@@ -425,4 +428,136 @@
.sum();
assertThat(totalCount).isEqualTo(2);
}
+
+ public void testSlicedStateCountMetricNoReset() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+
+ int whatMatcherId = 3;
+ int stateId = 4;
+ int onStateGroupId = 5;
+ int offStateGroupId = 6;
+
+ // Atom 9998 {
+ // repeated AttributionNode attribution_node = 1;
+ // optional WakeLockLevelEnum type = 2;
+ // optional string tag = 3;
+ // }
+ int whatAtomId = 9_998;
+
+ StatsdConfigProto.AtomMatcher whatMatcher =
+ MetricsUtils.getAtomMatcher(whatAtomId)
+ .setId(whatMatcherId)
+ .build();
+
+ StatsdConfigProto.State state = StatsdConfigProto.State.newBuilder()
+ .setId(stateId)
+ .setAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
+ .setMap(StatsdConfigProto.StateMap.newBuilder()
+ .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
+ .setGroupId(onStateGroupId)
+ .addValue(WakelockStateChanged.State.ACQUIRE_VALUE)
+ .addValue(WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE)
+ )
+ .addGroup(StatsdConfigProto.StateMap.StateGroup.newBuilder()
+ .setGroupId(offStateGroupId)
+ .addValue(WakelockStateChanged.State.RELEASE_VALUE)
+ .addValue(WakelockStateChanged.State.CHANGE_RELEASE_VALUE)
+ )
+ )
+ .build();
+
+ StatsdConfigProto.MetricStateLink stateLink = StatsdConfigProto.MetricStateLink.newBuilder()
+ .setStateAtomId(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
+ .setFieldsInWhat(FieldMatcher.newBuilder()
+ .setField(whatAtomId)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(1)
+ .setPosition(Position.FIRST)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AttributionNode.UID_FIELD_NUMBER)
+ )
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(2)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(3)
+ )
+ )
+ .setFieldsInState(FieldMatcher.newBuilder()
+ .setField(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(WakelockStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER)
+ .setPosition(Position.FIRST)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AttributionNode.UID_FIELD_NUMBER)
+ )
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(WakelockStateChanged.TYPE_FIELD_NUMBER)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(WakelockStateChanged.TAG_FIELD_NUMBER)
+ )
+ )
+ .build();
+
+ StatsdConfigProto.StatsdConfig.Builder builder = createConfigBuilder()
+ .addCountMetric(StatsdConfigProto.CountMetric.newBuilder()
+ .setId(MetricsUtils.COUNT_METRIC_ID)
+ .setBucket(StatsdConfigProto.TimeUnit.CTS)
+ .setWhat(whatMatcherId)
+ .addSliceByState(stateId)
+ .addStateLink(stateLink)
+ )
+ .addAtomMatcher(whatMatcher)
+ .addState(state);
+ uploadConfig(builder);
+
+ runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSliceByWakelockState");
+
+ StatsLogReport metricReport = getStatsLogReport();
+ LogUtil.CLog.d("Got the following stats log report: \n" + metricReport.toString());
+ assertThat(metricReport.getMetricId()).isEqualTo(MetricsUtils.COUNT_METRIC_ID);
+ assertThat(metricReport.hasCountMetrics()).isTrue();
+
+ StatsLogReport.CountMetricDataWrapper dataWrapper = metricReport.getCountMetrics();
+ assertThat(dataWrapper.getDataCount()).isEqualTo(2);
+
+
+ List<CountMetricData> sortedDataList = IntStream.range(0, dataWrapper.getDataCount())
+ .mapToObj(i -> {
+ CountMetricData data = dataWrapper.getData(i);
+ assertWithMessage("Unexpected SliceByState count for data[%s]", "" + i)
+ .that(data.getSliceByStateCount()).isEqualTo(1);
+ return data;
+ })
+ .sorted((data1, data2) ->
+ Long.compare(data1.getSliceByState(0).getGroupId(),
+ data2.getSliceByState(0).getGroupId())
+ )
+ .collect(Collectors.toList());
+
+ CountMetricData data = sortedDataList.get(0);
+ assertThat(data.getSliceByState(0).getAtomId())
+ .isEqualTo(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER);
+ assertThat(data.getSliceByState(0).getGroupId())
+ .isEqualTo(onStateGroupId);
+ long totalCount = data.getBucketInfoList().stream()
+ .mapToLong(CountBucketInfo::getCount)
+ .sum();
+ assertThat(totalCount).isEqualTo(6);
+
+ data = sortedDataList.get(1);
+ assertThat(data.getSliceByState(0).getAtomId())
+ .isEqualTo(Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER);
+ assertThat(data.getSliceByState(0).getGroupId())
+ .isEqualTo(offStateGroupId);
+ totalCount = data.getBucketInfoList().stream()
+ .mapToLong(CountBucketInfo::getCount)
+ .sum();
+ assertThat(totalCount).isEqualTo(3);
+ }
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
index 85cc7d9..95dbaa6 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/validation/BatteryStatsValidationTests.java
@@ -51,6 +51,7 @@
@Override
protected void tearDown() throws Exception {
plugInUsb();
+ super.tearDown();
}
/*
diff --git a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
index 2c75a7f..0da168b 100644
--- a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
+++ b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java
@@ -40,6 +40,7 @@
/**
* Host side CTS tests verifying userspace reboot functionality.
*/
+@RequiresDevice
@RunWith(DeviceJUnit4ClassRunner.class)
public class UserspaceRebootHostTest extends BaseHostJUnit4Test {
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
index 6d7d95c..3551205 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/IdleConstraintTest.java
@@ -45,12 +45,19 @@
private JobInfo.Builder mBuilder;
private UiDevice mUiDevice;
+ private String mInitialDisplayTimeout;
+
@Override
public void setUp() throws Exception {
super.setUp();
mBuilder = new JobInfo.Builder(STATE_JOB_ID, kJobServiceComponent);
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+
+ // Make sure the screen doesn't turn off when the test turns it on.
+ mInitialDisplayTimeout = mUiDevice.executeShellCommand(
+ "settings get system screen_off_timeout");
+ mUiDevice.executeShellCommand("settings put system screen_off_timeout 300000");
}
@Override
@@ -58,7 +65,12 @@
mJobScheduler.cancel(STATE_JOB_ID);
// Put device back in to normal operation.
toggleScreenOn(true);
- setCarMode(false);
+ if (isCarModeSupported()) {
+ setCarMode(false);
+ }
+
+ mUiDevice.executeShellCommand(
+ "settings put system screen_off_timeout " + mInitialDisplayTimeout);
super.tearDown();
}
@@ -153,6 +165,12 @@
verifyActiveState();
}
+ private boolean isCarModeSupported() {
+ // TVs don't support car mode.
+ return !getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK_ONLY);
+ }
+
/**
* Check if dock state is supported.
*/
@@ -227,6 +245,10 @@
* Ensure car mode is considered active.
*/
public void testCarModePreventsIdle() throws Exception {
+ if (!isCarModeSupported()) {
+ return;
+ }
+
toggleScreenOn(false);
setCarMode(true);
@@ -239,6 +261,10 @@
}
private void runIdleJobStartsOnlyWhenIdle() throws Exception {
+ if (!isCarModeSupported()) {
+ return;
+ }
+
toggleScreenOn(true);
kTestEnvironment.setExpectedExecutions(0);
@@ -276,6 +302,10 @@
}
public void testIdleJobStartsOnlyWhenIdle_carEndsIdle() throws Exception {
+ if (!isCarModeSupported()) {
+ return;
+ }
+
runIdleJobStartsOnlyWhenIdle();
setCarMode(true);
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index a1a5b67..c065b1e 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -73,6 +73,8 @@
private static final long POLL_INTERVAL = 500;
private static final long DEFAULT_WAIT_TIMEOUT = 2000;
private static final long SHELL_TIMEOUT = 3_000;
+ // TODO: mark Settings.System.SCREEN_OFF_TIMEOUT as @TestApi
+ private static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
enum Bucket {
ACTIVE,
@@ -99,6 +101,7 @@
private boolean mInitialWiFiState;
private boolean mInitialAirplaneModeState;
private String mInitialJobSchedulerConstants;
+ private String mInitialDisplayTimeout;
private TestAppInterface mTestAppInterface;
@@ -155,6 +158,10 @@
// Make sure test jobs can run regardless of bucket.
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.JOB_SCHEDULER_CONSTANTS, "min_ready_non_active_jobs_count=0");
+ // Make sure the screen doesn't turn off when the test turns it on.
+ mInitialDisplayTimeout =
+ Settings.System.getString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT);
+ Settings.System.putString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, "300000");
}
@Test
@@ -480,6 +487,9 @@
mUiDevice.executeShellCommand(
"cmd jobscheduler reset-execution-quota -u " + UserHandle.myUserId()
+ " " + TEST_APP_PACKAGE);
+
+ Settings.System.putString(
+ mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, mInitialDisplayTimeout);
}
private void setTestPackageRestricted(boolean restricted) throws Exception {
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityShortcutTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityShortcutTest.java
index 4384f29..58265f8 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityShortcutTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityShortcutTest.java
@@ -56,6 +56,7 @@
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -212,6 +213,32 @@
Arrays.asList(mSpeakingA11yServiceName, mShortcutTargetActivityName));
}
+ @Test
+ public void testAccessibilityButtonService_disableSelf_buttonRemoved() {
+ mA11yButtonServiceRule.enableService();
+ mShortcutSettingsRule.configureAccessibilityButton(
+ sUiAutomation, mA11yButtonServiceName);
+ mShortcutSettingsRule.waitForAccessibilityButtonStateChange(
+ sUiAutomation, Arrays.asList(mA11yButtonServiceName));
+
+ mA11yButtonServiceRule.getService().disableSelfAndRemove();
+ mShortcutSettingsRule.waitForAccessibilityButtonStateChange(sUiAutomation,
+ Collections.emptyList());
+ }
+
+ @Test
+ public void testAccessibilityButtonService_disableSelf_shortcutRemoved() {
+ mA11yButtonServiceRule.enableService();
+ mShortcutSettingsRule.configureAccessibilityShortcut(
+ sUiAutomation, mA11yButtonServiceName);
+ mShortcutSettingsRule.waitForAccessibilityShortcutStateChange(
+ sUiAutomation, Arrays.asList(mA11yButtonServiceName));
+
+ mA11yButtonServiceRule.getService().disableSelfAndRemove();
+ mShortcutSettingsRule.waitForAccessibilityShortcutStateChange(sUiAutomation,
+ Collections.emptyList());
+ }
+
/**
* Perform shortcut and wait for accessibility button clicked call back.
*
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 5a8e337..ff2fed4 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -89,6 +89,19 @@
android:screenOrientation="locked"/>
<service
+ android:name=".StubSystemActionsAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService" />
+ <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC" />
+ </intent-filter>
+
+ <meta-data
+ android:name="android.accessibilityservice"
+ android:resource="@xml/stub_system_actions_a11y_service" />
+ </service>
+
+ <service
android:name=".StubGestureAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index 73ffb68..7b6be1e 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -147,6 +147,8 @@
<string name="foo_bar_baz">Foo bar baz.</string>
+ <string name="stub_system_actions_a11y_service_description">com.android.accessibilityservice.cts.StubSystemActionsAccessibilityService</string>
+
<string name="stub_gesture_dispatch_a11y_service_description">com.android.accessibilityservice.cts.StubGestureAccessibilityService</string>
<string name="stub_gesture_detector_a11y_service_description">com.android.accessibilityservice.cts.GestureDetectionStubAccessibilityService</string>
diff --git a/tests/accessibilityservice/res/xml/stub_system_actions_a11y_service.xml b/tests/accessibilityservice/res/xml/stub_system_actions_a11y_service.xml
new file mode 100644
index 0000000..e505f5f
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/stub_system_actions_a11y_service.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/stub_system_actions_a11y_service_description"
+/>
\ No newline at end of file
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java
new file mode 100644
index 0000000..f53f126
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilitySystemActionTest.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts;
+
+import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
+import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES;
+import static org.junit.Assert.fail;
+
+import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule;
+import android.accessibilityservice.AccessibilityService;
+import android.app.Instrumentation;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.app.UiAutomation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
+import android.platform.test.annotations.AppModeFull;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class AccessibilitySystemActionTest {
+ // intent actions to trigger system action callbacks
+ private final static String INTENT_ACTION_SYSTEM_ACTION_CALLBACK_OVERRIDE_BACK = "android.accessibility.cts.end2endtests.action.system_action_callback_override_back";
+ private final static String INTENT_ACTION_SYSTEM_ACTION_CALLBACK_NEW = "android.accessibility.cts.end2endtests.action.system_action_callback_new";
+
+ private final static int NEW_ACTION_ID = 111;
+ private final static String MANAGE_ACCESSIBILITY_PERMISSION = "android.permission.MANAGE_ACCESSIBILITY";
+
+ private static Instrumentation sInstrumentation;
+ private static UiAutomation sUiAutomation;
+
+ private Context mContext;
+ private AccessibilityManager mAccessibilityManager;
+
+ private InstrumentedAccessibilityServiceTestRule<StubSystemActionsAccessibilityService>
+ mServiceRule = new InstrumentedAccessibilityServiceTestRule<>(
+ StubSystemActionsAccessibilityService.class, false);
+
+ private AccessibilityDumpOnFailureRule mDumpOnFailureRule =
+ new AccessibilityDumpOnFailureRule();
+
+ @Rule
+ public final RuleChain mRuleChain = RuleChain
+ .outerRule(mServiceRule)
+ .around(mDumpOnFailureRule);
+
+ StubSystemActionsAccessibilityService mService;
+
+ @BeforeClass
+ public static void oneTimeSetup() {
+ sInstrumentation = InstrumentationRegistry.getInstrumentation();
+ sUiAutomation = sInstrumentation.getUiAutomation(FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
+ sUiAutomation.adoptShellPermissionIdentity(MANAGE_ACCESSIBILITY_PERMISSION);
+ }
+
+ @AfterClass
+ public static void finalTearDown() {
+ sUiAutomation.dropShellPermissionIdentity();
+ sUiAutomation.destroy();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mAccessibilityManager =
+ (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+ // Start stub accessibility service.
+ mService = mServiceRule.enableService();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mService.setLatch(null);
+ }
+
+ @Test
+ @AppModeFull
+ public void testRegisterOverriddenLegacyAction() {
+ assertRegisterAction(AccessibilityService.GLOBAL_ACTION_BACK, null);
+ }
+
+ @Test
+ @AppModeFull
+ public void testUnregisterAction() {
+ assertRegisterAction(AccessibilityService.GLOBAL_ACTION_BACK, null);
+ assertUnregisterAction(AccessibilityService.GLOBAL_ACTION_BACK);
+ }
+
+ @Test
+ @AppModeFull
+ public void testNewActionInGetSystemActions() {
+ assertRegisterAction(NEW_ACTION_ID, null);
+ if (!mService.getSystemActions().contains(
+ new AccessibilityAction(NEW_ACTION_ID, null))) {
+ fail("new action should be in getSystemActions() list");
+ }
+ }
+
+
+ @Test
+ @AppModeFull
+ public void testNewActionNotInGetSystemActions() {
+ assertRegisterAction(NEW_ACTION_ID, null);
+ assertUnregisterAction(NEW_ACTION_ID);
+ if (mService.getSystemActions().contains(
+ new AccessibilityAction(NEW_ACTION_ID, null))) {
+ fail("new action should not be in getSystemActions() list");
+ }
+ }
+
+ @Test
+ @AppModeFull
+ public void testPerformOverriddenLegacyAction() {
+ assertRegisterAction(
+ AccessibilityService.GLOBAL_ACTION_BACK,
+ INTENT_ACTION_SYSTEM_ACTION_CALLBACK_OVERRIDE_BACK);
+ assertPerformGlobalAction(
+ AccessibilityService.GLOBAL_ACTION_BACK,
+ INTENT_ACTION_SYSTEM_ACTION_CALLBACK_OVERRIDE_BACK);
+ }
+
+ @Test
+ @AppModeFull
+ public void testPerformNewAction() {
+ assertRegisterAction(NEW_ACTION_ID, INTENT_ACTION_SYSTEM_ACTION_CALLBACK_NEW);
+ assertPerformGlobalAction(NEW_ACTION_ID, INTENT_ACTION_SYSTEM_ACTION_CALLBACK_NEW);
+ }
+
+ private void assertRegisterAction(int actionID, String pendingIntent) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.setLatch(latch);
+ try {
+ RemoteAction r = getRemoteAction(pendingIntent);
+ mAccessibilityManager.registerSystemAction(r, actionID);
+ if (!latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("did not register " + actionID);
+ }
+ } catch (InterruptedException e) {
+ fail("latch.await throws exception");
+ }
+ }
+
+ private void assertUnregisterAction(int actionID) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ mService.setLatch(latch);
+ try {
+ mAccessibilityManager.unregisterSystemAction(actionID);
+ if (!latch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("did not unregister " + actionID);
+ }
+ } catch (InterruptedException e) {
+ fail("latch.await throws exception");
+ }
+ }
+
+ private void assertPerformGlobalAction(int actionId, String pendingIntent) {
+ final CountDownLatch receiverLatch = new CountDownLatch(1);
+ BroadcastReceiver receiver = new SystemActionBroadcastReceiver(
+ receiverLatch,
+ pendingIntent);
+ mContext.registerReceiver(receiver, new IntentFilter(pendingIntent));
+ try {
+ mService.performGlobalAction(actionId);
+ if (!receiverLatch.await(DEFAULT_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ fail("action not triggered; did not receive callback intent");
+ }
+ } catch (InterruptedException e) {
+ fail("latch.await throws exception");
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ mAccessibilityManager.unregisterSystemAction(actionId);
+ }
+ }
+
+ private RemoteAction getRemoteAction(String pendingIntent) {
+ Intent i = new Intent(pendingIntent);
+ PendingIntent p = PendingIntent.getBroadcast(mContext, 0, i, 0);
+ return new RemoteAction(Icon.createWithContentUri("content://test"), "test1", "test1", p);
+ }
+
+ private static class SystemActionBroadcastReceiver extends BroadcastReceiver {
+ private final CountDownLatch mLatch;
+ private final String mExpectedAction;
+
+ public SystemActionBroadcastReceiver(CountDownLatch latch, String expectedAction) {
+ super();
+ mLatch = latch;
+ mExpectedAction = expectedAction;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (mExpectedAction.equals(action)) {
+ mLatch.countDown();
+ }
+ }
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSystemActionsAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSystemActionsAccessibilityService.java
new file mode 100644
index 0000000..b32dc8e
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSystemActionsAccessibilityService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accessibilityservice.cts;
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.content.Intent;
+
+import java.util.concurrent.CountDownLatch;
+
+public class StubSystemActionsAccessibilityService extends InstrumentedAccessibilityService {
+ private CountDownLatch latch;
+
+ @Override
+ public void onSystemActionsChanged() {
+ if (latch != null) {
+ latch.countDown();
+ }
+ }
+
+ void setLatch(CountDownLatch latch) {
+ this.latch = latch;
+ }
+}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index cfb04b4..83ac347 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -14,12 +14,12 @@
package android.accessibilityservice.cts.utils;
+import static android.accessibility.cts.common.ShellCommandBuilder.execShellCommand;
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
-import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.app.ActivityOptions;
@@ -58,6 +58,8 @@
*/
public class ActivityLaunchUtils {
private static final String LOG_TAG = "ActivityLaunchUtils";
+ private static final String AM_START_HOME_ACTIVITY_COMMAND =
+ "am start -a android.intent.action.MAIN -c android.intent.category.HOME";
// Using a static variable so it can be used in lambdas. Not preserving state in it.
private static Activity mTempActivity;
@@ -131,7 +133,7 @@
try {
executeAndWaitOn(
uiAutomation,
- () -> uiAutomation.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME),
+ () -> execShellCommand(uiAutomation, AM_START_HOME_ACTIVITY_COMMAND),
() -> isHomeScreenShowing(context, uiAutomation),
DEFAULT_TIMEOUT_MS,
"home screen");
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index c49e89a..9a34f20 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -38,7 +38,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
-import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -51,20 +50,15 @@
import android.util.DebugUtils;
import android.util.Log;
+import com.android.compatibility.common.util.AmMonitor;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.MemInfoReader;
import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.BufferedReader;
import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
import java.nio.DirectByteBuffer;
import java.util.ArrayList;
import java.util.List;
@@ -522,7 +516,8 @@
// Sleep for a while to make sure it's already blocking its main thread.
sleep(WAITFOR_MSEC);
- Monitor monitor = new Monitor(mInstrumentation);
+ AmMonitor monitor = new AmMonitor(mInstrumentation,
+ new String[]{AmMonitor.WAIT_FOR_CRASHED});
Intent intent = new Intent();
intent.setComponent(new ComponentName(STUB_PACKAGE_NAME, STUB_RECEIVER_NAMWE));
@@ -531,13 +526,13 @@
mContext.sendOrderedBroadcast(intent, null);
// Wait for the early ANR
- monitor.waitFor(Monitor.WAIT_FOR_EARLY_ANR, timeout);
+ monitor.waitFor(AmMonitor.WAIT_FOR_EARLY_ANR, timeout);
// Continue, so we could collect ANR traces
- monitor.sendCommand(Monitor.CMD_CONTINUE);
+ monitor.sendCommand(AmMonitor.CMD_CONTINUE);
// Wait for the ANR
- monitor.waitFor(Monitor.WAIT_FOR_ANR, timeout);
+ monitor.waitFor(AmMonitor.WAIT_FOR_ANR, timeout);
// Kill it
- monitor.sendCommand(Monitor.CMD_KILL);
+ monitor.sendCommand(AmMonitor.CMD_KILL);
// Wait the process gone
waitForGone(mWatcher);
long now2 = System.currentTimeMillis();
@@ -1138,111 +1133,4 @@
assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), cookie,
cookie == null ? 0 : cookie.length));
}
-
- /**
- * A utility class interact with "am monitor"
- */
- private static class Monitor {
- static final String WAIT_FOR_EARLY_ANR = "Waiting after early ANR... available commands:";
- static final String WAIT_FOR_ANR = "Waiting after ANR... available commands:";
- static final String WAIT_FOR_CRASHED = "Waiting after crash... available commands:";
- static final String CMD_CONTINUE = "c";
- static final String CMD_KILL = "k";
-
- final Instrumentation mInstrumentation;
- final ParcelFileDescriptor mReadFd;
- final FileInputStream mReadStream;
- final BufferedReader mReadReader;
- final ParcelFileDescriptor mWriteFd;
- final FileOutputStream mWriteStream;
- final PrintWriter mWritePrinter;
- final Thread mReaderThread;
-
- final ArrayList<String> mPendingLines = new ArrayList<>();
-
- boolean mStopping;
-
- Monitor(Instrumentation instrumentation) {
- mInstrumentation = instrumentation;
- ParcelFileDescriptor[] pfds = instrumentation.getUiAutomation()
- .executeShellCommandRw("am monitor");
- mReadFd = pfds[0];
- mReadStream = new ParcelFileDescriptor.AutoCloseInputStream(mReadFd);
- mReadReader = new BufferedReader(new InputStreamReader(mReadStream));
- mWriteFd = pfds[1];
- mWriteStream = new ParcelFileDescriptor.AutoCloseOutputStream(mWriteFd);
- mWritePrinter = new PrintWriter(new BufferedOutputStream(mWriteStream));
- mReaderThread = new ReaderThread();
- mReaderThread.start();
- }
-
- void waitFor(String expected, long timeout) {
- long waitUntil = SystemClock.uptimeMillis() + timeout;
- synchronized (mPendingLines) {
- while (true) {
- while (mPendingLines.size() == 0) {
- long now = SystemClock.uptimeMillis();
- if (now >= waitUntil) {
- String msg = "Timed out waiting for next line: expected=" + expected;
- Log.d(TAG, msg);
- throw new IllegalStateException(msg);
- }
- try {
- mPendingLines.wait(waitUntil - now);
- } catch (InterruptedException e) {
- }
- }
- String line = mPendingLines.remove(0);
- if (TextUtils.equals(line, expected)) {
- break;
- } else if (TextUtils.equals(line, WAIT_FOR_EARLY_ANR)
- || TextUtils.equals(line, WAIT_FOR_ANR)
- || TextUtils.equals(line, WAIT_FOR_CRASHED)) {
- // If we are getting any of the unexpected state,
- // for example, get a crash while waiting for an ANR,
- // it could be from another unrelated process, kill it directly.
- sendCommand(CMD_KILL);
- }
- }
- }
- }
-
- void finish() {
- synchronized (mPendingLines) {
- mStopping = true;
- }
- mWritePrinter.println("q");
- try {
- mWriteStream.close();
- } catch (IOException e) {
- }
- try {
- mReadStream.close();
- } catch (IOException e) {
- }
- }
-
- void sendCommand(String cmd) {
- mWritePrinter.println(cmd);
- mWritePrinter.flush();
- }
-
- final class ReaderThread extends Thread {
- @Override
- public void run() {
- try {
- String line;
- while ((line = mReadReader.readLine()) != null) {
- // Log.i(TAG, "debug: " + line);
- synchronized (mPendingLines) {
- mPendingLines.add(line);
- mPendingLines.notifyAll();
- }
- }
- } catch (IOException e) {
- Log.w(TAG, "Failed reading", e);
- }
- }
- }
- }
}
diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java
index 61b5cbc..1be2930 100644
--- a/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -44,7 +44,7 @@
import android.test.InstrumentationTestCase;
import android.util.Log;
-import com.android.compatibility.common.util.AnrMonitor;
+import com.android.compatibility.common.util.AmMonitor;
import com.android.compatibility.common.util.SystemUtil;
import java.io.IOException;
@@ -766,7 +766,8 @@
*/
public void testAppNotResponding() throws Exception {
// Setup the ANR monitor
- AnrMonitor monitor = new AnrMonitor(mInstrumentation);
+ AmMonitor monitor = new AmMonitor(mInstrumentation,
+ new String[]{AmMonitor.WAIT_FOR_CRASHED});
// Now tell it goto ANR
CommandReceiver.sendCommand(mTargetContext, CommandReceiver.COMMAND_SELF_INDUCED_ANR,
@@ -775,10 +776,10 @@
try {
// Verify we got the ANR
- assertTrue(monitor.waitFor(WAITFOR_MSEC));
+ assertTrue(monitor.waitFor(AmMonitor.WAIT_FOR_EARLY_ANR, WAITFOR_MSEC));
// Just kill the test app
- monitor.sendCommand(AnrMonitor.CMD_KILL);
+ monitor.sendCommand(AmMonitor.CMD_KILL);
} finally {
// clean up
monitor.finish();
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 553c0dc..572f9e7 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -2564,10 +2564,21 @@
}
}
- public void testAreBubblesAllowed() {
+ public void testAreBubblesAllowed_appNone() throws Exception {
+ setBubblesAppPref(0 /* none */);
assertFalse(mNotificationManager.areBubblesAllowed());
}
+ public void testAreBubblesAllowed_appSelected() throws Exception {
+ setBubblesAppPref(2 /* selected */);
+ assertFalse(mNotificationManager.areBubblesAllowed());
+ }
+
+ public void testAreBubblesAllowed_appAll() throws Exception {
+ setBubblesAppPref(1 /* all */);
+ assertTrue(mNotificationManager.areBubblesAllowed());
+ }
+
public void testNotificationIcon() {
int id = 6000;
@@ -2872,6 +2883,7 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.Builder nb = getConversationNotification();
@@ -2893,6 +2905,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
setUpNotifListener();
@@ -2918,6 +2932,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
setUpNotifListener();
@@ -2939,6 +2955,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
setUpNotifListener();
@@ -2964,6 +2982,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
setUpNotifListener();
@@ -3003,6 +3023,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
Notification.Builder nb = getConversationNotification();
@@ -3026,6 +3048,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
Notification.Builder nb = getConversationNotification();
@@ -3049,6 +3073,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
Notification.BubbleMetadata data =
@@ -3070,6 +3096,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
@@ -3091,6 +3119,8 @@
try {
setBubblesGlobal(false);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
@@ -3112,6 +3142,7 @@
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
setBubblesChannelAllowed(false);
+
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
@@ -3133,6 +3164,7 @@
setBubblesGlobal(true);
setBubblesAppPref(2 /* selected */);
setBubblesChannelAllowed(false);
+
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
@@ -3154,6 +3186,7 @@
setBubblesGlobal(true);
setBubblesAppPref(2 /* selected */);
setBubblesChannelAllowed(true);
+
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
@@ -3176,6 +3209,7 @@
setBubblesGlobal(true);
setBubblesAppPref(0 /* none */);
setBubblesChannelAllowed(false);
+
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
@@ -3198,6 +3232,7 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
createDynamicShortcut();
Notification.BubbleMetadata data =
new Notification.BubbleMetadata.Builder(SHARE_SHORTCUT_ID)
@@ -3205,12 +3240,11 @@
Notification.Builder nb = getConversationNotification();
boolean shouldBeBubble = !mActivityManager.isLowRamDevice();
- sendAndVerifyBubble(1, nb, data, shouldBeBubble);
+ sendAndVerifyBubble(42, nb, data, shouldBeBubble);
mListener.resetData();
deleteShortcuts();
-
- verifyNotificationBubbleState(1, false /* should be bubble */);
+ verifyNotificationBubbleState(42, false /* should be bubble */);
} finally {
deleteShortcuts();
}
@@ -3224,6 +3258,8 @@
try {
setBubblesGlobal(true);
setBubblesAppPref(1 /* all */);
+ setBubblesChannelAllowed(true);
+
createDynamicShortcut();
setUpNotifListener();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 5b1a86e..6c229e0 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -24,6 +24,7 @@
import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
+import android.app.PendingIntent;
import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
import android.autofillservice.cts.inline.InlineUiBot;
import android.content.ClipboardManager;
@@ -441,6 +442,11 @@
return Helper.createInlinePresentation(message);
}
+ protected InlinePresentation createInlinePresentation(String message,
+ PendingIntent attribution) {
+ return Helper.createInlinePresentation(message, attribution);
+ }
+
@NonNull
protected AutofillManager getAutofillManager() {
return mContext.getSystemService(AutofillManager.class);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java b/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java
index 6c29197..ce2c7a2 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutofillTestWatcher.java
@@ -21,6 +21,7 @@
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.TestNameUtils;
@@ -66,6 +67,7 @@
@Override
protected void finished(Description description) {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
final String testName = description.getDisplayName();
cleanAllActivities();
Log.i(TAG, "Finished " + testName);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index ecd5cd2..533b8df 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -32,11 +32,13 @@
import static com.google.common.truth.Truth.assertWithMessage;
import android.app.Activity;
+import android.app.PendingIntent;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
@@ -65,7 +67,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.autofill.InlinePresentationBuilder;
+import androidx.autofill.inline.v1.InlineSuggestionUi;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.BitmapUtils;
@@ -1518,15 +1520,29 @@
}
public static InlinePresentation createInlinePresentation(String message) {
- return new InlinePresentation(new InlinePresentationBuilder(message).build(),
- new InlinePresentationSpec.Builder(new Size(100, 100), new Size(400, 100))
- .build(), /* pinned= */ false);
+ final PendingIntent dummyIntent =
+ PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
+ return createInlinePresentation(message, dummyIntent, false);
+ }
+
+ public static InlinePresentation createInlinePresentation(String message,
+ PendingIntent attribution) {
+ return createInlinePresentation(message, attribution, false);
}
public static InlinePresentation createPinnedInlinePresentation(String message) {
- return new InlinePresentation(new InlinePresentationBuilder(message).build(),
+ final PendingIntent dummyIntent =
+ PendingIntent.getActivity(getContext(), 0, new Intent(), 0);
+ return createInlinePresentation(message, dummyIntent, true);
+ }
+
+ private static InlinePresentation createInlinePresentation(@NonNull String message,
+ @NonNull PendingIntent attribution, boolean pinned) {
+ return new InlinePresentation(
+ InlineSuggestionUi.newContentBuilder().setAttribution(attribution)
+ .setTitle(message).build().getSlice(),
new InlinePresentationSpec.Builder(new Size(100, 100), new Size(400, 100))
- .build(), /* pinned= */ true);
+ .build(), /* pinned= */ pinned);
}
private Helper() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 534aa5c..6baa5a1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -83,7 +83,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
@@ -498,7 +497,6 @@
sReplier.getNextFillRequest();
callback.assertUiShownEvent(username);
final Rect usernamePickerBoundaries1 = mUiBot.assertDatasets("DUDE").getVisibleBounds();
- final Bitmap usernameScreenshot1 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Username1 at " + usernameBoundaries1 + "; picker at " + usernamePickerBoundaries1);
// TODO(b/37566627): assertions below might be too aggressive - use range instead?
@@ -512,7 +510,6 @@
callback.assertUiHiddenEvent(username);
callback.assertUiShownEvent(password);
final Rect passwordPickerBoundaries1 = mUiBot.assertDatasets("SWEET").getVisibleBounds();
- final Bitmap passwordScreenshot1 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Password1 at " + passwordBoundaries1 + "; picker at " + passwordPickerBoundaries1);
// TODO(b/37566627): assertions below might be too aggressive - use range instead?
@@ -526,7 +523,6 @@
callback.assertUiHiddenEvent(password);
callback.assertUiShownEvent(username);
final Rect usernamePickerBoundaries2 = mUiBot.assertDatasets("DUDE").getVisibleBounds();
- final Bitmap usernameScreenshot2 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Username2 at " + usernameBoundaries2 + "; picker at " + usernamePickerBoundaries2);
@@ -535,20 +531,23 @@
callback.assertUiHiddenEvent(username);
callback.assertUiShownEvent(password);
final Rect passwordPickerBoundaries2 = mUiBot.assertDatasets("SWEET").getVisibleBounds();
- final Bitmap passwordScreenshot2 = mUiBot.takeScreenshot(mActivity);
Log.v(TAG,
"Password2 at " + passwordBoundaries2 + "; picker at " + passwordPickerBoundaries2);
// Assert final state matches initial...
// ... for username
- assertThat(usernameBoundaries2).isEqualTo(usernameBoundaries1);
- assertThat(usernamePickerBoundaries2).isEqualTo(usernamePickerBoundaries1);
- Helper.assertBitmapsAreSame("username", usernameScreenshot1, usernameScreenshot2);
+ assertWithMessage("Username2 at %s; Username1 at %s", usernameBoundaries2,
+ usernamePickerBoundaries1).that(usernameBoundaries2).isEqualTo(usernameBoundaries1);
+ assertWithMessage("Username2 picker at %s; Username1 picker at %s",
+ usernamePickerBoundaries2, usernamePickerBoundaries1).that(
+ usernamePickerBoundaries2).isEqualTo(usernamePickerBoundaries1);
// ... for password
- assertThat(passwordBoundaries2).isEqualTo(passwordBoundaries1);
- assertThat(passwordPickerBoundaries2).isEqualTo(passwordPickerBoundaries1);
- Helper.assertBitmapsAreSame("password", passwordScreenshot1, passwordScreenshot2);
+ assertWithMessage("Password2 at %s; Password1 at %s", passwordBoundaries2,
+ passwordBoundaries1).that(passwordBoundaries2).isEqualTo(passwordBoundaries1);
+ assertWithMessage("Password2 picker at %s; Password1 picker at %s",
+ passwordPickerBoundaries2, passwordPickerBoundaries1).that(
+ passwordPickerBoundaries2).isEqualTo(passwordPickerBoundaries1);
// Final sanity check
callback.assertNumberUnhandledEvents(0);
@@ -1534,10 +1533,10 @@
.setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
.build());
mActivity.forceAutofillOnUsername();
- mUiBot.waitForIdle();
} else {
- mActivity.onUsername(View::requestFocus);
+ mUiBot.selectByRelativeId(ID_USERNAME);
}
+ mUiBot.waitForIdle();
// Sanity check.
mUiBot.assertNoDatasetsEver();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index 6fd973e..9b17dd4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -211,8 +211,6 @@
}
private void autofill4PartitionsTest(boolean manually) throws Exception {
- final int expectedFlag = manually ? FLAG_MANUAL_REQUEST : 0;
-
// Set service.
enableService();
@@ -233,11 +231,12 @@
// Trigger auto-fill.
mActivity.triggerAutofill(manually, 1, 1);
final FillRequest fillRequest1 = sReplier.getNextFillRequest();
- assertThat(fillRequest1.flags).isEqualTo(expectedFlag);
if (manually) {
+ assertHasFlags(fillRequest1.flags, FLAG_MANUAL_REQUEST);
assertValue(fillRequest1.structure, ID_L1C1, "");
} else {
+ assertThat(fillRequest1.flags).isEqualTo(0);
assertTextIsSanitized(fillRequest1.structure, ID_L1C1);
}
assertTextIsSanitized(fillRequest1.structure, ID_L1C2);
@@ -265,13 +264,14 @@
// Trigger auto-fill.
mActivity.triggerAutofill(manually, 2, 1);
final FillRequest fillRequest2 = sReplier.getNextFillRequest();
- assertThat(fillRequest2.flags).isEqualTo(expectedFlag);
assertValue(fillRequest2.structure, ID_L1C1, "l1c1");
assertValue(fillRequest2.structure, ID_L1C2, "l1c2");
if (manually) {
+ assertHasFlags(fillRequest2.flags, FLAG_MANUAL_REQUEST);
assertValue(fillRequest2.structure, ID_L2C1, "");
} else {
+ assertThat(fillRequest2.flags).isEqualTo(0);
assertTextIsSanitized(fillRequest2.structure, ID_L2C1);
}
assertTextIsSanitized(fillRequest2.structure, ID_L2C2);
@@ -299,15 +299,16 @@
// Trigger auto-fill.
mActivity.triggerAutofill(manually, 3, 1);
final FillRequest fillRequest3 = sReplier.getNextFillRequest();
- assertThat(fillRequest3.flags).isEqualTo(expectedFlag);
assertValue(fillRequest3.structure, ID_L1C1, "l1c1");
assertValue(fillRequest3.structure, ID_L1C2, "l1c2");
assertValue(fillRequest3.structure, ID_L2C1, "l2c1");
assertValue(fillRequest3.structure, ID_L2C2, "l2c2");
if (manually) {
+ assertHasFlags(fillRequest3.flags, FLAG_MANUAL_REQUEST);
assertValue(fillRequest3.structure, ID_L3C1, "");
} else {
+ assertThat(fillRequest3.flags).isEqualTo(0);
assertTextIsSanitized(fillRequest3.structure, ID_L3C1);
}
assertTextIsSanitized(fillRequest3.structure, ID_L3C2);
@@ -335,7 +336,6 @@
// Trigger auto-fill.
mActivity.triggerAutofill(manually, 4, 1);
final FillRequest fillRequest4 = sReplier.getNextFillRequest();
- assertThat(fillRequest4.flags).isEqualTo(expectedFlag);
assertValue(fillRequest4.structure, ID_L1C1, "l1c1");
assertValue(fillRequest4.structure, ID_L1C2, "l1c2");
@@ -344,8 +344,10 @@
assertValue(fillRequest4.structure, ID_L3C1, "l3c1");
assertValue(fillRequest4.structure, ID_L3C2, "l3c2");
if (manually) {
+ assertHasFlags(fillRequest4.flags, FLAG_MANUAL_REQUEST);
assertValue(fillRequest4.structure, ID_L4C1, "");
} else {
+ assertThat(fillRequest4.flags).isEqualTo(0);
assertTextIsSanitized(fillRequest4.structure, ID_L4C1);
}
assertTextIsSanitized(fillRequest4.structure, ID_L4C2);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
index b84a96e..70f1d86 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Timeouts.java
@@ -29,6 +29,8 @@
public static final long MOCK_IME_TIMEOUT_MS = 5_000;
public static final long DRAWABLE_TIMEOUT_MS = 5_000;
+ public static final long LONG_PRESS_MS = 3000;
+
/**
* Timeout until framework binds / unbinds from service.
*/
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index d904add..0bfceb5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -17,6 +17,7 @@
package android.autofillservice.cts;
import static android.autofillservice.cts.Timeouts.DATASET_PICKER_NOT_SHOWN_NAPTIME_MS;
+import static android.autofillservice.cts.Timeouts.LONG_PRESS_MS;
import static android.autofillservice.cts.Timeouts.SAVE_NOT_SHOWN_NAPTIME_MS;
import static android.autofillservice.cts.Timeouts.SAVE_TIMEOUT;
import static android.autofillservice.cts.Timeouts.UI_DATASET_PICKER_TIMEOUT;
@@ -372,6 +373,14 @@
}
/**
+ * Finds the suggestion by name and perform long click on suggestion to trigger attribution
+ * intent.
+ */
+ public void longPressSuggestion(String name) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Selects a view by text.
*
* <p><b>NOTE:</b> when selecting an option in dataset picker is shown, prefer
@@ -826,7 +835,7 @@
public UiObject2 getAutofillMenuOption(String id) throws Exception {
final UiObject2 field = waitForObject(By.res(mPackageName, id));
// TODO: figure out why obj.longClick() doesn't always work
- field.click(3000);
+ field.click(LONG_PRESS_MS);
List<UiObject2> menuItems = waitForObjects(
By.res("android", RESOURCE_ID_CONTEXT_MENUITEM), mDefaultTimeout);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedTimeouts.java b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedTimeouts.java
index 2eef6e3..7bfdfc8 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedTimeouts.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/augmented/AugmentedTimeouts.java
@@ -23,7 +23,7 @@
*/
final class AugmentedTimeouts {
- private static final long ONE_TIMEOUT_TO_RULE_THEN_ALL_MS = 1_500;
+ private static final long ONE_TIMEOUT_TO_RULE_THEN_ALL_MS = 1_000;
private static final long ONE_NAPTIME_TO_RULE_THEN_ALL_MS = 3_000;
/**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
index be3f1ab..78361f1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineAugmentedLoginActivityTest.java
@@ -91,6 +91,42 @@
}
@Test
+ public void testAugmentedAutoFill_noFiltering() throws Exception {
+ // Set services
+ enableService();
+ enableAugmentedService();
+
+ // Set expectations
+ final EditText username = mActivity.getUsername();
+ final EditText password = mActivity.getPassword();
+ final AutofillId usernameId = username.getAutofillId();
+ final AutofillId passwordId = password.getAutofillId();
+ final AutofillValue usernameValue = username.getAutofillValue();
+ sReplier.addResponse(NO_RESPONSE);
+ sAugmentedReplier.addResponse(new CannedAugmentedFillResponse.Builder()
+ .addInlineSuggestion(new CannedAugmentedFillResponse.Dataset.Builder("Augment Me")
+ .setField(usernameId, "dude", createInlinePresentation("dude"))
+ .setField(passwordId, "sweet", createInlinePresentation("sweet"))
+ .build())
+ .setDataset(new CannedAugmentedFillResponse.Dataset.Builder("req1")
+ .build(), usernameId)
+ .build());
+
+ // Trigger auto-fill
+ mUiBot.selectByRelativeId(ID_USERNAME);
+ mUiBot.waitForIdleSync();
+ sReplier.getNextFillRequest();
+ sAugmentedReplier.getNextFillRequest();
+
+ // Assert suggestion
+ mUiBot.assertDatasets("dude");
+
+ // Suggestion was not shown.
+ mActivity.onUsername((v) -> v.setText("d"));
+ mUiBot.assertNoDatasetsEver();
+ }
+
+ @Test
public void testAugmentedAutoFill_twoDatasetThenFilledSecond() throws Exception {
// Set services
enableService();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
index 108ae74..204bb43 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineLoginActivityTest.java
@@ -27,10 +27,13 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import android.app.PendingIntent;
import android.autofillservice.cts.CannedFillResponse;
+import android.autofillservice.cts.DummyActivity;
import android.autofillservice.cts.Helper;
import android.autofillservice.cts.InstrumentedAutoFillService;
import android.autofillservice.cts.LoginActivityCommonTestCase;
+import android.content.Intent;
import android.service.autofill.FillContext;
import org.junit.Test;
@@ -156,4 +159,43 @@
assertWithMessage("Password node is focused").that(
findNodeByResourceId(request.structure, ID_PASSWORD).isFocused()).isFalse();
}
+
+ @Test
+ public void testLongClickAttribution() throws Exception {
+ // Set service.
+ enableService();
+
+ Intent intent = new Intent(mContext, DummyActivity.class);
+ PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+ .addDataset(new CannedFillResponse.CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setPresentation(createPresentation("The Username"))
+ .setInlinePresentation(
+ createInlinePresentation("The Username", pendingIntent))
+ .build());
+
+ sReplier.addResponse(builder.build());
+ mActivity.expectAutoFill("dude");
+
+ // Trigger auto-fill.
+ mUiBot.selectByRelativeId(ID_USERNAME);
+ mUiBot.waitForIdleSync();
+
+ mUiBot.assertDatasets("The Username");
+
+ // Long click on suggestion
+ mUiBot.longPressSuggestion("The Username");
+ mUiBot.waitForIdleSync();
+
+ // Make sure the attribution showed worked
+ mUiBot.selectByText("foo");
+
+ // Go back to the filled app.
+ mUiBot.pressBack();
+
+ sReplier.getNextFillRequest();
+ mUiBot.waitForIdleSync();
+ }
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java
index 1d1803a..463fd7b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineUiBot.java
@@ -17,6 +17,7 @@
package android.autofillservice.cts.inline;
import static android.autofillservice.cts.Timeouts.DATASET_PICKER_NOT_SHOWN_NAPTIME_MS;
+import static android.autofillservice.cts.Timeouts.LONG_PRESS_MS;
import static android.autofillservice.cts.Timeouts.UI_TIMEOUT;
import android.autofillservice.cts.UiBot;
@@ -74,6 +75,16 @@
}
@Override
+ public void longPressSuggestion(String name) throws Exception {
+ final UiObject2 strip = findSuggestionStrip(UI_TIMEOUT);
+ final UiObject2 dataset = strip.findObject(By.text(name));
+ if (dataset == null) {
+ throw new AssertionError("no dataset " + name + " in " + getChildrenAsText(strip));
+ }
+ dataset.click(LONG_PRESS_MS);
+ }
+
+ @Override
public UiObject2 assertDatasets(String...names) throws Exception {
final UiObject2 picker = findSuggestionStrip(UI_TIMEOUT);
return assertDatasets(picker, names);
diff --git a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
index afd2beb..4a88c84 100644
--- a/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/LogicalCameraDeviceTest.java
@@ -88,11 +88,12 @@
private static final double NS_PER_MS = 1000000.0;
private static final int MAX_IMAGE_COUNT = 3;
- private static final int NUM_FRAMES_CHECKED = 30;
+ private static final int NUM_FRAMES_CHECKED = 300;
private static final double FRAME_DURATION_THRESHOLD = 0.03;
private static final double FOV_THRESHOLD = 0.03;
private static final double ASPECT_RATIO_THRESHOLD = 0.03;
+ private static final long MAX_TIMESTAMP_DIFFERENCE_THRESHOLD = 10000000; // 10ms
private StateWaiter mSessionWaiter;
@@ -1221,15 +1222,43 @@
}
}
}
+
double logicalAvgDurationMs2 = (logicalTimestamps2[NUM_FRAMES_CHECKED-1] -
logicalTimestamps2[0])/(NS_PER_MS*(NUM_FRAMES_CHECKED-1));
-
- // Check framerate slow down with physical streams, but do not enforce.
+ // Check CALIBRATED synchronization between physical cameras
+ Integer syncType = mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE);
double fpsRatio = (logicalAvgDurationMs2 - logicalAvgDurationMs)/logicalAvgDurationMs;
- if (fpsRatio > FRAME_DURATION_THRESHOLD) {
- Log.w(TAG, "The average frame duration with concurrent physical streams is" +
- logicalAvgDurationMs2 + " ms vs " + logicalAvgDurationMs +
- " ms for logical streams only");
+ if (syncType == CameraCharacteristics.LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED) {
+ // Check framerate doesn't slow down with physical streams
+ mCollector.expectTrue(
+ "The average frame duration with concurrent physical streams is" +
+ logicalAvgDurationMs2 + " ms vs " + logicalAvgDurationMs +
+ " ms for logical streams only", fpsRatio <= FRAME_DURATION_THRESHOLD);
+
+ long maxTimestampDelta = 0;
+ for (int i = 0; i < NUM_FRAMES_CHECKED; i++) {
+ long delta = Math.abs(physicalTimestamps[0][i] - physicalTimestamps[1][i]);
+ if (delta > maxTimestampDelta) {
+ maxTimestampDelta = delta;
+ }
+ }
+
+ Log.i(TAG, "Maximum difference between physical camera timestamps: "
+ + maxTimestampDelta);
+
+ // The maximum timestamp difference should not be larger than the threshold.
+ mCollector.expectTrue(
+ "The maximum timestamp deltas between the physical cameras "
+ + maxTimestampDelta + " is larger than " + MAX_TIMESTAMP_DIFFERENCE_THRESHOLD,
+ maxTimestampDelta <= MAX_TIMESTAMP_DIFFERENCE_THRESHOLD);
+ } else {
+ // Do not enforce fps check for APPROXIMATE synced device.
+ if (fpsRatio > FRAME_DURATION_THRESHOLD) {
+ Log.w(TAG, "The average frame duration with concurrent physical streams is" +
+ logicalAvgDurationMs2 + " ms vs " + logicalAvgDurationMs +
+ " ms for logical streams only");
+ }
}
if (VERBOSE) {
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index c0d01b2..fe642c3 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -100,6 +100,10 @@
android:resizeableActivity="true"
android:taskAffinity="nobody.but.SideActivity"/>
+ <activity android:name="android.server.wm.ActivityManagerTestBase$ConfigChangeHandlingActivity"
+ android:resizeableActivity="true"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen" />
+
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$FirstActivity" />
<activity android:name="android.server.wm.lifecycle.ActivityLifecycleClientTestBase$SecondActivity"/>
@@ -406,7 +410,7 @@
<activity android:name="android.server.wm.StartActivityAsUserActivity"
android:directBootAware="true"/>
- <activity android:name="android.server.wm.WindowInsetsAnimationTests$TestActivity"
+ <activity android:name="android.server.wm.WindowInsetsAnimationTestBase$TestActivity"
android:theme="@android:style/Theme.Material.NoActionBar" />
<activity android:name="android.server.wm.ForceRelayoutTestBase$TestActivity"
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index 8d2f216..f1423f0 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -297,6 +297,12 @@
public static final String EXTRA_CUTOUT_EXISTS = "cutoutExists";
}
+ /** Extra key constants for {@link android.server.wm.app.LandscapeOrientationActivity}. */
+ public static class LandscapeOrientationActivity {
+ public static final String EXTRA_CONFIG_INFO_IN_ON_CREATE = "config_info_in_on_create";
+ public static final String EXTRA_DISPLAY_REAL_SIZE = "display_real_size";
+ }
+
/** Extra key constants for {@link android.server.wm.app.FontScaleActivity}. */
public static class FontScaleActivity {
public static final String EXTRA_FONT_PIXEL_SIZE = "fontPixelSize";
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
index 5eb30d5..00984fa 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/LandscapeOrientationActivity.java
@@ -16,11 +16,35 @@
package android.server.wm.app;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE;
+
import android.content.res.Configuration;
+import android.graphics.Point;
+import android.os.Bundle;
+import android.server.wm.CommandSession.ConfigInfo;
+import android.view.Display;
public class LandscapeOrientationActivity extends AbstractLifecycleLogActivity {
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!getIntent().hasExtra(EXTRA_CONFIG_INFO_IN_ON_CREATE)) {
+ return;
+ }
+ withTestJournalClient(client -> {
+ final Bundle extras = new Bundle();
+ final Display display = getDisplay();
+ final Point size = new Point();
+ display.getRealSize(size);
+ extras.putParcelable(EXTRA_CONFIG_INFO_IN_ON_CREATE, new ConfigInfo(this, display));
+ extras.putParcelable(EXTRA_DISPLAY_REAL_SIZE, size);
+ client.putExtras(extras);
+ });
+ }
+
+ @Override
protected void onResume() {
super.onResume();
dumpConfigInfo();
diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
index 19ba5c0..8bff8c4 100644
--- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
+++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java
@@ -69,6 +69,7 @@
import java.util.List;
import java.util.concurrent.TimeoutException;
+import java.util.stream.Stream;
/**
* This class covers all test cases for starting/blocking background activities.
@@ -212,28 +213,26 @@
}
@Test
- public void testActivityNotBlockedwhenForegroundActivityLaunchInSameTask() throws Exception {
+ public void testActivityBroughtToTopOfTaskWhenLaunchedInTheBackground() throws Exception {
// Start foreground activity, and foreground activity able to launch background activity
// successfully
Intent intent = new Intent();
intent.setComponent(APP_A_FOREGROUND_ACTIVITY);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.putExtra(LAUNCH_BACKGROUND_ACTIVITY_EXTRA, true);
- intent.putExtra(START_ACTIVITY_FROM_FG_ACTIVITY_DELAY_MS_EXTRA, 2000);
mContext.startActivity(intent);
boolean result = waitForActivityFocused(ACTIVITY_FOCUS_TIMEOUT_MS,
APP_A_FOREGROUND_ACTIVITY);
assertTrue("Not able to launch background activity", result);
assertTaskStack(new ComponentName[]{APP_A_FOREGROUND_ACTIVITY}, APP_A_FOREGROUND_ACTIVITY);
-
- // The foreground activity will be paused but will attempt to restart itself in onPause()
pressHomeAndResumeAppSwitch();
+ mContext.sendBroadcast(getLaunchActivitiesBroadcast(APP_A_BACKGROUND_ACTIVITY));
+
result = waitForActivityFocused(APP_A_FOREGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
assertFalse("Previously foreground Activity should not be able to relaunch itself", result);
- assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY, APP_A_FOREGROUND_ACTIVITY},
+ assertTaskStack(new ComponentName[] {APP_A_BACKGROUND_ACTIVITY, APP_A_FOREGROUND_ACTIVITY},
APP_A_FOREGROUND_ACTIVITY);
}
@@ -322,13 +321,8 @@
// The activity, now in the background, will attempt to start 2 activities in quick
// succession
- Intent broadcastIntent = new Intent(ACTION_LAUNCH_BACKGROUND_ACTIVITIES);
- Intent bgActivity1 = new Intent();
- bgActivity1.setComponent(APP_A_BACKGROUND_ACTIVITY);
- Intent bgActivity2 = new Intent();
- bgActivity2.setComponent(APP_A_SECOND_BACKGROUND_ACTIVITY);
- broadcastIntent.putExtra(LAUNCH_INTENTS_EXTRA, new Intent[]{bgActivity1, bgActivity2});
- mContext.sendBroadcast(broadcastIntent);
+ mContext.sendBroadcast(getLaunchActivitiesBroadcast(APP_A_BACKGROUND_ACTIVITY,
+ APP_A_SECOND_BACKGROUND_ACTIVITY));
// There should be 2 activities in the background (not focused) INITIALIZING
result = waitForActivityFocused(APP_A_BACKGROUND_ACTIVITY);
@@ -450,16 +444,32 @@
assertTaskStack(new ComponentName[]{APP_A_BACKGROUND_ACTIVITY}, APP_A_BACKGROUND_ACTIVITY);
}
+ private Intent getLaunchActivitiesBroadcast(ComponentName... componentNames) {
+ Intent broadcastIntent = new Intent(ACTION_LAUNCH_BACKGROUND_ACTIVITIES);
+ Intent[] intents = Stream.of(componentNames)
+ .map(c -> {
+ Intent intent = new Intent();
+ intent.setComponent(c);
+ return intent;
+ })
+ .toArray(Intent[]::new);
+ broadcastIntent.putExtra(LAUNCH_INTENTS_EXTRA, intents);
+ return broadcastIntent;
+ }
+
private void pressHomeAndResumeAppSwitch() {
// Press home key to ensure stopAppSwitches is called because the last-stop-app-switch-time
// is a criteria of allowing background start.
pressHomeButton();
- // Waiting for home visible before resuming app switches to make sure the part that sets the
- // stop-app-switches time from pressHomeButton() doesn't race with resumeAppSwitches()
- mWmState.waitForHomeActivityVisible();
// Resume the stopped state (it won't affect last-stop-app-switch-time) so we don't need to
// wait extra time to prevent the next launch from being delayed.
resumeAppSwitches();
+ mWmState.waitForHomeActivityVisible();
+ // Resuming app switches again after home became visible because the previous call might
+ // have raced with pressHomeButton().
+ // TODO(b/155454710): Remove previous call after making sure all the tests don't depend on
+ // the timing here.
+ resumeAppSwitches();
}
private void assertTaskStack(ComponentName[] expectedComponents,
diff --git a/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml b/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
index a664f0b..25e12ab 100644
--- a/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/jetpack/AndroidManifest.xml
@@ -29,6 +29,7 @@
<activity android:name="android.server.wm.jetpack.TestConfigChangeHandlingActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"
/>
+ <activity android:name="android.server.wm.jetpack.TestGetWindowLayoutInfoActivity" />
</application>
<!-- self-instrumenting test package. -->
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
index 4e67f8a..7b9bcf6 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ExtensionTest.java
@@ -62,14 +62,18 @@
private ActivityTestRule<TestConfigChangeHandlingActivity> mConfigHandlingActivityTestRule =
new ActivityTestRule<>(TestConfigChangeHandlingActivity.class,
false /* initialTouchMode */, false /* launchActivity */);
+ private ActivityTestRule<TestGetWindowLayoutInfoActivity> mGetWindowLayoutInfoActivityTestRule =
+ new ActivityTestRule<>(TestGetWindowLayoutInfoActivity.class,
+ false /* initialTouchMode */, false /* launchActivity */);
/**
- * This chain rule will launch TestActivity before each test starts, and cleanup both activities
+ * This chain rule will launch TestActivity before each test starts, and cleanup all activities
* after each test finishes.
*/
@Rule
- public TestRule chain =
- RuleChain.outerRule(mActivityTestRule).around(mConfigHandlingActivityTestRule);
+ public TestRule chain = RuleChain.outerRule(mActivityTestRule)
+ .around(mConfigHandlingActivityTestRule)
+ .around(mGetWindowLayoutInfoActivityTestRule);
private TestActivity mActivity;
private TestInterfaceCompat mExtension;
@@ -165,6 +169,21 @@
}
@Test
+ public void testGetWindowLayoutInfo_activityNotAttached_notReturnIncorrectValue() {
+ // No display feature to compare, finish test early.
+ assumeHasDisplayFeatures();
+
+ // The value is verified inside TestGetWindowLayoutInfoActivity
+ TestGetWindowLayoutInfoActivity.resetResumeCounter();
+ TestGetWindowLayoutInfoActivity testGetWindowLayoutInfoActivity =
+ mGetWindowLayoutInfoActivityTestRule.launchActivity(new Intent());
+
+ // Make sure the activity has gone through all states.
+ assertThat(TestGetWindowLayoutInfoActivity.waitForOnResume()).isTrue();
+ assertThat(testGetWindowLayoutInfoActivity.waitForLayout()).isTrue();
+ }
+
+ @Test
public void testGetWindowLayoutInfo_configChanged_windowLayoutUpdates() {
// No display feature to compare, finish test early.
assumeHasDisplayFeatures();
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/TestGetWindowLayoutInfoActivity.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/TestGetWindowLayoutInfoActivity.java
new file mode 100644
index 0000000..e9f6c66
--- /dev/null
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/TestGetWindowLayoutInfoActivity.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm.jetpack;
+
+import static android.server.wm.jetpack.JetpackExtensionTestBase.getActivityWindowToken;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+import android.os.IBinder;
+import android.server.wm.jetpack.wrapper.TestInterfaceCompat;
+import android.server.wm.jetpack.wrapper.TestWindowLayoutInfo;
+import android.view.View;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Activity that can verify the return value of
+ * {@link android.server.wm.jetpack.wrapper.TestInterfaceCompat#getWindowLayoutInfo(IBinder)} on
+ * activity's different states.
+ */
+public class TestGetWindowLayoutInfoActivity extends TestActivity {
+
+ private TestInterfaceCompat mExtension;
+ @Nullable private TestWindowLayoutInfo prevWindowLayoutInfo;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mExtension = ExtensionUtils.getInterfaceCompat(this);
+ assertCorrectWindowLayoutInfoOrException(true /* isOkToThrowException */);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ assertCorrectWindowLayoutInfoOrException(true /* isOkToThrowException */);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ assertCorrectWindowLayoutInfoOrException(true /* isOkToThrowException */);
+ }
+
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
+ int oldTop, int oldRight, int oldBottom) {
+ super.onLayoutChange(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom);
+ assertCorrectWindowLayoutInfoOrException(false /* isOkToThrowException */);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ private void assertCorrectWindowLayoutInfoOrException(boolean isOkToThrowException) {
+ IBinder windowToken = getActivityWindowToken(this);
+ if (windowToken == null) {
+ return;
+ }
+
+ TestWindowLayoutInfo windowLayoutInfo = null;
+ try {
+ windowLayoutInfo = mExtension.getWindowLayoutInfo(windowToken);
+ } catch (Exception e) {
+ assertThat(isOkToThrowException).isTrue();
+ }
+
+ if (prevWindowLayoutInfo == null) {
+ prevWindowLayoutInfo = windowLayoutInfo;
+ } else {
+ assertThat(windowLayoutInfo).isEqualTo(prevWindowLayoutInfo);
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
index 868187c..c898ef6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java
@@ -23,6 +23,8 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.server.wm.ComponentNameUtils.getWindowName;
import static android.server.wm.StateLogger.logE;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
@@ -35,6 +37,8 @@
import static android.server.wm.app.Components.PORTRAIT_ORIENTATION_ACTIVITY;
import static android.server.wm.app.Components.RESIZEABLE_ACTIVITY;
import static android.server.wm.app.Components.TEST_ACTIVITY;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_CONFIG_INFO_IN_ON_CREATE;
+import static android.server.wm.app.Components.LandscapeOrientationActivity.EXTRA_DISPLAY_REAL_SIZE;
import static android.server.wm.translucentapp26.Components.SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
@@ -53,11 +57,19 @@
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
-import android.content.res.Configuration;
+import android.content.Context;
+import android.graphics.Point;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
+import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
import android.server.wm.CommandSession.ActivitySession;
+import android.server.wm.CommandSession.ConfigInfo;
import android.server.wm.CommandSession.SizeInfo;
+import android.server.wm.TestJournalProvider.TestJournalContainer;
+import android.server.wm.settings.SettingsSession;
+import android.view.Display;
import org.junit.Test;
@@ -68,7 +80,7 @@
* atest CtsWindowManagerDeviceTestCases:AppConfigurationTests
*/
@Presubmit
-public class AppConfigurationTests extends ActivityManagerTestBase {
+public class AppConfigurationTests extends MultiDisplayTestBase {
private static final int SMALL_WIDTH_DP = 426;
private static final int SMALL_HEIGHT_DP = 320;
@@ -311,21 +323,21 @@
mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
SizeInfo reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY);
assertEquals("portrait activity should be in portrait",
- Configuration.ORIENTATION_PORTRAIT, reportedSizes.orientation);
+ ORIENTATION_PORTRAIT, reportedSizes.orientation);
separateTestJournal();
launchActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
mWmState.assertVisibility(LANDSCAPE_ORIENTATION_ACTIVITY, true /* visible */);
reportedSizes = getLastReportedSizesForActivity(LANDSCAPE_ORIENTATION_ACTIVITY);
assertEquals("landscape activity should be in landscape",
- Configuration.ORIENTATION_LANDSCAPE, reportedSizes.orientation);
+ ORIENTATION_LANDSCAPE, reportedSizes.orientation);
separateTestJournal();
launchActivity(PORTRAIT_ORIENTATION_ACTIVITY);
mWmState.assertVisibility(PORTRAIT_ORIENTATION_ACTIVITY, true /* visible */);
reportedSizes = getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY);
assertEquals("portrait activity should be in portrait",
- Configuration.ORIENTATION_PORTRAIT, reportedSizes.orientation);
+ ORIENTATION_PORTRAIT, reportedSizes.orientation);
}
@Test
@@ -337,7 +349,7 @@
final SizeInfo initialReportedSizes =
getLastReportedSizesForActivity(PORTRAIT_ORIENTATION_ACTIVITY);
assertEquals("portrait activity should be in portrait",
- Configuration.ORIENTATION_PORTRAIT, initialReportedSizes.orientation);
+ ORIENTATION_PORTRAIT, initialReportedSizes.orientation);
separateTestJournal();
launchActivity(SDK26_TRANSLUCENT_LANDSCAPE_ACTIVITY, WINDOWING_MODE_FULLSCREEN);
@@ -407,9 +419,9 @@
// Launch an activity that requests different orientation and check that it will be applied
final boolean launchingPortrait;
- if (initialOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ if (initialOrientation == ORIENTATION_LANDSCAPE) {
launchingPortrait = true;
- } else if (initialOrientation == Configuration.ORIENTATION_PORTRAIT) {
+ } else if (initialOrientation == ORIENTATION_PORTRAIT) {
launchingPortrait = false;
} else {
fail("Unexpected orientation value: " + initialOrientation);
@@ -436,6 +448,46 @@
}
@Test
+ public void testRotatedInfoWithFixedRotationTransform() {
+ assumeTrue("Skipping test: no rotation support", supportsRotation());
+
+ // TODO(b/143053092): Remove the settings if it becomes stable.
+ mObjectTracker.manage(new SettingsSession<>(
+ Settings.Global.getUriFor("fixed_rotation_transform"),
+ Settings.Global::getInt, Settings.Global::putInt)).set(1);
+
+ getLaunchActivityBuilder()
+ .setUseInstrumentation()
+ .setTargetActivity(LANDSCAPE_ORIENTATION_ACTIVITY)
+ // Request the info from onCreate because at that moment the real display hasn't
+ // rotated but the activity is rotated.
+ .setIntentExtra(bundle -> bundle.putBoolean(EXTRA_CONFIG_INFO_IN_ON_CREATE, true))
+ .execute();
+ mWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+
+ final SizeInfo reportedSizes = getActivityDisplaySize(LANDSCAPE_ORIENTATION_ACTIVITY);
+ final Bundle extras = TestJournalContainer.get(LANDSCAPE_ORIENTATION_ACTIVITY).extras;
+ final Point onCreateRealDisplaySize = extras.getParcelable(EXTRA_DISPLAY_REAL_SIZE);
+ final ConfigInfo onCreateConfigInfo = extras.getParcelable(EXTRA_CONFIG_INFO_IN_ON_CREATE);
+ final SizeInfo onCreateSize = onCreateConfigInfo.sizeInfo;
+
+ assertEquals("The last reported size should be the same as the one from onCreate",
+ reportedSizes, onCreateConfigInfo.sizeInfo);
+
+ final Display display = mDm.getDisplay(Display.DEFAULT_DISPLAY);
+ final Point expectedRealDisplaySize = new Point();
+ display.getRealSize(expectedRealDisplaySize);
+
+ assertEquals("The activity should get the final display rotation in onCreate",
+ display.getRotation(), onCreateConfigInfo.rotation);
+ assertEquals("The activity should get the final display size in onCreate",
+ expectedRealDisplaySize, onCreateRealDisplaySize);
+ assertEquals("The app size of activity should have the same orientation",
+ expectedRealDisplaySize.x > expectedRealDisplaySize.y,
+ onCreateSize.displayWidth > onCreateSize.displayHeight);
+ }
+
+ @Test
public void testNonFullscreenActivityPermitted() throws Exception {
if(!supportsRotation()) {
//cannot physically rotate the screen on automotive device, skip
@@ -472,7 +524,7 @@
// Request portrait
mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
- waitForBroadcastActivityReady(Configuration.ORIENTATION_PORTRAIT);
+ waitForBroadcastActivityReady(ORIENTATION_PORTRAIT);
// Finish activity
mBroadcastActionTrigger.finishBroadcastReceiverActivity();
@@ -503,7 +555,7 @@
launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_LANDSCAPE);
mWmState.waitForLastOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- waitForBroadcastActivityReady(Configuration.ORIENTATION_LANDSCAPE);
+ waitForBroadcastActivityReady(ORIENTATION_LANDSCAPE);
mBroadcastActionTrigger.finishBroadcastReceiverActivity();
// Verify that activity brought to front is in originally requested orientation.
@@ -520,7 +572,7 @@
launchActivityInNewTask(BROADCAST_RECEIVER_ACTIVITY);
mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
- waitForBroadcastActivityReady(Configuration.ORIENTATION_PORTRAIT);
+ waitForBroadcastActivityReady(ORIENTATION_PORTRAIT);
mBroadcastActionTrigger.finishBroadcastReceiverActivity();
// Verify that activity brought to front is in originally requested orientation.
@@ -577,6 +629,52 @@
}
/**
+ * Test that the orientation for a simulated display context will not change when the device is
+ * rotated.
+ */
+ @Test
+ public void testAppContextDerivedDisplayContextOrientationWhenRotating() {
+ assumeTrue("Skipping test: no rotation support", supportsRotation());
+ assumeTrue("Skipping test: no multi-display support", supportsMultiDisplay());
+
+ RotationSession rotationSession = createManagedRotationSession();
+ rotationSession.set(ROTATION_0);
+
+ TestActivitySession<ConfigChangeHandlingActivity> activitySession
+ = createManagedTestActivitySession();
+ activitySession.launchTestActivityOnDisplaySync(ConfigChangeHandlingActivity.class,
+ Display.DEFAULT_DISPLAY);
+ final ConfigChangeHandlingActivity activity = activitySession.getActivity();
+
+ VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
+ WindowManagerState.DisplayContent displayContent = virtualDisplaySession
+ .setSimulateDisplay(true)
+ .setSimulationDisplaySize(100 /* width */, 200 /* height */)
+ .createDisplay();
+
+ DisplayManager dm = activity.getSystemService(DisplayManager.class);
+ Display simulatedDisplay = dm.getDisplay(displayContent.mId);
+ Context simulatedDisplayContext = activity.getApplicationContext()
+ .createDisplayContext(simulatedDisplay);
+ assertEquals(ORIENTATION_PORTRAIT,
+ simulatedDisplayContext.getResources().getConfiguration().orientation);
+
+ separateTestJournal();
+
+ final int[] rotations = {ROTATION_270, ROTATION_180, ROTATION_90, ROTATION_0};
+ for (final int rotation : rotations) {
+ rotationSession.set(rotation);
+
+ assertRelaunchOrConfigChanged(activity.getComponentName(), 0 /* numRelaunch */,
+ 1 /* numConfigChange */);
+ separateTestJournal();
+
+ assertEquals("Display context orientation must not be changed", ORIENTATION_PORTRAIT,
+ simulatedDisplayContext.getResources().getConfiguration().orientation);
+ }
+ }
+
+ /**
* Test that activity orientation will not change when trying to rotate fixed-orientation
* activity.
* Also verify that occluded activity will not get config changes.
@@ -639,7 +737,7 @@
// Request portrait
mBroadcastActionTrigger.requestOrientation(SCREEN_ORIENTATION_PORTRAIT);
mWmState.waitForLastOrientation(SCREEN_ORIENTATION_PORTRAIT);
- waitForBroadcastActivityReady(Configuration.ORIENTATION_PORTRAIT);
+ waitForBroadcastActivityReady(ORIENTATION_PORTRAIT);
// Finish activity
mBroadcastActionTrigger.moveTopTaskToBack();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
index 8a5d21c..4336898 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/CrossAppDragAndDropTests.java
@@ -31,7 +31,6 @@
import android.content.ComponentName;
import android.graphics.Point;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.Presubmit;
@@ -93,8 +92,6 @@
private static final String EXTRA_MODE = "mode";
private static final String EXTRA_LOGTAG = "logtag";
- protected DisplayManager mDm;
-
private Map<String, String> mSourceResults;
private Map<String, String> mTargetResults;
@@ -113,7 +110,6 @@
mSourceLogTag = SOURCE_LOG_TAG + mSessionId;
mTargetLogTag = TARGET_LOG_TAG + mSessionId;
- mDm = mContext.getSystemService(DisplayManager.class);
cleanupState();
mUseTaskOrganizer = false;
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
index 45acc41..48a01d3 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayCutoutTests.java
@@ -46,9 +46,11 @@
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.graphics.Insets;
import android.graphics.Point;
import android.graphics.Rect;
@@ -232,6 +234,7 @@
}
private void runTest(int cutoutMode, TestDef test, int orientation) {
+ assumeTrue("Skipping test: orientation not supported", supportsOrientation(orientation));
final TestActivity activity = launchAndWait(mDisplayCutoutActivity,
cutoutMode, orientation);
@@ -508,6 +511,25 @@
return activity;
}
+ private boolean supportsOrientation(int orientation) {
+ String systemFeature = "";
+ switch(orientation) {
+ case SCREEN_ORIENTATION_PORTRAIT:
+ case SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ systemFeature = PackageManager.FEATURE_SCREEN_PORTRAIT;
+ break;
+ case SCREEN_ORIENTATION_LANDSCAPE:
+ case SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ systemFeature = PackageManager.FEATURE_SCREEN_LANDSCAPE;
+ break;
+ default:
+ throw new UnsupportedOperationException("Orientation not supported");
+ }
+
+ return getInstrumentation().getTargetContext().getPackageManager()
+ .hasSystemFeature(systemFeature);
+ }
+
public static class TestActivity extends Activity {
static final String EXTRA_CUTOUT_MODE = "extra.cutout_mode";
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java
index 1ef4920..667ed05 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplaySizeTest.java
@@ -107,7 +107,7 @@
try {
final int initialLength = 500;
final int newLength = 1000;
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ final DisplayManager displayManager = mDm;
virtualDisplay = displayManager.createVirtualDisplay("CtsDisplay", initialLength,
initialLength, 160 /* densityDpi */, null /* surface */, 0 /* flags */);
final Display targetDisplay = virtualDisplay.getDisplay();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
index a72a299..6ad0dc1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayTests.java
@@ -178,15 +178,13 @@
private Configuration getDisplayResourcesConfiguration(int displayWidth, int displayHeight)
throws Exception {
final Context context = getInstrumentation().getContext();
- final DisplayManager displayManager =
- (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
final DisplayContent displayContent = virtualDisplaySession
.setSimulateDisplay(true)
.setSimulationDisplaySize(displayWidth, displayHeight)
.createDisplay();
- final Display display = displayManager.getDisplay(displayContent.mId);
+ final Display display = mDm.getDisplay(displayContent.mId);
Configuration config = context.createDisplayContext(display)
.getResources().getConfiguration();
return config;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java
index 1c9f174..74b3c7b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MinimalPostProcessingTests.java
@@ -27,11 +27,8 @@
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
-import android.hardware.display.DisplayManager;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
-
import org.junit.Test;
@Presubmit
@@ -55,14 +52,11 @@
}
private boolean isMinimalPostProcessingSupported(int displayId) {
- return mContext.getSystemService(DisplayManager.class)
- .getDisplay(displayId)
- .isMinimalPostProcessingSupported();
+ return mDm.getDisplay(displayId).isMinimalPostProcessingSupported();
}
private boolean isMinimalPostProcessingRequested(int displayId) {
- return mContext.getSystemService(DisplayManager.class)
- .isMinimalPostProcessingRequested(displayId);
+ return mDm.isMinimalPostProcessingRequested(displayId);
}
private int getDisplayId(ComponentName name) {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
index 1a6f665..44bd353 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java
@@ -26,7 +26,6 @@
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY;
import static android.server.wm.ActivityLauncher.KEY_NEW_TASK;
-import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT;
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.UiDeviceUtils.pressHomeButton;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
@@ -69,7 +68,6 @@
import android.server.wm.CommandSession.SizeInfo;
import android.server.wm.WindowManagerState.ActivityTask;
import android.server.wm.WindowManagerState.DisplayContent;
-import android.util.DisplayMetrics;
import android.view.SurfaceView;
import com.android.compatibility.common.util.SystemUtil;
@@ -772,9 +770,8 @@
@Test
public void testImmediateLaunchOnNewDisplay() {
// Create new virtual display and immediately launch an activity on it.
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
SurfaceView surfaceView = new SurfaceView(mContext);
- VirtualDisplay virtualDisplay = displayManager.createVirtualDisplay(
+ final VirtualDisplay virtualDisplay = mDm.createVirtualDisplay(
"testImmediateLaunchOnNewDisplay", /*width=*/ 400, /*height=*/ 400,
/*densityDpi=*/ 320, surfaceView.getHolder().getSurface(),
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
@@ -836,8 +833,6 @@
@Test
public void testLaunchPendingIntentActivity() throws Exception {
- final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
-
final DisplayContent displayContent = createManagedVirtualDisplaySession()
.setSimulateDisplay(true)
.createDisplay();
@@ -850,7 +845,7 @@
final int resultCode = 1;
// Activity should be launched on target display according to the caller context.
final Context displayContext =
- mContext.createDisplayContext(displayManager.getDisplay(displayContent.mId));
+ mContext.createDisplayContext(mDm.getDisplay(displayContent.mId));
getPendingIntentActivity(TOP_ACTIVITY).send(displayContext, resultCode, null /* intent */);
waitAndAssertTopResumedActivity(TOP_ACTIVITY, displayContent.mId,
"Activity launched on secondary display and on top");
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
index e9a889d..caf44c9 100755
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayClientTests.java
@@ -37,7 +37,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.DisplayContent;
@@ -199,8 +198,7 @@
.setSimulateDisplay(true)
.createDisplay();
- final Display display = mContext.getSystemService(DisplayManager.class)
- .getDisplay(newDisplay.mId);
+ final Display display = mDm.getDisplay(newDisplay.mId);
final Context newDisplayContext = mContext.createDisplayContext(display);
final InputMethodManager imm = newDisplayContext.getSystemService(InputMethodManager.class);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
index 7f14db4..efe194f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySecurityTests.java
@@ -60,7 +60,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.DisplayContent;
@@ -440,8 +439,7 @@
.setPublicDisplay(false)
.createDisplay();
try {
- final Display display = mContext.getSystemService(DisplayManager.class).getDisplay(
- newDisplay.mId);
+ final Display display = mDm.getDisplay(newDisplay.mId);
final Context newDisplayContext = mContext.createDisplayContext(display);
newDisplayContext.getSystemService(WindowManager.class).addView(new View(mContext),
new ViewGroup.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
index c50f5d5..be945d6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -203,8 +203,8 @@
@Test
public void testNavBarShowingOnDisplayWithDecor() {
assumeHasBars();
- final DisplayContent newDisplay = createManagedExternalDisplaySession()
- .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
+ final DisplayContent newDisplay = createManagedVirtualDisplaySession()
+ .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
mWmState.waitAndAssertNavBarShownOnDisplay(newDisplay.mId);
}
@@ -219,8 +219,8 @@
mWmState.waitForHomeActivityVisible();
final List<WindowState> expected = mWmState.getAllNavigationBarStates();
- createManagedExternalDisplaySession().setPublicDisplay(true)
- .setShowSystemDecorations(false).createVirtualDisplay();
+ createManagedVirtualDisplaySession().setSimulateDisplay(true)
+ .setShowSystemDecorations(false).createDisplay();
waitAndAssertNavBarStatesAreTheSame(expected);
}
@@ -251,9 +251,9 @@
// display, go back to verify whether the nav bar states are unchanged to verify that no nav
// bars were added to a display that was added before executing this method that shouldn't
// have nav bars (i.e. private or without system ui decor).
- try (final ExternalDisplaySession secondDisplaySession = new ExternalDisplaySession()) {
+ try (final VirtualDisplaySession secondDisplaySession = new VirtualDisplaySession()) {
final DisplayContent supportsSysDecorDisplay = secondDisplaySession
- .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
+ .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
mWmState.waitAndAssertNavBarShownOnDisplay(supportsSysDecorDisplay.mId);
// This display has finished his task. Just close it.
}
@@ -287,6 +287,19 @@
assertEquals("No stacks on newly launched virtual display", 0, newDisplay.mRootTasks.size());
}
+ /** Tests launching a home activity on untrusted virtual display. */
+ @Test
+ public void testLaunchHomeActivityOnUntrustedVirtualSecondaryDisplay() {
+ createManagedHomeActivitySession(SECONDARY_HOME_ACTIVITY);
+
+ // Create new virtual display with system decoration support flag.
+ final DisplayContent newDisplay = createManagedExternalDisplaySession()
+ .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
+
+ // Secondary home activity can't be launched on the untrusted virtual display.
+ assertEquals("No stacks on untrusted virtual display", 0, newDisplay.mRootTasks.size());
+ }
+
/**
* Tests launching a single instance home activity on virtual display with system decoration
* support.
@@ -339,10 +352,11 @@
}
private void assertSecondaryHomeResumedOnNewDisplay(ComponentName homeComponentName) {
- // Create new virtual display with system decoration support.
- final DisplayContent newDisplay = createManagedExternalDisplaySession()
+ // Create new simulated display with system decoration support.
+ final DisplayContent newDisplay = createManagedVirtualDisplaySession()
+ .setSimulateDisplay(true)
.setShowSystemDecorations(true)
- .createVirtualDisplay();
+ .createDisplay();
waitAndAssertActivityStateOnDisplay(homeComponentName, STATE_RESUMED,
newDisplay.mId, "Activity launched on secondary display must be resumed");
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
index dbaedc1..6f97aa6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayTestBase.java
@@ -83,6 +83,7 @@
* @see DisplayTests
* @see MultiDisplayKeyguardTests
* @see MultiDisplayLockedKeyguardTests
+ * @see AppConfigurationTests
*/
public class MultiDisplayTestBase extends ActivityManagerTestBase {
@@ -777,6 +778,12 @@
return this;
}
+ /**
+ * @deprecated untrusted virtual display won't have system decorations even it has the flag.
+ * Only use this method to verify that. To test secondary display with system decorations,
+ * please use simulated display.
+ */
+ @Deprecated
ExternalDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
mShowSystemDecorations = showSystemDecorations;
return this;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
index feb8947..ab3d71a 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java
@@ -26,6 +26,7 @@
import static android.server.wm.ComponentNameUtils.getActivityName;
import static android.server.wm.ComponentNameUtils.getWindowName;
import static android.server.wm.UiDeviceUtils.pressWindowButton;
+import static android.server.wm.WindowManagerState.STATE_PAUSED;
import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.WindowManagerState.STATE_STOPPED;
import static android.server.wm.app.Components.ALWAYS_FOCUSABLE_PIP_ACTIVITY;
@@ -97,6 +98,8 @@
import android.util.Log;
import android.util.Size;
+import androidx.test.filters.FlakyTest;
+
import com.android.compatibility.common.util.AppOpsUtils;
import com.android.compatibility.common.util.SystemUtil;
@@ -207,6 +210,8 @@
@Test
public void testPinnedStackInBoundsAfterRotation() {
+ // Launch an activity that is not fixed-orientation so that the display can rotate
+ launchActivity(TEST_ACTIVITY);
// Launch an activity into the pinned stack
launchActivity(PIP_ACTIVITY,
EXTRA_ENTER_PIP, "true",
@@ -421,26 +426,25 @@
// Go home and ensure that there is a pinned stack
separateTestJournal();
launchHomeActivity();
- waitForEnterPip(PIP_ACTIVITY);
+ waitForEnterPipAnimationComplete(PIP_ACTIVITY);
assertPinnedStackExists();
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(PIP_ACTIVITY);
- // Check that onPictureInPictureRequested was called to try to enter pip from there
- assertEquals("onPictureInPictureRequested", 1,
- lifecycleCounts.getCount(ActivityCallback.ON_PICTURE_IN_PICTURE_REQUESTED));
- // Check that onPause + onUserLeaveHint were called only once. These assertions verify that
- // onPictureInPictureRequested doesn't attempt to trigger these callbacks because going
- // home is already causing them to be called
- assertEquals("onPause", 1, lifecycleCounts.getCount(ActivityCallback.ON_PAUSE));
- assertEquals("onUserLeaveHint", 1,
- lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT));
-
- final int lastUserLeaveHintIndex =
- lifecycleCounts.getLastIndex(ActivityCallback.ON_USER_LEAVE_HINT);
- final int lastPipRequestedIndex =
- lifecycleCounts.getLastIndex(ActivityCallback.ON_PICTURE_IN_PICTURE_REQUESTED);
- // Check that onPictureInPictureRequested was called first and onUserLeaveHint after
- assertThat(lastPipRequestedIndex, lessThan(lastUserLeaveHintIndex));
+ // Check the order of the callbacks accounting for a task overlay activity that might show.
+ // The PIP request (with a user leave hint) should come before the pip mode change.
+ final int firstUserLeaveIndex =
+ lifecycleCounts.getFirstIndex(ActivityCallback.ON_USER_LEAVE_HINT);
+ final int firstPipRequestedIndex =
+ lifecycleCounts.getFirstIndex(ActivityCallback.ON_PICTURE_IN_PICTURE_REQUESTED);
+ final int firstPipModeChangedIndex =
+ lifecycleCounts.getFirstIndex(ActivityCallback.ON_PICTURE_IN_PICTURE_MODE_CHANGED);
+ assertTrue("missing request", firstPipRequestedIndex != -1);
+ assertTrue("missing user leave", firstUserLeaveIndex != -1);
+ assertTrue("missing pip mode changed", firstPipModeChangedIndex != -1);
+ assertTrue("pip requested not before pause",
+ firstPipRequestedIndex < firstUserLeaveIndex);
+ assertTrue("unexpected user leave hint",
+ firstUserLeaveIndex < firstPipModeChangedIndex);
}
@Test
@@ -455,26 +459,24 @@
// Call onPictureInPictureRequested and verify activity enters pip
separateTestJournal();
mBroadcastActionTrigger.doAction(ACTION_ON_PIP_REQUESTED);
- waitForEnterPip(PIP_ACTIVITY);
+ waitForEnterPipAnimationComplete(PIP_ACTIVITY);
assertPinnedStackExists();
final ActivityLifecycleCounts lifecycleCounts = new ActivityLifecycleCounts(PIP_ACTIVITY);
- // Check that onPictureInPictureRequested was called
- assertEquals("onPictureInPictureRequested", 1,
- lifecycleCounts.getCount(ActivityCallback.ON_PICTURE_IN_PICTURE_REQUESTED));
- // Verify that cycling through userLeaveHint was not necessary since the activity overrode
- // onPictureInPictureRequested and entered PIP mode from there
- assertEquals("onUserLeaveHint", 0,
- lifecycleCounts.getCount(ActivityCallback.ON_USER_LEAVE_HINT));
- // Verify onPause does get called when the activity eventually enters PIP mode
- assertEquals("onPause", 1, lifecycleCounts.getCount(ActivityCallback.ON_PAUSE));
-
- final int lastOnPauseIndex =
- lifecycleCounts.getLastIndex(ActivityCallback.ON_PAUSE);
- final int lastPipRequestedIndex =
- lifecycleCounts.getLastIndex(ActivityCallback.ON_PICTURE_IN_PICTURE_REQUESTED);
- // Check that onPictureInPictureRequested was called first and onPause after
- assertThat(lastPipRequestedIndex, lessThan(lastOnPauseIndex));
+ // Check the order of the callbacks accounting for a task overlay activity that might show.
+ // The PIP request (without a user leave hint) should come before the pip mode change.
+ final int firstUserLeaveIndex =
+ lifecycleCounts.getFirstIndex(ActivityCallback.ON_USER_LEAVE_HINT);
+ final int firstPipRequestedIndex =
+ lifecycleCounts.getFirstIndex(ActivityCallback.ON_PICTURE_IN_PICTURE_REQUESTED);
+ final int firstPipModeChangedIndex =
+ lifecycleCounts.getFirstIndex(ActivityCallback.ON_PICTURE_IN_PICTURE_MODE_CHANGED);
+ assertTrue("missing request", firstPipRequestedIndex != -1);
+ assertTrue("missing pip mode changed", firstPipModeChangedIndex != -1);
+ assertTrue("pip requested not before pause",
+ firstPipRequestedIndex < firstPipModeChangedIndex);
+ assertTrue("unexpected user leave hint",
+ firstUserLeaveIndex == -1 || firstUserLeaveIndex > firstPipModeChangedIndex);
}
@Test
@@ -931,6 +933,7 @@
separateTestJournal();
removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
waitForExitPipToFullscreen(PIP_ACTIVITY);
+ waitForValidPictureInPictureCallbacks(PIP_ACTIVITY);
// Confirm that we get stop before the multi-window and picture-in-picture mode change
// callbacks
@@ -995,6 +998,7 @@
}
@Test
+ @FlakyTest(bugId=156314330)
public void testFinishPipActivityWithTaskOverlay() throws Exception {
// Launch PiP activity
launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
@@ -1038,6 +1042,7 @@
assertEquals("onPause", 0, lifecycleCounts.getCount(ActivityCallback.ON_PAUSE));
}
+ @FlakyTest(bugId = 156003518)
@Test
public void testPinnedStackWithDockedStack() throws Exception {
assumeTrue(supportsSplitScreenMultiWindow());
@@ -1134,7 +1139,7 @@
EXTRA_ENTER_PIP, "true",
EXTRA_START_ACTIVITY, getActivityName(PIP_ACTIVITY),
EXTRA_FINISH_SELF_ON_RESUME, "true");
- waitForEnterPip(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ waitForEnterPip(PIP_ACTIVITY);
assertPinnedStackExists();
// Launch the root activity again, of the matching task and ensure that we expand to
@@ -1142,7 +1147,7 @@
int activityTaskId = mWmState.getTaskByActivity(PIP_ACTIVITY).mTaskId;
launchHomeActivity();
launchActivity(TEST_ACTIVITY_WITH_SAME_AFFINITY);
- waitForExitPipToFullscreen(TEST_ACTIVITY_WITH_SAME_AFFINITY);
+ waitForExitPipToFullscreen(PIP_ACTIVITY);
assertPinnedStackDoesNotExist();
assertEquals(activityTaskId, mWmState.getTaskByActivity(
PIP_ACTIVITY).mTaskId);
@@ -1277,32 +1282,26 @@
* subsequent animation to start).
*/
private void waitForEnterPip(ComponentName activityName) {
- mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
- .setWindowingMode(WINDOWING_MODE_PINNED)
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .build());
+ mWmState.waitForWithAmState(wmState -> {
+ ActivityTask task = wmState.getTaskByActivity(activityName);
+ return task != null && task.getWindowingMode() == WINDOWING_MODE_PINNED;
+ }, "checking task windowing mode");
}
/**
* Waits until the picture-in-picture animation has finished.
- * TODO(b/149947030): use the transition completed signal from TaskOrganizer
*/
private void waitForEnterPipAnimationComplete(ComponentName activityName) {
waitForEnterPip(activityName);
- final Rect pinnedStackBounds = new Rect();
mWmState.waitForWithAmState(wmState -> {
- final Rect displayStableBounds = wmState.getStableBounds();
- Rect newBounds = wmState.getStandardStackByWindowingMode(WINDOWING_MODE_PINNED)
- .getBounds();
- if (pinnedStackBounds.equals(newBounds)
- && (displayStableBounds.width() / 2) > newBounds.width()
- && (displayStableBounds.height() / 2) > newBounds.height()) {
- return true;
- } else if (newBounds != null) {
- pinnedStackBounds.set(newBounds);
+ ActivityTask task = wmState.getTaskByActivity(activityName);
+ if (task == null) {
+ return false;
}
- return false;
- }, "stack bounds stabilized, consider in pinned mode");
+ WindowManagerState.Activity activity = task.getActivity(activityName);
+ return activity.getWindowingMode() == WINDOWING_MODE_PINNED
+ && activity.getState().equals(STATE_PAUSED);
+ }, "checking activity windowing mode");
}
/**
@@ -1316,28 +1315,20 @@
/**
* Waits until the picture-in-picture animation to fullscreen has finished.
- * TODO(b/149947030): use the transition completed signal from TaskOrganizer
*/
private void waitForExitPipToFullscreen(ComponentName activityName) {
- mWmState.waitForValidState(new WaitForValidActivityState.Builder(activityName)
- .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
- .setActivityType(ACTIVITY_TYPE_STANDARD)
- .build());
- final Rect stackBounds = new Rect();
mWmState.waitForWithAmState(wmState -> {
- final Rect displayStableBounds = wmState.getStableBounds();
final ActivityTask task = wmState.getTaskByActivity(activityName);
- if (task == null) return false;
- Rect newBounds = task.getBounds();
- if (stackBounds.equals(newBounds)
- && (displayStableBounds.width() / 2) < newBounds.width()
- && (displayStableBounds.height() / 2) < newBounds.height()) {
- return true;
- } else if (newBounds != null) {
- stackBounds.set(newBounds);
+ if (task == null) {
+ return false;
}
- return false;
- }, "stack bounds stabilized, consider in fullscreen mode");
+ final WindowManagerState.Activity activity = task.getActivity(activityName);
+ return activity.getWindowingMode() != WINDOWING_MODE_PINNED;
+ }, "checking activity windowing mode");
+ mWmState.waitForWithAmState(wmState -> {
+ final ActivityTask task = wmState.getTaskByActivity(activityName);
+ return task != null && task.getWindowingMode() != WINDOWING_MODE_PINNED;
+ }, "checking task windowing mode");
}
/**
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java b/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
index 3619bed..5e813d1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/PresentationTest.java
@@ -21,7 +21,6 @@
import static org.junit.Assert.assertTrue;
import android.content.Intent;
-import android.hardware.display.DisplayManager;
import android.platform.test.annotations.Presubmit;
import android.server.wm.app.Components;
import android.view.Display;
@@ -38,8 +37,7 @@
@Test
public void testPresentationFollowsDisplayFlag() {
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
- for (Display display : displayManager.getDisplays()) {
+ for (Display display : mDm.getDisplays()) {
launchPresentationActivity(display.getDisplayId());
if ((display.getFlags() & Display.FLAG_PRESENTATION) != Display.FLAG_PRESENTATION) {
assertNoPresentationDisplayed();
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
index 474d9d2..ef4760e 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
@@ -54,6 +54,8 @@
import androidx.test.rule.ActivityTestRule;
import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.compatibility.common.util.WidgetTestUtils;
+
import android.platform.test.annotations.Presubmit;
@@ -78,7 +80,7 @@
private View mEmbeddedView;
private WindowManager.LayoutParams mEmbeddedLayoutParams;
- private boolean mClicked = false;
+ private volatile boolean mClicked = false;
/*
* Configurable state to control how the surfaceCreated callback
@@ -220,7 +222,8 @@
mActivityRule.runOnUiThread(() -> {
mVr.relayout(bigEdgeLength, bigEdgeLength);
});
- mInstrumentation.waitForIdleSync();
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule,
+ mEmbeddedView, null);
// But after the click should hit.
CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java b/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java
index 9617c7b..5e3e030 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/VrDisplayTests.java
@@ -29,6 +29,7 @@
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
+import android.platform.test.annotations.FlakyTest;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.server.wm.WindowManagerState.DisplayContent;
@@ -46,6 +47,7 @@
* atest CtsWindowManagerDeviceTestCases:VrDisplayTests
*/
@Presubmit
+@FlakyTest
@android.server.wm.annotation.Group3
public class VrDisplayTests extends MultiDisplayTestBase {
private static final int VR_VIRTUAL_DISPLAY_WIDTH = 700;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTestBase.java
index 2422945..83407f6 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTestBase.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowContextTestBase.java
@@ -19,19 +19,17 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.content.Context;
-import android.hardware.display.DisplayManager;
import android.view.Display;
/** Base class for window context tests */
class WindowContextTestBase extends MultiDisplayTestBase {
- private DisplayManager mDisplayManager = mContext.getSystemService(DisplayManager.class);
Context createWindowContext(int displayId) {
return createWindowContext(displayId, TYPE_APPLICATION_OVERLAY);
}
Context createWindowContext(int displayId, int type) {
- final Display display = mDisplayManager.getDisplay(displayId);
+ final Display display = mDm.getDisplay(displayId);
return mContext.createDisplayContext(display).createWindowContext(
type, null /* options */);
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
index 7b38972..6e21f4b 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationControllerTests.java
@@ -44,7 +44,7 @@
import android.graphics.Insets;
import android.os.CancellationSignal;
import android.platform.test.annotations.Presubmit;
-import android.server.wm.WindowInsetsAnimationTests.TestActivity;
+import android.server.wm.WindowInsetsAnimationTestBase.TestActivity;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
new file mode 100644
index 0000000..32255d2
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationImeTests.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.graphics.Insets.NONE;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
+
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.withSettings;
+
+import android.content.pm.PackageManager;
+import android.platform.test.annotations.Presubmit;
+import android.view.WindowInsets;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSessionRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.InOrder;
+
+/**
+ * Same as {@link WindowInsetsAnimationTests} but IME specific.
+ *
+ * Build/Install/Run:
+ * atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationImeTests
+ */
+@Presubmit
+public class WindowInsetsAnimationImeTests extends WindowInsetsAnimationTestBase {
+
+ @Rule
+ public final MockImeSessionRule mMockImeSessionRule = new MockImeSessionRule(
+ InstrumentationRegistry.getInstrumentation().getContext(),
+ InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+ new ImeSettings.Builder()
+ );
+
+ @Before
+ public void setup() throws Exception {
+ super.setUp();
+ assumeTrue("MockIme cannot be used for devices that do not support installable IMEs",
+ mInstrumentation.getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_INPUT_METHODS));
+ mActivity = startActivity(TestActivity.class);
+ mRootView = mActivity.getWindow().getDecorView();
+ }
+
+ @Test
+ public void testImeAnimationCallbacksShowAndHide() {
+ WindowInsets before = mActivity.mLastWindowInsets;
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().show(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+ commonAnimationAssertions(mActivity, before, true /* show */, ime());
+ mActivity.mCallback.animationDone = false;
+
+ before = mActivity.mLastWindowInsets;
+
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().hide(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
+
+ commonAnimationAssertions(mActivity, before, false /* show */, ime());
+ }
+
+ @Test
+ @FlakyTest(detail = "Promote once confirmed non-flaky")
+ public void testAnimationCallbacks_overlapping_opposite() {
+ WindowInsets before = mActivity.mLastWindowInsets;
+
+ MultiAnimCallback callbackInner = new MultiAnimCallback();
+ MultiAnimCallback callback = mock(MultiAnimCallback.class,
+ withSettings()
+ .spiedInstance(callbackInner)
+ .defaultAnswer(CALLS_REAL_METHODS)
+ .verboseLogging());
+ mActivity.mView.setWindowInsetsAnimationCallback(callback);
+
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().hide(navigationBars()));
+ getInstrumentation().runOnMainSync(
+ () -> mRootView.getWindowInsetsController().show(ime()));
+
+ waitForOrFail("Waiting until animation done", () -> callback.animationDone);
+
+ WindowInsets after = mActivity.mLastWindowInsets;
+
+ InOrder inOrder = inOrder(callback, mActivity.mListener);
+
+ inOrder.verify(callback).onPrepare(eq(callback.navBarAnim));
+
+ inOrder.verify(mActivity.mListener).onApplyWindowInsets(any(), argThat(
+ argument -> NONE.equals(argument.getInsets(navigationBars()))
+ && NONE.equals(argument.getInsets(ime()))));
+
+ inOrder.verify(callback).onStart(eq(callback.navBarAnim), argThat(
+ argument -> argument.getLowerBound().equals(NONE)
+ && argument.getUpperBound().equals(before.getInsets(navigationBars()))));
+
+ inOrder.verify(callback).onPrepare(eq(callback.imeAnim));
+ inOrder.verify(mActivity.mListener).onApplyWindowInsets(
+ any(), eq(mActivity.mLastWindowInsets));
+
+ inOrder.verify(callback).onStart(eq(callback.imeAnim), argThat(
+ argument -> argument.getLowerBound().equals(NONE)
+ && !argument.getUpperBound().equals(NONE)));
+
+ inOrder.verify(callback).onEnd(eq(callback.navBarAnim));
+ inOrder.verify(callback).onEnd(eq(callback.imeAnim));
+
+ assertAnimationSteps(callback.navAnimSteps, false /* showAnimation */);
+ assertAnimationSteps(callback.imeAnimSteps, false /* showAnimation */);
+
+ assertEquals(before.getInsets(navigationBars()),
+ callback.navAnimSteps.get(0).insets.getInsets(navigationBars()));
+ assertEquals(after.getInsets(navigationBars()),
+ callback.navAnimSteps.get(callback.navAnimSteps.size() - 1).insets
+ .getInsets(navigationBars()));
+
+ assertEquals(before.getInsets(ime()),
+ callback.imeAnimSteps.get(0).insets.getInsets(ime()));
+ assertEquals(after.getInsets(ime()),
+ callback.imeAnimSteps.get(callback.imeAnimSteps.size() - 1).insets
+ .getInsets(ime()));
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
new file mode 100644
index 0000000..404fd70
--- /dev/null
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTestBase.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.server.wm;
+
+import static android.graphics.Insets.NONE;
+import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowInsets.Type.navigationBars;
+import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
+
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
+import android.server.wm.WindowInsetsAnimationTestBase.AnimCallback.AnimationStep;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import org.junit.Assert;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.BiPredicate;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+/**
+ * Base class for tests in {@link WindowInsetsAnimation} and {@link WindowInsetsAnimation.Callback}.
+ */
+public class WindowInsetsAnimationTestBase extends WindowManagerTestBase {
+
+ protected TestActivity mActivity;
+ protected View mRootView;
+
+ protected void commonAnimationAssertions(TestActivity activity, WindowInsets before,
+ boolean show, int types) {
+
+ AnimCallback callback = activity.mCallback;
+
+ InOrder inOrder = inOrder(activity.mCallback, activity.mListener);
+
+ WindowInsets after = activity.mLastWindowInsets;
+ inOrder.verify(callback).onPrepare(eq(callback.lastAnimation));
+ inOrder.verify(activity.mListener).onApplyWindowInsets(any(), any());
+
+ inOrder.verify(callback).onStart(eq(callback.lastAnimation), argThat(
+ argument -> argument.getLowerBound().equals(NONE)
+ && argument.getUpperBound().equals(show
+ ? after.getInsets(types)
+ : before.getInsets(types))));
+
+ inOrder.verify(callback, atLeast(2)).onProgress(any(), argThat(
+ argument -> argument.size() == 1 && argument.get(0) == callback.lastAnimation));
+ inOrder.verify(callback).onEnd(eq(callback.lastAnimation));
+
+ if ((types & systemBars()) != 0) {
+ assertTrue((callback.lastAnimation.getTypeMask() & systemBars()) != 0);
+ }
+ if ((types & ime()) != 0) {
+ assertTrue((callback.lastAnimation.getTypeMask() & ime()) != 0);
+ }
+ assertTrue(callback.lastAnimation.getDurationMillis() > 0);
+ assertNotNull(callback.lastAnimation.getInterpolator());
+ assertBeforeAfterState(callback.animationSteps, before, after);
+ assertAnimationSteps(callback.animationSteps, show /* increasing */);
+ }
+
+ private void assertBeforeAfterState(ArrayList<AnimationStep> steps, WindowInsets before,
+ WindowInsets after) {
+ assertEquals(before, steps.get(0).insets);
+ assertEquals(after, steps.get(steps.size() - 1).insets);
+ }
+
+ protected void assertAnimationSteps(ArrayList<AnimationStep> steps, boolean showAnimation) {
+ assertTrue(steps.size() >= 2);
+ assertEquals(0f, steps.get(0).fraction, 0f);
+ assertEquals(0f, steps.get(0).interpolatedFraction, 0f);
+ assertEquals(1f, steps.get(steps.size() - 1).fraction, 0f);
+ assertEquals(1f, steps.get(steps.size() - 1).interpolatedFraction, 0f);
+ if (showAnimation) {
+ assertEquals(1f, steps.get(steps.size() - 1).alpha, 0f);
+ } else {
+ assertEquals(1f, steps.get(0).alpha, 0f);
+ }
+
+ assertListElements(steps, step -> step.fraction,
+ (current, next) -> next >= current);
+ assertListElements(steps, step -> step.interpolatedFraction,
+ (current, next) -> next >= current);
+ assertListElements(steps, step -> step.alpha, alpha -> alpha >= 0f);
+ assertListElements(steps, step -> step.insets, compareInsets(systemBars(), showAnimation));
+ }
+
+ private BiPredicate<WindowInsets, WindowInsets> compareInsets(int types,
+ boolean showAnimation) {
+ if (showAnimation) {
+ return (current, next) ->
+ next.getInsets(types).left >= current.getInsets(types).left
+ && next.getInsets(types).top >= current.getInsets(types).top
+ && next.getInsets(types).right >= current.getInsets(types).right
+ && next.getInsets(types).bottom >= current.getInsets(types).bottom;
+ } else {
+ return (current, next) ->
+ next.getInsets(types).left <= current.getInsets(types).left
+ && next.getInsets(types).top <= current.getInsets(types).top
+ && next.getInsets(types).right <= current.getInsets(types).right
+ && next.getInsets(types).bottom <= current.getInsets(types).bottom;
+ }
+ }
+
+ private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
+ Predicate<V> predicate) {
+ for (int i = 0; i <= list.size() - 1; i++) {
+ V value = getter.apply(list.get(i));
+ assertTrue("Predicate.test failed i=" + i + " value="
+ + value, predicate.test(value));
+ }
+ }
+
+ private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
+ BiPredicate<V, V> comparator) {
+ for (int i = 0; i <= list.size() - 2; i++) {
+ V current = getter.apply(list.get(i));
+ V next = getter.apply(list.get(i + 1));
+ assertTrue(comparator.test(current, next));
+ }
+ }
+
+ public static class AnimCallback extends WindowInsetsAnimation.Callback {
+
+ public static class AnimationStep {
+
+ AnimationStep(WindowInsets insets, float fraction, float interpolatedFraction,
+ float alpha) {
+ this.insets = insets;
+ this.fraction = fraction;
+ this.interpolatedFraction = interpolatedFraction;
+ this.alpha = alpha;
+ }
+
+ WindowInsets insets;
+ float fraction;
+ float interpolatedFraction;
+ float alpha;
+ }
+
+ WindowInsetsAnimation lastAnimation;
+ volatile boolean animationDone;
+ final ArrayList<AnimationStep> animationSteps = new ArrayList<>();
+
+ public AnimCallback(int dispatchMode) {
+ super(dispatchMode);
+ }
+
+ @Override
+ public void onPrepare(WindowInsetsAnimation animation) {
+ animationSteps.clear();
+ lastAnimation = animation;
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+ return bounds;
+ }
+
+ @Override
+ public WindowInsets onProgress(WindowInsets insets,
+ List<WindowInsetsAnimation> runningAnimations) {
+ animationSteps.add(new AnimationStep(insets, lastAnimation.getFraction(),
+ lastAnimation.getInterpolatedFraction(), lastAnimation.getAlpha()));
+ return WindowInsets.CONSUMED;
+ }
+
+ @Override
+ public void onEnd(WindowInsetsAnimation animation) {
+ animationDone = true;
+ }
+ }
+
+ protected static class MultiAnimCallback extends WindowInsetsAnimation.Callback {
+
+ WindowInsetsAnimation statusBarAnim;
+ WindowInsetsAnimation navBarAnim;
+ WindowInsetsAnimation imeAnim;
+ volatile boolean animationDone;
+ final ArrayList<AnimationStep> statusAnimSteps = new ArrayList<>();
+ final ArrayList<AnimationStep> navAnimSteps = new ArrayList<>();
+ final ArrayList<AnimationStep> imeAnimSteps = new ArrayList<>();
+ Runnable startRunnable;
+ final ArraySet<WindowInsetsAnimation> runningAnims = new ArraySet<>();
+
+ public MultiAnimCallback() {
+ super(DISPATCH_MODE_STOP);
+ }
+
+ @Override
+ public void onPrepare(WindowInsetsAnimation animation) {
+ if ((animation.getTypeMask() & statusBars()) != 0) {
+ statusBarAnim = animation;
+ }
+ if ((animation.getTypeMask() & navigationBars()) != 0) {
+ navBarAnim = animation;
+ }
+ if ((animation.getTypeMask() & ime()) != 0) {
+ imeAnim = animation;
+ }
+ }
+
+ @Override
+ public WindowInsetsAnimation.Bounds onStart(
+ WindowInsetsAnimation animation, WindowInsetsAnimation.Bounds bounds) {
+ if (startRunnable != null) {
+ startRunnable.run();
+ }
+ runningAnims.add(animation);
+ return bounds;
+ }
+
+ @Override
+ public WindowInsets onProgress(WindowInsets insets,
+ List<WindowInsetsAnimation> runningAnimations) {
+ if (statusBarAnim != null) {
+ statusAnimSteps.add(new AnimationStep(insets, statusBarAnim.getFraction(),
+ statusBarAnim.getInterpolatedFraction(), statusBarAnim.getAlpha()));
+ }
+ if (navBarAnim != null) {
+ navAnimSteps.add(new AnimationStep(insets, navBarAnim.getFraction(),
+ navBarAnim.getInterpolatedFraction(), navBarAnim.getAlpha()));
+ }
+ if (imeAnim != null) {
+ imeAnimSteps.add(new AnimationStep(insets, imeAnim.getFraction(),
+ imeAnim.getInterpolatedFraction(), imeAnim.getAlpha()));
+ }
+
+ assertEquals(runningAnims.size(), runningAnimations.size());
+ for (int i = runningAnimations.size() - 1; i >= 0; i--) {
+ Assert.assertNotEquals(-1,
+ runningAnims.indexOf(runningAnimations.get(i)));
+ }
+
+ return WindowInsets.CONSUMED;
+ }
+
+ @Override
+ public void onEnd(WindowInsetsAnimation animation) {
+ runningAnims.remove(animation);
+ if (runningAnims.isEmpty()) {
+ animationDone = true;
+ }
+ }
+ }
+
+ public static class TestActivity extends FocusableActivity {
+
+ AnimCallback mCallback =
+ spy(new AnimCallback(WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP));
+ WindowInsets mLastWindowInsets;
+
+ View.OnApplyWindowInsetsListener mListener;
+ LinearLayout mView;
+ View mChild;
+ EditText mEditor;
+
+ public class InsetsListener implements View.OnApplyWindowInsetsListener {
+
+ @Override
+ public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+ mLastWindowInsets = insets;
+ return WindowInsets.CONSUMED;
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mListener = spy(new InsetsListener());
+ mView = new LinearLayout(this);
+ mView.setWindowInsetsAnimationCallback(mCallback);
+ mView.setOnApplyWindowInsetsListener(mListener);
+ mChild = new TextView(this);
+ mEditor = new EditText(this);
+ mView.addView(mChild);
+
+ getWindow().setDecorFitsSystemWindows(false);
+ getWindow().getAttributes().layoutInDisplayCutoutMode =
+ LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ setContentView(mView);
+ mEditor.requestFocus();
+ }
+ }
+}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
index 86fc5ee..e3a5328 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java
@@ -17,54 +17,37 @@
package android.server.wm;
import static android.graphics.Insets.NONE;
-import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_CONTINUE_ON_SUBTREE;
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
-import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.withSettings;
-import android.os.Bundle;
import android.platform.test.annotations.Presubmit;
-import android.server.wm.WindowInsetsAnimationTests.AnimCallback.AnimationStep;
-import android.util.ArraySet;
-import android.view.View;
-import android.view.View.OnApplyWindowInsetsListener;
import android.view.WindowInsets;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
import android.view.WindowInsetsAnimation.Callback;
-import android.widget.EditText;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InOrder;
-import java.util.ArrayList;
import java.util.List;
-import java.util.function.BiPredicate;
-import java.util.function.Function;
-import java.util.function.Predicate;
import androidx.test.filters.FlakyTest;
@@ -75,13 +58,10 @@
* atest CtsWindowManagerDeviceTestCases:WindowInsetsAnimationTests
*/
@Presubmit
-public class WindowInsetsAnimationTests extends WindowManagerTestBase {
-
- TestActivity mActivity;
- View mRootView;
+public class WindowInsetsAnimationTests extends WindowInsetsAnimationTestBase {
@Before
- public void setUp() throws Exception {
+ public void setup() throws Exception {
super.setUp();
mActivity = startActivity(TestActivity.class);
mRootView = mActivity.getWindow().getDecorView();
@@ -118,26 +98,6 @@
}
@Test
- public void testImeAnimationCallbacksShowAndHide() {
- WindowInsets before = mActivity.mLastWindowInsets;
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().show(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
- commonAnimationAssertions(mActivity, before, true /* show */, ime());
- mActivity.mCallback.animationDone = false;
-
- before = mActivity.mLastWindowInsets;
-
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().hide(ime()));
-
- waitForOrFail("Waiting until animation done", () -> mActivity.mCallback.animationDone);
-
- commonAnimationAssertions(mActivity, before, false /* show */, ime());
- }
-
- @Test
@FlakyTest(detail = "Promote once confirmed non-flaky")
public void testAnimationCallbacks_overlapping() {
WindowInsets before = mActivity.mLastWindowInsets;
@@ -199,67 +159,6 @@
}
@Test
- @FlakyTest(detail = "Promote once confirmed non-flaky")
- public void testAnimationCallbacks_overlapping_opposite() {
- WindowInsets before = mActivity.mLastWindowInsets;
-
- MultiAnimCallback callbackInner = new MultiAnimCallback();
- MultiAnimCallback callback = mock(MultiAnimCallback.class,
- withSettings()
- .spiedInstance(callbackInner)
- .defaultAnswer(CALLS_REAL_METHODS)
- .verboseLogging());
- mActivity.mView.setWindowInsetsAnimationCallback(callback);
-
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().hide(navigationBars()));
- getInstrumentation().runOnMainSync(
- () -> mRootView.getWindowInsetsController().show(ime()));
-
- waitForOrFail("Waiting until animation done", () -> callback.animationDone);
-
- WindowInsets after = mActivity.mLastWindowInsets;
-
- InOrder inOrder = inOrder(callback, mActivity.mListener);
-
- inOrder.verify(callback).onPrepare(eq(callback.navBarAnim));
-
- inOrder.verify(mActivity.mListener).onApplyWindowInsets(any(), argThat(
- argument -> NONE.equals(argument.getInsets(navigationBars()))
- && NONE.equals(argument.getInsets(ime()))));
-
- inOrder.verify(callback).onStart(eq(callback.navBarAnim), argThat(
- argument -> argument.getLowerBound().equals(NONE)
- && argument.getUpperBound().equals(before.getInsets(navigationBars()))));
-
- inOrder.verify(callback).onPrepare(eq(callback.imeAnim));
- inOrder.verify(mActivity.mListener).onApplyWindowInsets(
- any(), eq(mActivity.mLastWindowInsets));
-
- inOrder.verify(callback).onStart(eq(callback.imeAnim), argThat(
- argument -> argument.getLowerBound().equals(NONE)
- && !argument.getUpperBound().equals(NONE)));
-
- inOrder.verify(callback).onEnd(eq(callback.navBarAnim));
- inOrder.verify(callback).onEnd(eq(callback.imeAnim));
-
- assertAnimationSteps(callback.navAnimSteps, false /* showAnimation */);
- assertAnimationSteps(callback.imeAnimSteps, false /* showAnimation */);
-
- assertEquals(before.getInsets(navigationBars()),
- callback.navAnimSteps.get(0).insets.getInsets(navigationBars()));
- assertEquals(after.getInsets(navigationBars()),
- callback.navAnimSteps.get(callback.navAnimSteps.size() - 1).insets
- .getInsets(navigationBars()));
-
- assertEquals(before.getInsets(ime()),
- callback.imeAnimSteps.get(0).insets.getInsets(ime()));
- assertEquals(after.getInsets(ime()),
- callback.imeAnimSteps.get(callback.imeAnimSteps.size() - 1).insets
- .getInsets(ime()));
- }
-
- @Test
public void testAnimationCallbacks_consumedByDecor() {
getInstrumentation().runOnMainSync(() -> {
mActivity.getWindow().setDecorFitsSystemWindows(true);
@@ -326,256 +225,5 @@
insets -> NONE.equals(insets.getInsets(navigationBars()))), any());
}
- private void commonAnimationAssertions(TestActivity activity, WindowInsets before,
- boolean show, int types) {
- AnimCallback callback = activity.mCallback;
-
- InOrder inOrder = inOrder(activity.mCallback, activity.mListener);
-
- WindowInsets after = activity.mLastWindowInsets;
- inOrder.verify(callback).onPrepare(eq(callback.lastAnimation));
- inOrder.verify(activity.mListener).onApplyWindowInsets(any(), any());
-
- inOrder.verify(callback).onStart(eq(callback.lastAnimation), argThat(
- argument -> argument.getLowerBound().equals(NONE)
- && argument.getUpperBound().equals(show
- ? after.getInsets(types)
- : before.getInsets(types))));
-
- inOrder.verify(callback, atLeast(2)).onProgress(any(), argThat(
- argument -> argument.size() == 1 && argument.get(0) == callback.lastAnimation));
- inOrder.verify(callback).onEnd(eq(callback.lastAnimation));
-
- if ((types & systemBars()) != 0) {
- assertTrue((callback.lastAnimation.getTypeMask() & systemBars()) != 0);
- }
- if ((types & ime()) != 0) {
- assertTrue((callback.lastAnimation.getTypeMask() & ime()) != 0);
- }
- assertTrue(callback.lastAnimation.getDurationMillis() > 0);
- assertNotNull(callback.lastAnimation.getInterpolator());
- assertBeforeAfterState(callback.animationSteps, before, after);
- assertAnimationSteps(callback.animationSteps, show /* increasing */);
- }
-
- private void assertBeforeAfterState(ArrayList<AnimationStep> steps, WindowInsets before,
- WindowInsets after) {
- assertEquals(before, steps.get(0).insets);
- assertEquals(after, steps.get(steps.size() - 1).insets);
- }
-
- private void assertAnimationSteps(ArrayList<AnimationStep> steps, boolean showAnimation) {
- assertTrue(steps.size() >= 2);
- assertEquals(0f, steps.get(0).fraction, 0f);
- assertEquals(0f, steps.get(0).interpolatedFraction, 0f);
- assertEquals(1f, steps.get(steps.size() - 1).fraction, 0f);
- assertEquals(1f, steps.get(steps.size() - 1).interpolatedFraction, 0f);
- if (showAnimation) {
- assertEquals(1f, steps.get(steps.size() - 1).alpha, 0f);
- } else {
- assertEquals(1f, steps.get(0).alpha, 0f);
- }
-
- assertListElements(steps, step -> step.fraction,
- (current, next) -> next >= current);
- assertListElements(steps, step -> step.interpolatedFraction,
- (current, next) -> next >= current);
- assertListElements(steps, step -> step.alpha, alpha -> alpha >= 0f);
- assertListElements(steps, step -> step.insets, compareInsets(systemBars(), showAnimation));
- }
-
- private BiPredicate<WindowInsets, WindowInsets> compareInsets(int types,
- boolean showAnimation) {
- if (showAnimation) {
- return (current, next) ->
- next.getInsets(types).left >= current.getInsets(types).left
- && next.getInsets(types).top >= current.getInsets(types).top
- && next.getInsets(types).right >= current.getInsets(types).right
- && next.getInsets(types).bottom >= current.getInsets(types).bottom;
- } else {
- return (current, next) ->
- next.getInsets(types).left <= current.getInsets(types).left
- && next.getInsets(types).top <= current.getInsets(types).top
- && next.getInsets(types).right <= current.getInsets(types).right
- && next.getInsets(types).bottom <= current.getInsets(types).bottom;
- }
- }
-
- private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
- Predicate<V> predicate) {
- for (int i = 0; i <= list.size() - 1; i++) {
- V value = getter.apply(list.get(i));
- assertTrue("Predicate.test failed i=" + i + " value=" + value, predicate.test(value));
- }
- }
-
- private <T, V> void assertListElements(ArrayList<T> list, Function<T, V> getter,
- BiPredicate<V, V> comparator) {
- for (int i = 0; i <= list.size() - 2; i++) {
- V current = getter.apply(list.get(i));
- V next = getter.apply(list.get(i + 1));
- assertTrue(comparator.test(current, next));
- }
- }
-
- public static class AnimCallback extends WindowInsetsAnimation.Callback {
-
- public static class AnimationStep {
-
- AnimationStep(WindowInsets insets, float fraction, float interpolatedFraction,
- float alpha) {
- this.insets = insets;
- this.fraction = fraction;
- this.interpolatedFraction = interpolatedFraction;
- this.alpha = alpha;
- }
-
- WindowInsets insets;
- float fraction;
- float interpolatedFraction;
- float alpha;
- }
-
- WindowInsetsAnimation lastAnimation;
- volatile boolean animationDone;
- final ArrayList<AnimationStep> animationSteps = new ArrayList<>();
-
- public AnimCallback(int dispatchMode) {
- super(dispatchMode);
- }
-
- @Override
- public void onPrepare(WindowInsetsAnimation animation) {
- animationSteps.clear();
- lastAnimation = animation;
- }
-
- @Override
- public Bounds onStart(WindowInsetsAnimation animation, Bounds bounds) {
- return bounds;
- }
-
- @Override
- public WindowInsets onProgress(WindowInsets insets,
- List<WindowInsetsAnimation> runningAnimations) {
- animationSteps.add(new AnimationStep(insets, lastAnimation.getFraction(),
- lastAnimation.getInterpolatedFraction(), lastAnimation.getAlpha()));
- return WindowInsets.CONSUMED;
- }
-
- @Override
- public void onEnd(WindowInsetsAnimation animation) {
- animationDone = true;
- }
- }
-
- public static class MultiAnimCallback extends WindowInsetsAnimation.Callback {
-
- WindowInsetsAnimation statusBarAnim;
- WindowInsetsAnimation navBarAnim;
- WindowInsetsAnimation imeAnim;
- volatile boolean animationDone;
- final ArrayList<AnimationStep> statusAnimSteps = new ArrayList<>();
- final ArrayList<AnimationStep> navAnimSteps = new ArrayList<>();
- final ArrayList<AnimationStep> imeAnimSteps = new ArrayList<>();
- Runnable startRunnable;
- final ArraySet<WindowInsetsAnimation> runningAnims = new ArraySet<>();
-
- public MultiAnimCallback() {
- super(DISPATCH_MODE_STOP);
- }
-
- @Override
- public void onPrepare(WindowInsetsAnimation animation) {
- if ((animation.getTypeMask() & statusBars()) != 0) {
- statusBarAnim = animation;
- }
- if ((animation.getTypeMask() & navigationBars()) != 0) {
- navBarAnim = animation;
- }
- if ((animation.getTypeMask() & ime()) != 0) {
- imeAnim = animation;
- }
- }
-
- @Override
- public Bounds onStart(WindowInsetsAnimation animation, Bounds bounds) {
- if (startRunnable != null) {
- startRunnable.run();
- }
- runningAnims.add(animation);
- return bounds;
- }
-
- @Override
- public WindowInsets onProgress(WindowInsets insets,
- List<WindowInsetsAnimation> runningAnimations) {
- if (statusBarAnim != null) {
- statusAnimSteps.add(new AnimationStep(insets, statusBarAnim.getFraction(),
- statusBarAnim.getInterpolatedFraction(), statusBarAnim.getAlpha()));
- }
- if (navBarAnim != null) {
- navAnimSteps.add(new AnimationStep(insets, navBarAnim.getFraction(),
- navBarAnim.getInterpolatedFraction(), navBarAnim.getAlpha()));
- }
- if (imeAnim != null) {
- imeAnimSteps.add(new AnimationStep(insets, imeAnim.getFraction(),
- imeAnim.getInterpolatedFraction(), imeAnim.getAlpha()));
- }
-
- assertEquals(runningAnims.size(), runningAnimations.size());
- for (int i = runningAnimations.size() - 1; i >= 0; i--) {
- Assert.assertNotEquals(-1, runningAnims.indexOf(runningAnimations.get(i)));
- }
-
- return WindowInsets.CONSUMED;
- }
-
- @Override
- public void onEnd(WindowInsetsAnimation animation) {
- runningAnims.remove(animation);
- if (runningAnims.isEmpty()) {
- animationDone = true;
- }
- }
- }
-
- public static class TestActivity extends FocusableActivity {
-
- AnimCallback mCallback = spy(new AnimCallback(Callback.DISPATCH_MODE_STOP));
- WindowInsets mLastWindowInsets;
-
- OnApplyWindowInsetsListener mListener;
- LinearLayout mView;
- View mChild;
- EditText mEditor;
-
- public class InsetsListener implements OnApplyWindowInsetsListener {
-
- @Override
- public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
- mLastWindowInsets = insets;
- return WindowInsets.CONSUMED;
- }
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mListener = spy(new InsetsListener());
- mView = new LinearLayout(this);
- mView.setWindowInsetsAnimationCallback(mCallback);
- mView.setOnApplyWindowInsetsListener(mListener);
- mChild = new TextView(this);
- mEditor = new EditText(this);
- mView.addView(mChild);
-
- getWindow().setDecorFitsSystemWindows(false);
- getWindow().getAttributes().layoutInDisplayCutoutMode =
- LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- setContentView(mView);
- mEditor.requestFocus();
- }
- }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
index 3baae0b..f05bf53 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -18,8 +18,8 @@
import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN;
import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE;
import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
@@ -43,10 +43,8 @@
import android.platform.test.annotations.Presubmit;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowInsets;
-import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation;
import android.view.WindowManager;
import android.widget.EditText;
@@ -71,7 +69,6 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:WindowInsetsControllerTests
*/
-@FlakyTest(detail = "Promote once confirmed non-flaky")
@Presubmit
public class WindowInsetsControllerTests extends WindowManagerTestBase {
@@ -86,8 +83,8 @@
final TestActivity activity = startActivity(TestActivity.class);
final View rootView = activity.getWindow().getDecorView();
- testHideInternal(rootView, Type.statusBars());
- testHideInternal(rootView, Type.navigationBars());
+ testHideInternal(rootView, statusBars());
+ testHideInternal(rootView, navigationBars());
}
private void testHideInternal(View rootView, int types) {
@@ -104,8 +101,8 @@
final TestActivity activity = startActivity(TestActivity.class);
final View rootView = activity.getWindow().getDecorView();
- testShowInternal(rootView, Type.statusBars());
- testShowInternal(rootView, Type.navigationBars());
+ testShowInternal(rootView, statusBars());
+ testShowInternal(rootView, navigationBars());
}
private void testShowInternal(View rootView, int types) {
@@ -136,12 +133,13 @@
}
@Test
+ @FlakyTest(detail = "~1% flaky")
public void testSetSystemBarsBehavior_showBarsByTouch() throws InterruptedException {
final TestActivity activity = startActivity(TestActivity.class);
final View rootView = activity.getWindow().getDecorView();
// The show-by-touch behavior will only be applied while navigation bars get hidden.
- final int types = Type.navigationBars();
+ final int types = navigationBars();
assumeTrue(rootView.getRootWindowInsets().isVisible(types));
rootView.getWindowInsetsController().setSystemBarsBehavior(BEHAVIOR_SHOW_BARS_BY_TOUCH);
@@ -159,7 +157,7 @@
final View rootView = activity.getWindow().getDecorView();
// Assume we have the bars and they can be visible.
- final int types = Type.statusBars();
+ final int types = statusBars();
assumeTrue(rootView.getRootWindowInsets().isVisible(types));
rootView.getWindowInsetsController().setSystemBarsBehavior(BEHAVIOR_SHOW_BARS_BY_SWIPE);
@@ -171,8 +169,7 @@
PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
// Swiping from top of display can show bars.
- dragOnDisplay(rootView.getWidth() / 2f, 0 /* downY */,
- rootView.getWidth() / 2f, rootView.getHeight() / 2f);
+ dragFromTopToCenter(rootView);
PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
}
@@ -182,7 +179,7 @@
final View rootView = activity.getWindow().getDecorView();
// Assume we have the bars and they can be visible.
- final int types = Type.statusBars();
+ final int types = statusBars();
assumeTrue(rootView.getRootWindowInsets().isVisible(types));
rootView.getWindowInsetsController().setSystemBarsBehavior(
@@ -195,8 +192,7 @@
PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
// Swiping from top of display can show transient bars, but apps cannot detect that.
- dragOnDisplay(rootView.getWidth() / 2f, 0 /* downY */,
- rootView.getWidth() / 2f, rootView.getHeight() /2f);
+ dragFromTopToCenter(rootView);
PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
}
@@ -268,6 +264,124 @@
}
@Test
+ public void testSetSystemUiVisibilityAfterCleared_showBarsByTouch() throws Exception {
+ final TestActivity activity = startActivity(TestActivity.class);
+ final View rootView = activity.getWindow().getDecorView();
+
+ // The show-by-touch behavior will only be applied while navigation bars get hidden.
+ final int types = navigationBars();
+ assumeTrue(rootView.getRootWindowInsets().isVisible(types));
+
+ // If we don't have any of the immersive flags, the default behavior will be show-bars-by-
+ // touch.
+ final int targetFlag = SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+
+ // Use flags to hide navigation bar.
+ ANIMATION_CALLBACK.reset();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
+ rootView.setSystemUiVisibility(targetFlag);
+ });
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
+
+ // Touching on display can show bars.
+ tapOnDisplay(rootView.getWidth() / 2f, rootView.getHeight() / 2f);
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+
+ // Use flags to hide navigation bar again.
+ ANIMATION_CALLBACK.reset();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
+ rootView.setSystemUiVisibility(targetFlag);
+ });
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
+
+ // Touching on display can show bars.
+ tapOnDisplay(rootView.getWidth() / 2f, rootView.getHeight() / 2f);
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+ }
+
+ @Test
+ public void testSetSystemUiVisibilityAfterCleared_showBarsBySwipe() throws Exception {
+ final TestActivity activity = startActivity(TestActivity.class);
+ final View rootView = activity.getWindow().getDecorView();
+
+ // Assume we have the bars and they can be visible.
+ final int types = statusBars();
+ assumeTrue(rootView.getRootWindowInsets().isVisible(types));
+
+ final int targetFlags = SYSTEM_UI_FLAG_IMMERSIVE | SYSTEM_UI_FLAG_FULLSCREEN;
+
+ // Use flags to hide status bar.
+ ANIMATION_CALLBACK.reset();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
+ rootView.setSystemUiVisibility(targetFlags);
+ });
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
+
+ // Swiping from top of display can show bars.
+ dragFromTopToCenter(rootView);
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+
+ // Use flags to hide status bar again.
+ ANIMATION_CALLBACK.reset();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
+ rootView.setSystemUiVisibility(targetFlags);
+ });
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
+
+ // Swiping from top of display can show bars.
+ dragFromTopToCenter(rootView);
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+ }
+
+ @Test
+ public void testSetSystemUiVisibilityAfterCleared_showBarsByApp() throws Exception {
+ final TestActivity activity = startActivity(TestActivity.class);
+ final View rootView = activity.getWindow().getDecorView();
+
+ // Assume we have the bars and they can be visible.
+ final int types = statusBars();
+ assumeTrue(rootView.getRootWindowInsets().isVisible(types));
+
+ // Use the flag to hide status bar.
+ ANIMATION_CALLBACK.reset();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
+ rootView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
+ });
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
+
+ // Clearing the flag can show status bar.
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setSystemUiVisibility(0);
+ });
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+
+ // Use the flag to hide status bar again.
+ ANIMATION_CALLBACK.reset();
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setWindowInsetsAnimationCallback(ANIMATION_CALLBACK);
+ rootView.setSystemUiVisibility(SYSTEM_UI_FLAG_FULLSCREEN);
+ });
+ ANIMATION_CALLBACK.waitForFinishing(TIMEOUT);
+ PollingCheck.waitFor(TIMEOUT, () -> !rootView.getRootWindowInsets().isVisible(types));
+
+ // Clearing the flag can show status bar.
+ getInstrumentation().runOnMainSync(() -> {
+ rootView.setSystemUiVisibility(0);
+ });
+ PollingCheck.waitFor(TIMEOUT, () -> rootView.getRootWindowInsets().isVisible(types));
+ }
+
+ @Test
public void testHideOnCreate() throws Exception {
final TestHideOnCreateActivity activity = startActivity(TestHideOnCreateActivity.class);
final View rootView = activity.getWindow().getDecorView();
@@ -330,7 +444,7 @@
final View childWindow = new View(activity);
getInstrumentation().runOnMainSync(() -> {
activity.getWindowManager().addView(childWindow,
- new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+ new WindowManager.LayoutParams(TYPE_APPLICATION));
mErrorCollector.checkThat(childWindow.getWindowInsetsController(), is(notNullValue()));
});
getInstrumentation().waitForIdleSync();
@@ -354,6 +468,11 @@
dragOnDisplay(x, y, x, y);
}
+ private void dragFromTopToCenter(View view) {
+ dragOnDisplay(view.getWidth() / 2f, 0 /* downY */,
+ view.getWidth() / 2f, view.getHeight() / 2f);
+ }
+
private void dragOnDisplay(float downX, float downY, float upX, float upY) {
final long downTime = SystemClock.elapsedRealtime();
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 8a6ba7c..1f1d4e8 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
@@ -222,6 +222,7 @@
protected final Context mContext = getInstrumentation().getContext();
protected final ActivityManager mAm = mContext.getSystemService(ActivityManager.class);
protected final ActivityTaskManager mAtm = mContext.getSystemService(ActivityTaskManager.class);
+ protected final DisplayManager mDm = mContext.getSystemService(DisplayManager.class);
/** The tracker to manage objects (especially {@link AutoCloseable}) in a test method. */
protected final ObjectTracker mObjectTracker = new ObjectTracker();
@@ -1862,6 +1863,7 @@
static class ActivityLifecycleCounts {
private final int[] mCounts = new int[ActivityCallback.SIZE];
+ private final int[] mFirstIndexes = new int[ActivityCallback.SIZE];
private final int[] mLastIndexes = new int[ActivityCallback.SIZE];
private ComponentName mActivityName;
@@ -1878,11 +1880,15 @@
// The callback list could be from the reference of TestJournal. If we are counting for
// retrying, there may be new data added to the list from other threads.
TestJournalContainer.withThreadSafeAccess(() -> {
+ Arrays.fill(mFirstIndexes, -1);
for (int i = 0; i < callbacks.size(); i++) {
final ActivityCallback callback = callbacks.get(i);
final int ordinal = callback.ordinal();
mCounts[ordinal]++;
mLastIndexes[ordinal] = i;
+ if (mFirstIndexes[ordinal] == -1) {
+ mFirstIndexes[ordinal] = i;
+ }
}
});
}
@@ -1891,6 +1897,10 @@
return mCounts[callback.ordinal()];
}
+ int getFirstIndex(ActivityCallback callback) {
+ return mFirstIndexes[callback.ordinal()];
+ }
+
int getLastIndex(ActivityCallback callback) {
return mLastIndexes[callback.ordinal()];
}
@@ -2310,4 +2320,8 @@
*/
public static class SideActivity extends Activity {
}
+
+ /** Activity that can handle all config changes. */
+ public static class ConfigChangeHandlingActivity extends CommandSession.BasicTestActivity {
+ }
}
diff --git a/tests/inputmethod/mockime/Android.bp b/tests/inputmethod/mockime/Android.bp
index f4faf83..c73f870 100644
--- a/tests/inputmethod/mockime/Android.bp
+++ b/tests/inputmethod/mockime/Android.bp
@@ -24,6 +24,7 @@
libs: ["junit"],
static_libs: [
"androidx.annotation_annotation",
+ "androidx.autofill_autofill",
"compatibility-device-util-axt",
],
}
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index a3f6118..f6bf355 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -71,6 +71,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import androidx.autofill.inline.UiVersions;
+import androidx.autofill.inline.UiVersions.StylesBuilder;
+import androidx.autofill.inline.v1.InlineSuggestionUi;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
@@ -724,12 +727,15 @@
@MainThread
@Override
public InlineSuggestionsRequest onCreateInlineSuggestionsRequest(Bundle uiExtras) {
+ StylesBuilder stylesBuilder = UiVersions.newStylesBuilder();
+ stylesBuilder.addStyle(InlineSuggestionUi.newStyleBuilder().build());
+ Bundle styles = stylesBuilder.build();
return getTracer().onCreateInlineSuggestionsRequest(() -> {
final ArrayList<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
- new Size(400, 100)).build());
+ new Size(400, 100)).setStyle(styles).build());
presentationSpecs.add(new InlinePresentationSpec.Builder(new Size(100, 100),
- new Size(400, 100)).build());
+ new Size(400, 100)).setStyle(styles).build());
return new InlineSuggestionsRequest.Builder(presentationSpecs)
.setMaxSuggestionCount(6)
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionBlockingMethodTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionBlockingMethodTest.java
index dc335ce..a412e7a 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionBlockingMethodTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionBlockingMethodTest.java
@@ -41,6 +41,7 @@
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputConnectionWrapper;
import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
import android.view.inputmethod.cts.util.TestActivity;
import android.widget.EditText;
import android.widget.LinearLayout;
@@ -62,6 +63,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
/**
@@ -69,7 +71,7 @@
*/
@LargeTest
@RunWith(AndroidJUnit4.class)
-public class InputConnectionBlockingMethodTest {
+public class InputConnectionBlockingMethodTest extends EndToEndImeTestBase {
private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
private static final long LONG_TIMEOUT = TimeUnit.SECONDS.toMillis(30);
private static final long IMMEDIATE_TIMEOUT_NANO = TimeUnit.MILLISECONDS.toNanos(200);
@@ -184,35 +186,42 @@
InstrumentationRegistry.getInstrumentation().getContext(),
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
new ImeSettings.Builder())) {
- final ImeEventStream stream = imeSession.openEventStream();
+ final AtomicBoolean isTestRunning = new AtomicBoolean(true);
+ try {
+ final ImeEventStream stream = imeSession.openEventStream();
- final String marker = getTestMarker();
- TestActivity.startSync(activity-> {
- final LinearLayout layout = new LinearLayout(activity);
- layout.setOrientation(LinearLayout.VERTICAL);
- final EditText editText = new EditText(activity) {
- @Override
- public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
- return inputConnectionWrapperProvider.apply(
- super.onCreateInputConnection(outAttrs));
- }
- };
- editText.setPrivateImeOptions(marker);
- editText.setHint("editText");
- editText.requestFocus();
+ final String marker = getTestMarker();
+ TestActivity.startSync(activity -> {
+ final LinearLayout layout = new LinearLayout(activity);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ final EditText editText = new EditText(activity) {
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+ final InputConnection ic = super.onCreateInputConnection(outAttrs);
+ // Fall back to the original InputConnection once the test is done.
+ return isTestRunning.get()
+ ? inputConnectionWrapperProvider.apply(ic) : ic;
+ }
+ };
+ editText.setPrivateImeOptions(marker);
+ editText.setHint("editText");
+ editText.requestFocus();
- layout.addView(editText);
- activity.getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
- return layout;
- });
+ layout.addView(editText);
+ activity.getWindow().setSoftInputMode(SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ return layout;
+ });
- // Wait until the MockIme gets bound to the TestActivity.
- expectBindInput(stream, Process.myPid(), TIMEOUT);
+ // Wait until the MockIme gets bound to the TestActivity.
+ expectBindInput(stream, Process.myPid(), TIMEOUT);
- // Wait until "onStartInput" gets called for the EditText.
- expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
+ // Wait until "onStartInput" gets called for the EditText.
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
- testProcedure.run(imeSession, stream);
+ testProcedure.run(imeSession, stream);
+ } finally {
+ isTestRunning.set(false);
+ }
}
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java
index 1dd20d0..23f32ce 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodStartInputLifecycleTest.java
@@ -34,6 +34,7 @@
import android.text.TextUtils;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.cts.util.DisableScreenDozeRule;
+import android.view.inputmethod.cts.util.EndToEndImeTestBase;
import android.view.inputmethod.cts.util.TestActivity;
import android.view.inputmethod.cts.util.TestUtils;
import android.view.inputmethod.cts.util.UnlockScreenRule;
@@ -46,6 +47,7 @@
import com.android.compatibility.common.util.CtsTouchUtils;
import com.android.cts.mockime.ImeCommand;
+import com.android.cts.mockime.ImeEvent;
import com.android.cts.mockime.ImeEventStream;
import com.android.cts.mockime.ImeSettings;
import com.android.cts.mockime.MockImeSession;
@@ -57,10 +59,11 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Predicate;
@MediumTest
@RunWith(AndroidJUnit4.class)
-public class InputMethodStartInputLifecycleTest {
+public class InputMethodStartInputLifecycleTest extends EndToEndImeTestBase {
@Rule
public final DisableScreenDozeRule mDisableScreenDozeRule = new DisableScreenDozeRule();
@Rule
@@ -115,8 +118,7 @@
TestUtils.turnScreenOff();
TestUtils.waitOnMainUntil(() -> screenStateCallbackRef.get() == SCREEN_STATE_OFF
&& editText.getWindowVisibility() != VISIBLE, TIMEOUT);
- assertTrue(TestUtils.getOnMainSync(
- () -> !imManager.isActive(editText) && !imManager.isAcceptingText()));
+ expectEvent(stream, onFinishInputMatcher(), TIMEOUT);
final ImeCommand commit = imeSession.callCommitText("Hi!", 1);
expectCommand(stream, commit, TIMEOUT);
TestUtils.waitOnMainUntil(() -> !TextUtils.equals(editText.getText(), "Hi!"), TIMEOUT,
@@ -137,4 +139,8 @@
"InputMethodService#commitText should work after screen on");
}
}
+
+ private static Predicate<ImeEvent> onFinishInputMatcher() {
+ return event -> TextUtils.equals("onFinishInput", event.getEventName());
+ }
}
diff --git a/tests/media/jni/NativeCodecDecoderTest.cpp b/tests/media/jni/NativeCodecDecoderTest.cpp
index d5eb85b..8dace62 100644
--- a/tests/media/jni/NativeCodecDecoderTest.cpp
+++ b/tests/media/jni/NativeCodecDecoderTest.cpp
@@ -341,8 +341,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
- isPass);
CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
CHECK_ERR(
loopCounter == 0 && mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
@@ -462,8 +460,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
- isPass);
CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
if (!isPass) continue;
@@ -483,8 +479,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt",
- isPass);
CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
if (validateFormat) {
if (mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
@@ -611,8 +605,6 @@
CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
- CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log,
- "input cnt != output cnt", isPass);
CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
CHECK_ERR(loopCounter == 0 && mIsAudio &&
(!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
diff --git a/tests/media/jni/NativeCodecTestBase.h b/tests/media/jni/NativeCodecTestBase.h
index 65f4111..b730fb0 100644
--- a/tests/media/jni/NativeCodecTestBase.h
+++ b/tests/media/jni/NativeCodecTestBase.h
@@ -92,7 +92,12 @@
uint32_t adler32(const uint8_t* input, int offset, int len);
public:
- void saveInPTS(int64_t pts) { inpPtsArray.push_back(pts); }
+ void saveInPTS(int64_t pts) {
+ // Add only Unique timeStamp, discarding any duplicate frame / non-display frame
+ if(0 == std::count(inpPtsArray.begin(), inpPtsArray.end(), pts)) {
+ inpPtsArray.push_back(pts);
+ }
+ }
void saveOutPTS(int64_t pts) { outPtsArray.push_back(pts); }
bool isPtsStrictlyIncreasing(int64_t lastPts);
bool isOutPtsListIdenticalToInpPtsList(bool requireSorting);
diff --git a/tests/media/jni/NativeMuxerTest.cpp b/tests/media/jni/NativeMuxerTest.cpp
index 9e759bc..b4e7858 100644
--- a/tests/media/jni/NativeMuxerTest.cpp
+++ b/tests/media/jni/NativeMuxerTest.cpp
@@ -66,14 +66,14 @@
bool combineMedias(AMediaMuxer* muxer, MuxerNativeTestHelper* that, const int* repeater);
- bool equals(MuxerNativeTestHelper* that);
+ bool isSubsetOf(MuxerNativeTestHelper* that);
void offsetTimeStamp(int trackID, long tsOffset, int sampleOffset);
private:
void splitMediaToMuxerParameters();
- static const int STTS_TOLERANCE = 100;
+ static const int STTS_TOLERANCE_US = 100;
const char* mSrcPath;
const char* mMime;
int mTrackCount;
@@ -263,7 +263,7 @@
// returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
// stream are present in ref media stream
-bool MuxerNativeTestHelper::equals(MuxerNativeTestHelper* that) {
+bool MuxerNativeTestHelper::isSubsetOf(MuxerNativeTestHelper* that) {
if (this == that) return true;
if (that == nullptr) return false;
@@ -279,31 +279,27 @@
if (thisMime != nullptr && thatMime != nullptr && !strcmp(thisMime, thatMime)) {
if (!isCSDIdentical(thisFormat, thatFormat)) continue;
if (mBufferInfo[i].size() == that->mBufferInfo[j].size()) {
- int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
- for (int k = 0; k < mBufferInfo[i].size(); k++) {
+ int tolerance =
+ strncmp(thisMime, "video/", strlen("video/")) ? 0 : STTS_TOLERANCE_US;
+ int k = 0;
+ for (; k < mBufferInfo[i].size(); k++) {
AMediaCodecBufferInfo* thisInfo = mBufferInfo[i][k];
AMediaCodecBufferInfo* thatInfo = that->mBufferInfo[j][k];
if (thisInfo->flags != thatInfo->flags) {
- flagsDiff++;
+ break;
}
if (thisInfo->size != thatInfo->size) {
- sizeDiff++;
+ break;
} else if (memcmp(mBuffer + thisInfo->offset,
that->mBuffer + thatInfo->offset, thisInfo->size)) {
- buffDiff++;
+ break;
}
if (abs(thisInfo->presentationTimeUs - thatInfo->presentationTimeUs) >
- STTS_TOLERANCE) {
- tsDiff++;
+ tolerance) {
+ break;
}
}
- if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0 && buffDiff == 0)
- break;
- else {
- ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff "
- "%d, buffDiff %d", thisMime, (int)mBufferInfo[i].size(), flagsDiff,
- sizeDiff, tsDiff, buffDiff);
- }
+ if (k == mBufferInfo[i].size()) break;
}
}
}
@@ -524,7 +520,7 @@
fclose(rfp);
if (muxStatus) {
auto* refInfo = new MuxerNativeTestHelper(crefPath);
- if (!mediaInfoA->equals(refInfo) || !mediaInfoB->equals(refInfo)) {
+ if (!mediaInfoA->isSubsetOf(refInfo) || !mediaInfoB->isSubsetOf(refInfo)) {
isPass = false;
ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing src A and src B "
"failed", csrcPathA, csrcPathB, jformat);
@@ -540,7 +536,7 @@
fclose(ofp);
if (status) {
auto* dstInfo = new MuxerNativeTestHelper(cdstPath);
- if (!dstInfo->equals(refInfo)) {
+ if (!dstInfo->isSubsetOf(refInfo)) {
isPass = false;
ALOGE("testMultiTrack: inputs: %s %s, fmt: %d, error ! muxing "
"src A: %d, src B: %d failed", csrcPathA, csrcPathB,
@@ -614,7 +610,7 @@
AMediaMuxer_delete(muxer);
fclose(ofp);
auto* outInfo = new MuxerNativeTestHelper(cdstPath);
- isPass = mediaInfo->equals(outInfo);
+ isPass = mediaInfo->isSubsetOf(outInfo);
if (!isPass) {
ALOGE("Validation failed after adding timestamp offset to track %d", trackID);
}
@@ -668,7 +664,7 @@
fclose(ofp);
if (muxStatus) {
auto* outInfo = new MuxerNativeTestHelper(cdstPath, cmime);
- result = mediaInfo->equals(outInfo);
+ result = mediaInfo->isSubsetOf(outInfo);
delete outInfo;
}
if ((muxStatus && !result) ||
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java
new file mode 100644
index 0000000..e57b3e7
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderExtTest.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediav2.cts;
+
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(Parameterized.class)
+public class CodecDecoderExtTest extends CodecDecoderTestBase {
+ private static final String LOG_TAG = CodecDecoderExtTest.class.getSimpleName();
+
+ private final String mRefFile;
+
+ public CodecDecoderExtTest(String mime, String testFile, String refFile) {
+ super(mime, testFile);
+ mRefFile = refFile;
+ }
+
+ @Parameterized.Parameters(name = "{index}({0})")
+ public static Collection<Object[]> input() {
+ return Arrays.asList(new Object[][]{
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ //show and no-show frames are sent as separate inputs
+ "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
+ //show and no-show frames are sent as one input
+ "bbb_340x280_768kbps_30fps_vp9.webm"},
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ //show and no-show frames are sent as separate inputs
+ "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm",
+ //show and no-show frames are sent as one input
+ "bbb_520x390_1mbps_30fps_vp9.webm"},
+ });
+ }
+
+ /**
+ * Test decodes and compares decoded output of two files.
+ */
+ @LargeTest
+ @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testDecodeAndValidate() throws IOException, InterruptedException {
+ ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+ if (listOfDecoders.isEmpty()) {
+ fail("no suitable codecs found for mime: " + mMime);
+ }
+ final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+ for (String decoder : listOfDecoders) {
+ decodeToMemory(mTestFile, decoder, 0, mode, Integer.MAX_VALUE);
+ OutputManager test = mOutputBuff;
+ String log = String.format("codec: %s, test file: %s, ref file: %s:: ", decoder,
+ mTestFile, mRefFile);
+ assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
+ assertTrue(log + "no input sent", 0 != mInputCount);
+ assertTrue(log + "output received", 0 != mOutputCount);
+ if (mIsAudio) {
+ assertTrue("reference output pts is not strictly increasing",
+ test.isPtsStrictlyIncreasing(mPrevOutputPts));
+ } else {
+ assertTrue("input pts list and output pts list are not identical",
+ test.isOutPtsListIdenticalToInpPtsList(false));
+ }
+ decodeToMemory(mRefFile, decoder, 0, mode, Integer.MAX_VALUE);
+ OutputManager ref = mOutputBuff;
+ assertTrue(log + "decoder outputs are not identical", ref.equals(test));
+ }
+ }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
index 5d1bc41..58d83c9 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
@@ -21,7 +21,6 @@
import android.media.MediaFormat;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.Pair;
import android.view.Surface;
import android.view.SurfaceView;
import android.view.ViewGroup;
@@ -36,7 +35,6 @@
import org.junit.runners.Parameterized;
import java.io.IOException;
-import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -44,31 +42,19 @@
import java.util.List;
import java.util.Set;
-import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@RunWith(Parameterized.class)
-public class CodecDecoderSurfaceTest extends CodecTestBase {
+public class CodecDecoderSurfaceTest extends CodecDecoderTestBase {
private static final String LOG_TAG = CodecDecoderSurfaceTest.class.getSimpleName();
- private final String mMime;
- private final String mTestFile;
private final String mReconfigFile;
-
- private final ArrayList<ByteBuffer> mCsdBuffers;
- private int mCurrCsdIdx;
-
- private MediaExtractor mExtractor;
private SurfaceView mSurfaceView;
public CodecDecoderSurfaceTest(String mime, String testFile, String reconfigFile) {
- mMime = mime;
- mTestFile = testFile;
+ super(mime, testFile);
mReconfigFile = reconfigFile;
- mCsdBuffers = new ArrayList<>();
- mAsyncHandle = new CodecAsyncHandler();
- mIsAudio = mMime.startsWith("audio/");
}
private void setScreenParams(int width, int height, boolean noStretch) {
@@ -92,85 +78,6 @@
mActivityRule.getActivity().runOnUiThread(() -> mSurfaceView.setLayoutParams(lp));
}
- private MediaFormat setUpSource(String srcFile) throws IOException {
- mExtractor = new MediaExtractor();
- mExtractor.setDataSource(mInpPrefix + srcFile);
- for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
- MediaFormat format = mExtractor.getTrackFormat(trackID);
- if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
- mExtractor.selectTrack(trackID);
- if (!mIsAudio) {
- // COLOR_FormatYUV420Flexible by default should be supported by all components
- // This call shouldn't effect configure() call for any codec
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
- }
- return format;
- }
- }
- fail("No track with mime: " + mMime + " found in file: " + srcFile);
- return null;
- }
-
- private void enqueueCodecConfig(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
- inputBuffer.put((ByteBuffer) csdBuffer.rewind());
- mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
- MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
- }
- }
-
- private void queueCodecConfig() throws InterruptedException {
- if (mIsCodecInAsyncMode) {
- for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
- mCurrCsdIdx++) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
- if (element != null) {
- enqueueCodecConfig(element.first);
- }
- }
- } else {
- for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
- enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
- }
- }
- }
-
- void enqueueInput(int bufferIndex) {
- if (mExtractor.getSampleSize() < 0) {
- enqueueEOS(bufferIndex);
- } else {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- mExtractor.readSampleData(inputBuffer, 0);
- int size = (int) mExtractor.getSampleSize();
- long pts = mExtractor.getSampleTime();
- int extractorFlags = mExtractor.getSampleFlags();
- int codecFlags = 0;
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
- }
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
- }
- if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
- codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + codecFlags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
- if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
- MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
- mOutputBuff.saveInPTS(pts);
- mInputCount++;
- }
- }
- }
-
void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
mSawOutputEOS = true;
@@ -261,6 +168,9 @@
"bbb_520x390_1mbps_30fps_vp8.webm"},
{MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm",
"bbb_520x390_1mbps_30fps_vp9.webm"},
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm",
+ "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm"},
{MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4",
"bbb_520x390_1mbps_30fps_av1.mp4"},
});
@@ -309,8 +219,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / " +
- mInputCount, mInputCount == mOutputCount);
assertTrue(log + "decoder output is flaky", ref.equals(test));
}
mCodec.release();
@@ -389,8 +297,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
- + mInputCount, mInputCount == mOutputCount);
assertTrue(log + "decoder output is flaky", ref.equals(test));
/* test flush in eos state */
@@ -407,8 +313,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount + " / "
- + mInputCount, mInputCount == mOutputCount);
assertTrue(log + "decoder output is flaky", ref.equals(test));
}
mCodec.release();
@@ -481,10 +385,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
/* test reconfigure codec at eos state */
@@ -501,10 +401,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
mExtractor.release();
@@ -526,10 +422,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", configRef.equals(test));
mExtractor.release();
}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index ca05d0e..1daf63f 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -16,14 +16,11 @@
package android.mediav2.cts;
-import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
-import android.os.PersistableBundle;
import android.util.Log;
-import android.util.Pair;
import android.view.Surface;
import androidx.test.filters.LargeTest;
@@ -48,7 +45,6 @@
import java.util.List;
import java.util.Set;
-import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -62,30 +58,19 @@
* of these tests are not to cover CDD requirements but to test components and their plugins
*/
@RunWith(Parameterized.class)
-public class CodecDecoderTest extends CodecTestBase {
+public class CodecDecoderTest extends CodecDecoderTestBase {
private static final String LOG_TAG = CodecDecoderTest.class.getSimpleName();
- private final String mMime;
- private final String mTestFile;
private final String mRefFile;
private final String mReconfigFile;
private final float mRmsError;
- private ArrayList<ByteBuffer> mCsdBuffers;
- private int mCurrCsdIdx;
-
- private MediaExtractor mExtractor;
-
public CodecDecoderTest(String mime, String testFile, String refFile, String reconfigFile,
float rmsError) {
- mMime = mime;
- mTestFile = testFile;
+ super(mime, testFile);
mRefFile = refFile;
mReconfigFile = reconfigFile;
mRmsError = rmsError;
- mAsyncHandle = new CodecAsyncHandler();
- mCsdBuffers = new ArrayList<>();
- mIsAudio = mMime.startsWith("audio/");
}
private short[] setUpAudioReference() throws IOException {
@@ -106,164 +91,6 @@
return refData;
}
- private MediaFormat setUpSource(String srcFile) throws IOException {
- mExtractor = new MediaExtractor();
- mExtractor.setDataSource(mInpPrefix + srcFile);
- for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
- MediaFormat format = mExtractor.getTrackFormat(trackID);
- if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
- mExtractor.selectTrack(trackID);
- if (!mIsAudio) {
- // COLOR_FormatYUV420Flexible by default should be supported by all components
- // This call shouldn't effect configure() call for any codec
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
- }
- return format;
- }
- }
- fail("No track with mime: " + mMime + " found in file: " + srcFile);
- return null;
- }
-
- private boolean hasCSD(MediaFormat format) {
- return format.containsKey("csd-0");
- }
-
- private void enqueueCodecConfig(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
- inputBuffer.put((ByteBuffer) csdBuffer.rewind());
- mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
- MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
- }
- }
-
- void enqueueInput(int bufferIndex) {
- if (mExtractor.getSampleSize() < 0) {
- enqueueEOS(bufferIndex);
- } else {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- mExtractor.readSampleData(inputBuffer, 0);
- int size = (int) mExtractor.getSampleSize();
- long pts = mExtractor.getSampleTime();
- int extractorFlags = mExtractor.getSampleFlags();
- int codecFlags = 0;
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
- }
- if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
- codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
- }
- if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
- codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + codecFlags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
- if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
- MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
- mOutputBuff.saveInPTS(pts);
- mInputCount++;
- }
- }
- }
-
- private void enqueueInput(int bufferIndex, ByteBuffer buffer, MediaCodec.BufferInfo info) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- inputBuffer.put((ByteBuffer) buffer.rewind());
- int flags = 0;
- if ((info.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
- flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
- }
- if ((info.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
- flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- mCodec.queueInputBuffer(bufferIndex, info.offset, info.size, info.presentationTimeUs,
- flags);
- if (info.size > 0 && ((flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) &&
- ((flags & MediaCodec.BUFFER_FLAG_PARTIAL_FRAME) == 0)) {
- mOutputBuff.saveInPTS(info.presentationTimeUs);
- mInputCount++;
- }
- }
-
- void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
- if (info.size > 0 && mSaveToMem) {
- if (mIsAudio) {
- ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
- mOutputBuff.saveToMemory(buf, info);
- } else {
- // tests both getOutputImage and getOutputBuffer. Can do time division
- // multiplexing but lets allow it for now
- Image img = mCodec.getOutputImage(bufferIndex);
- assertTrue(img != null);
- mOutputBuff.checksum(img);
-
- ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
- mOutputBuff.checksum(buf, info.size);
- }
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
- mOutputBuff.saveOutPTS(info.presentationTimeUs);
- mOutputCount++;
- }
- mCodec.releaseOutputBuffer(bufferIndex, false);
- }
-
- private void doWork(ByteBuffer buffer, ArrayList<MediaCodec.BufferInfo> list)
- throws InterruptedException {
- int frameCount = 0;
- if (mIsCodecInAsyncMode) {
- // output processing after queuing EOS is done in waitForAllOutputs()
- while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < list.size()) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
- if (element != null) {
- int bufferID = element.first;
- MediaCodec.BufferInfo info = element.second;
- if (info != null) {
- dequeueOutput(bufferID, info);
- } else {
- enqueueInput(bufferID, buffer, list.get(frameCount));
- frameCount++;
- }
- }
- }
- } else {
- MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
- // output processing after queuing EOS is done in waitForAllOutputs()
- while (!mSawInputEOS && frameCount < list.size()) {
- int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
- if (outputBufferId >= 0) {
- dequeueOutput(outputBufferId, outInfo);
- } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- mOutFormat = mCodec.getOutputFormat();
- mSignalledOutFormatChanged = true;
- }
- int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
- if (inputBufferId != -1) {
- enqueueInput(inputBufferId, buffer, list.get(frameCount));
- frameCount++;
- }
- }
- }
- }
-
private ArrayList<MediaCodec.BufferInfo> createSubFrames(ByteBuffer buffer, int sfCount) {
int size = (int) mExtractor.getSampleSize();
if (size < 0) return null;
@@ -290,48 +117,6 @@
return list;
}
- private void queueCodecConfig() throws InterruptedException {
- if (mIsCodecInAsyncMode) {
- for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
- mCurrCsdIdx++) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
- if (element != null) {
- enqueueCodecConfig(element.first);
- }
- }
- } else {
- for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
- enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
- }
- }
- }
-
- private void decodeToMemory(String file, String decoder, long pts, int mode, int frameLimit)
- throws IOException, InterruptedException {
- mSaveToMem = true;
- mOutputBuff = new OutputManager();
- mCodec = MediaCodec.createByCodecName(decoder);
- MediaFormat format = setUpSource(file);
- configureCodec(format, false, true, false);
- mCodec.start();
- mExtractor.seekTo(pts, mode);
- doWork(frameLimit);
- queueEOS();
- waitForAllOutputs();
- mCodec.stop();
- mCodec.release();
- mExtractor.release();
- mSaveToMem = false;
- }
-
- @Override
- PersistableBundle validateMetrics(String decoder, MediaFormat format) {
- PersistableBundle metrics = super.validateMetrics(decoder, format);
- assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
- assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 0);
- return metrics;
- }
-
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
Set<String> list = new HashSet<>();
@@ -417,6 +202,9 @@
"bbb_520x390_1mbps_30fps_vp8.webm", -1.0f},
{MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_340x280_768kbps_30fps_vp9.webm", null,
"bbb_520x390_1mbps_30fps_vp9.webm", -1.0f},
+ {MediaFormat.MIMETYPE_VIDEO_VP9,
+ "bbb_340x280_768kbps_30fps_split_non_display_frame_vp9.webm", null,
+ "bbb_520x390_1mbps_30fps_split_non_display_frame_vp9.webm", -1.0f},
{MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_340x280_768kbps_30fps_av1.mp4", null,
"bbb_520x390_1mbps_30fps_av1.mp4", -1.0f},
});
@@ -481,10 +269,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
if (loopCounter != 0) {
assertTrue(log + "decoder output is flaky", ref.equals(test));
} else {
@@ -620,10 +404,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
/* test flush in eos state */
@@ -640,10 +420,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -767,10 +543,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -796,10 +568,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", ref.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -836,10 +604,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is flaky", configRef.equals(test));
if (validateFormat) {
assertTrue(log + "not received format change",
@@ -980,11 +744,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(
- log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
if (loopCounter != 0) {
assertTrue(log + "decoder output is flaky", ref.equals(test));
} else {
@@ -1079,10 +838,6 @@
assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError());
assertTrue(log + "no input sent", 0 != mInputCount);
assertTrue(log + "output received", 0 != mOutputCount);
- if (!mIsAudio) {
- assertTrue(log + "input count != output count, act/exp: " + mOutputCount +
- " / " + mInputCount, mInputCount == mOutputCount);
- }
assertTrue(log + "decoder output is not consistent with ref", ref.equals(test));
}
mCodec.release();
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
index d6caa62..180e063 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
@@ -338,15 +338,21 @@
}
private void queueEOS() throws InterruptedException {
- if (!mSawDecInputEOS) {
- if (mIsCodecInAsyncMode) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getInput();
+ if (mIsCodecInAsyncMode) {
+ while (!mAsyncHandleDecoder.hasSeenError() && !mSawDecInputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandleDecoder.getWork();
if (element != null) {
- enqueueDecoderEOS(element.first);
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueDecoderOutput(bufferID, info);
+ } else {
+ enqueueDecoderEOS(element.first);
+ }
}
- } else {
- enqueueDecoderEOS(mDecoder.dequeueInputBuffer(-1));
}
+ } else if (!mSawDecInputEOS) {
+ enqueueDecoderEOS(mDecoder.dequeueInputBuffer(-1));
}
if (mIsCodecInAsyncMode) {
while (!hasSeenError() && !mSawDecOutputEOS) {
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
index 6a4c7d9..79d8f20 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
@@ -60,53 +60,27 @@
* of these tests are not to cover CDD requirements but to test components and their plugins
*/
@RunWith(Parameterized.class)
-public class CodecEncoderTest extends CodecTestBase {
+public class CodecEncoderTest extends CodecEncoderTestBase {
private static final String LOG_TAG = CodecEncoderTest.class.getSimpleName();
- // files are in WorkDir.getMediaDirString();
- private static final String mInputAudioFile = "bbb_2ch_44kHz_s16le.raw";
- private static final String mInputVideoFile = "bbb_cif_yuv420p_30fps.yuv";
- private final int INP_FRM_WIDTH = 352;
- private final int INP_FRM_HEIGHT = 288;
-
- private final String mMime;
private final int[] mBitrates;
private final int[] mEncParamList1;
private final int[] mEncParamList2;
- private final String mInputFile;
private ArrayList<MediaFormat> mFormats;
- private byte[] mInputData;
- private int mNumBytesSubmitted;
- private long mInputOffsetPts;
private int mNumSyncFramesReceived;
private ArrayList<Integer> mSyncFramesPos;
- private int mWidth, mHeight;
- private int mChannels;
- private int mSampleRate;
- private int mFrameRate;
- private int mMaxBFrames;
-
public CodecEncoderTest(String mime, int[] bitrates, int[] encoderInfo1, int[] encoderInfo2) {
- mMime = mime;
- mFrameRate = 30;
- if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12;
- else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12;
- mMaxBFrames = 0;
+ super(mime);
mBitrates = bitrates;
mEncParamList1 = encoderInfo1;
mEncParamList2 = encoderInfo2;
mFormats = new ArrayList<>();
mSyncFramesPos = new ArrayList<>();
- mAsyncHandle = new CodecAsyncHandler();
- mIsAudio = mMime.startsWith("audio/");
- mInputFile = mIsAudio ? mInputAudioFile : mInputVideoFile;
}
@Override
void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
super.resetContext(isAsync, signalEOSWithLastFrame);
- mNumBytesSubmitted = 0;
- mInputOffsetPts = 0;
mNumSyncFramesReceived = 0;
mSyncFramesPos.clear();
}
@@ -114,184 +88,16 @@
@Override
void flushCodec() {
super.flushCodec();
- if (mIsAudio) {
- mInputOffsetPts =
- (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate);
- } else {
- mInputOffsetPts = (mInputCount + 5) * 1000000L / mFrameRate;
- }
- mPrevOutputPts = mInputOffsetPts - 1;
- mNumBytesSubmitted = 0;
mNumSyncFramesReceived = 0;
mSyncFramesPos.clear();
}
- private void setUpSource(String srcFile) throws IOException {
- String inpPath = mInpPrefix + srcFile;
- try (FileInputStream fInp = new FileInputStream(inpPath)) {
- int size = (int) new File(inpPath).length();
- mInputData = new byte[size];
- fInp.read(mInputData, 0, size);
- }
- }
-
- void fillImage(Image image) {
- Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
- int imageWidth = image.getWidth();
- int imageHeight = image.getHeight();
- Image.Plane[] planes = image.getPlanes();
- int offset = mNumBytesSubmitted;
- for (int i = 0; i < planes.length; ++i) {
- ByteBuffer buf = planes[i].getBuffer();
- int width = imageWidth;
- int height = imageHeight;
- int tileWidth = INP_FRM_WIDTH;
- int tileHeight = INP_FRM_HEIGHT;
- int rowStride = planes[i].getRowStride();
- int pixelStride = planes[i].getPixelStride();
- if (i != 0) {
- width = imageWidth / 2;
- height = imageHeight / 2;
- tileWidth = INP_FRM_WIDTH / 2;
- tileHeight = INP_FRM_HEIGHT / 2;
- }
- if (pixelStride == 1) {
- if (width == rowStride && width == tileWidth && height == tileHeight) {
- buf.put(mInputData, offset, width * height);
- } else {
- for (int z = 0; z < height; z += tileHeight) {
- int rowsToCopy = Math.min(height - z, tileHeight);
- for (int y = 0; y < rowsToCopy; y++) {
- for (int x = 0; x < width; x += tileWidth) {
- int colsToCopy = Math.min(width - x, tileWidth);
- buf.position((z + y) * rowStride + x);
- buf.put(mInputData, offset + y * tileWidth, colsToCopy);
- }
- }
- }
- }
- } else {
- // do it pixel-by-pixel
- for (int z = 0; z < height; z += tileHeight) {
- int rowsToCopy = Math.min(height - z, tileHeight);
- for (int y = 0; y < rowsToCopy; y++) {
- int lineOffset = (z + y) * rowStride;
- for (int x = 0; x < width; x += tileWidth) {
- int colsToCopy = Math.min(width - x, tileWidth);
- for (int w = 0; w < colsToCopy; w++) {
- buf.position(lineOffset + (x + w) * pixelStride);
- buf.put(mInputData[offset + y * tileWidth + w]);
- }
- }
- }
- }
- }
- offset += tileWidth * tileHeight;
- }
- }
-
- void fillByteBuffer(ByteBuffer inputBuffer) {
- int offset = 0, frmOffset = mNumBytesSubmitted;
- for (int plane = 0; plane < 3; plane++) {
- int width = mWidth;
- int height = mHeight;
- int tileWidth = INP_FRM_WIDTH;
- int tileHeight = INP_FRM_HEIGHT;
- if (plane != 0) {
- width = mWidth / 2;
- height = mHeight / 2;
- tileWidth = INP_FRM_WIDTH / 2;
- tileHeight = INP_FRM_HEIGHT / 2;
- }
- for (int k = 0; k < height; k += tileHeight) {
- int rowsToCopy = Math.min(height - k, tileHeight);
- for (int j = 0; j < rowsToCopy; j++) {
- for (int i = 0; i < width; i += tileWidth) {
- int colsToCopy = Math.min(width - i, tileWidth);
- inputBuffer.position(offset + (k + j) * width + i);
- inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy);
- }
- }
- }
- offset += width * height;
- frmOffset += tileWidth * tileHeight;
- }
- }
-
- void enqueueInput(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- if (mNumBytesSubmitted >= mInputData.length) {
- enqueueEOS(bufferIndex);
- } else {
- int size;
- int flags = 0;
- long pts = mInputOffsetPts;
- if (mIsAudio) {
- pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate);
- size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted);
- inputBuffer.put(mInputData, mNumBytesSubmitted, size);
- if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) {
- flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- mNumBytesSubmitted += size;
- } else {
- pts += mInputCount * 1000000L / mFrameRate;
- size = mWidth * mHeight * 3 / 2;
- int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2;
- if (mNumBytesSubmitted + frmSize > mInputData.length) {
- fail("received partial frame to encode");
- } else {
- Image img = mCodec.getInputImage(bufferIndex);
- if (img != null) {
- fillImage(img);
- } else {
- if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) {
- inputBuffer.put(mInputData, mNumBytesSubmitted, size);
- } else {
- fillByteBuffer(inputBuffer);
- }
- }
- }
- if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) {
- flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
- mSawInputEOS = true;
- }
- mNumBytesSubmitted += frmSize;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + flags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
- mOutputBuff.saveInPTS(pts);
- mInputCount++;
- }
- }
-
void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- if (info.size > 0) {
- if (mSaveToMem) {
- ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
- mOutputBuff.saveToMemory(buf, info);
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
- mOutputBuff.saveOutPTS(info.presentationTimeUs);
- mOutputCount++;
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
+ if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
mNumSyncFramesReceived += 1;
mSyncFramesPos.add(mOutputCount);
}
- }
- mCodec.releaseOutputBuffer(bufferIndex, false);
+ super.dequeueOutput(bufferIndex, info);
}
private void encodeToMemory(String file, String encoder, int frameLimit, MediaFormat format)
@@ -340,14 +146,6 @@
return colorFormat;
}
- @Override
- PersistableBundle validateMetrics(String codec, MediaFormat format) {
- PersistableBundle metrics = super.validateMetrics(codec, format);
- assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
- assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 1);
- return metrics;
- }
-
private void forceSyncFrame() {
final Bundle syncFrame = new Bundle();
syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index 3e09a8d..1086981 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -22,6 +22,7 @@
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
+import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import android.os.PersistableBundle;
@@ -32,6 +33,10 @@
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
+import org.junit.Assert;
+
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -47,6 +52,7 @@
import java.util.concurrent.locks.ReentrantLock;
import java.util.zip.CRC32;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -212,7 +218,10 @@
}
void saveInPTS(long pts) {
- inpPtsList.add(pts);
+ // Add only Unique timeStamp, discarding any duplicate frame / non-display frame
+ if (!inpPtsList.contains(pts)) {
+ inpPtsList.add(pts);
+ }
}
void saveOutPTS(long pts) {
@@ -680,15 +689,21 @@
}
void queueEOS() throws InterruptedException {
- if (!mSawInputEOS) {
- if (mIsCodecInAsyncMode) {
- Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
+ if (mIsCodecInAsyncMode) {
+ while (!mAsyncHandle.hasSeenError() && !mSawInputEOS) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
if (element != null) {
- enqueueEOS(element.first);
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueOutput(bufferID, info);
+ } else {
+ enqueueEOS(element.first);
+ }
}
- } else {
- enqueueEOS(mCodec.dequeueInputBuffer(-1));
}
+ } else if (!mSawInputEOS) {
+ enqueueEOS(mCodec.dequeueInputBuffer(-1));
}
}
@@ -810,3 +825,454 @@
return metrics;
}
}
+
+class CodecDecoderTestBase extends CodecTestBase {
+ private static final String LOG_TAG = CodecDecoderTestBase.class.getSimpleName();
+
+ String mMime;
+ String mTestFile;
+
+ ArrayList<ByteBuffer> mCsdBuffers;
+ private int mCurrCsdIdx;
+
+ MediaExtractor mExtractor;
+
+ CodecDecoderTestBase(String mime, String testFile) {
+ mMime = mime;
+ mTestFile = testFile;
+ mAsyncHandle = new CodecAsyncHandler();
+ mCsdBuffers = new ArrayList<>();
+ mIsAudio = mMime.startsWith("audio/");
+ }
+
+ MediaFormat setUpSource(String srcFile) throws IOException {
+ mExtractor = new MediaExtractor();
+ mExtractor.setDataSource(mInpPrefix + srcFile);
+ for (int trackID = 0; trackID < mExtractor.getTrackCount(); trackID++) {
+ MediaFormat format = mExtractor.getTrackFormat(trackID);
+ if (mMime.equalsIgnoreCase(format.getString(MediaFormat.KEY_MIME))) {
+ mExtractor.selectTrack(trackID);
+ if (!mIsAudio) {
+ // COLOR_FormatYUV420Flexible by default should be supported by all components
+ // This call shouldn't effect configure() call for any codec
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
+ }
+ return format;
+ }
+ }
+ fail("No track with mime: " + mMime + " found in file: " + srcFile);
+ return null;
+ }
+
+ boolean hasCSD(MediaFormat format) {
+ return format.containsKey("csd-0");
+ }
+
+ void enqueueCodecConfig(int bufferIndex) {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ ByteBuffer csdBuffer = mCsdBuffers.get(mCurrCsdIdx);
+ inputBuffer.put((ByteBuffer) csdBuffer.rewind());
+ mCodec.queueInputBuffer(bufferIndex, 0, csdBuffer.limit(), 0,
+ MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "queued csd: id: " + bufferIndex + " size: " + csdBuffer.limit());
+ }
+ }
+
+ void enqueueInput(int bufferIndex) {
+ if (mExtractor.getSampleSize() < 0) {
+ enqueueEOS(bufferIndex);
+ } else {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ mExtractor.readSampleData(inputBuffer, 0);
+ int size = (int) mExtractor.getSampleSize();
+ long pts = mExtractor.getSampleTime();
+ int extractorFlags = mExtractor.getSampleFlags();
+ int codecFlags = 0;
+ if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+ }
+ if ((extractorFlags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+ }
+ if (!mExtractor.advance() && mSignalEOSWithLastFrame) {
+ codecFlags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+ " flags: " + codecFlags);
+ }
+ mCodec.queueInputBuffer(bufferIndex, 0, size, pts, codecFlags);
+ if (size > 0 && (codecFlags & (MediaCodec.BUFFER_FLAG_CODEC_CONFIG |
+ MediaCodec.BUFFER_FLAG_PARTIAL_FRAME)) == 0) {
+ mOutputBuff.saveInPTS(pts);
+ mInputCount++;
+ }
+ }
+ }
+
+ void enqueueInput(int bufferIndex, ByteBuffer buffer, MediaCodec.BufferInfo info) {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ inputBuffer.put((ByteBuffer) buffer.rewind());
+ int flags = 0;
+ if ((info.flags & MediaExtractor.SAMPLE_FLAG_SYNC) != 0) {
+ flags |= MediaCodec.BUFFER_FLAG_KEY_FRAME;
+ }
+ if ((info.flags & MediaExtractor.SAMPLE_FLAG_PARTIAL_FRAME) != 0) {
+ flags |= MediaCodec.BUFFER_FLAG_PARTIAL_FRAME;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "input: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ mCodec.queueInputBuffer(bufferIndex, info.offset, info.size, info.presentationTimeUs,
+ flags);
+ if (info.size > 0 && ((flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) &&
+ ((flags & MediaCodec.BUFFER_FLAG_PARTIAL_FRAME) == 0)) {
+ mOutputBuff.saveInPTS(info.presentationTimeUs);
+ mInputCount++;
+ }
+ }
+
+ void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ if (info.size > 0 && mSaveToMem) {
+ if (mIsAudio) {
+ ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+ mOutputBuff.saveToMemory(buf, info);
+ } else {
+ // tests both getOutputImage and getOutputBuffer. Can do time division
+ // multiplexing but lets allow it for now
+ Image img = mCodec.getOutputImage(bufferIndex);
+ assertTrue(img != null);
+ mOutputBuff.checksum(img);
+
+ ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+ mOutputBuff.checksum(buf, info.size);
+ }
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mOutputBuff.saveOutPTS(info.presentationTimeUs);
+ mOutputCount++;
+ }
+ mCodec.releaseOutputBuffer(bufferIndex, false);
+ }
+
+ void doWork(ByteBuffer buffer, ArrayList<MediaCodec.BufferInfo> list)
+ throws InterruptedException {
+ int frameCount = 0;
+ if (mIsCodecInAsyncMode) {
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!mAsyncHandle.hasSeenError() && !mSawInputEOS && frameCount < list.size()) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getWork();
+ if (element != null) {
+ int bufferID = element.first;
+ MediaCodec.BufferInfo info = element.second;
+ if (info != null) {
+ dequeueOutput(bufferID, info);
+ } else {
+ enqueueInput(bufferID, buffer, list.get(frameCount));
+ frameCount++;
+ }
+ }
+ }
+ } else {
+ MediaCodec.BufferInfo outInfo = new MediaCodec.BufferInfo();
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!mSawInputEOS && frameCount < list.size()) {
+ int outputBufferId = mCodec.dequeueOutputBuffer(outInfo, Q_DEQ_TIMEOUT_US);
+ if (outputBufferId >= 0) {
+ dequeueOutput(outputBufferId, outInfo);
+ } else if (outputBufferId == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ mOutFormat = mCodec.getOutputFormat();
+ mSignalledOutFormatChanged = true;
+ }
+ int inputBufferId = mCodec.dequeueInputBuffer(Q_DEQ_TIMEOUT_US);
+ if (inputBufferId != -1) {
+ enqueueInput(inputBufferId, buffer, list.get(frameCount));
+ frameCount++;
+ }
+ }
+ }
+ }
+
+ void queueCodecConfig() throws InterruptedException {
+ if (mIsCodecInAsyncMode) {
+ for (mCurrCsdIdx = 0; !mAsyncHandle.hasSeenError() && mCurrCsdIdx < mCsdBuffers.size();
+ mCurrCsdIdx++) {
+ Pair<Integer, MediaCodec.BufferInfo> element = mAsyncHandle.getInput();
+ if (element != null) {
+ enqueueCodecConfig(element.first);
+ }
+ }
+ } else {
+ for (mCurrCsdIdx = 0; mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
+ enqueueCodecConfig(mCodec.dequeueInputBuffer(-1));
+ }
+ }
+ }
+
+ void decodeToMemory(String file, String decoder, long pts, int mode, int frameLimit)
+ throws IOException, InterruptedException {
+ mSaveToMem = true;
+ mOutputBuff = new OutputManager();
+ mCodec = MediaCodec.createByCodecName(decoder);
+ MediaFormat format = setUpSource(file);
+ configureCodec(format, false, true, false);
+ mCodec.start();
+ mExtractor.seekTo(pts, mode);
+ doWork(frameLimit);
+ queueEOS();
+ waitForAllOutputs();
+ mCodec.stop();
+ mCodec.release();
+ mExtractor.release();
+ mSaveToMem = false;
+ }
+
+ @Override
+ PersistableBundle validateMetrics(String decoder, MediaFormat format) {
+ PersistableBundle metrics = super.validateMetrics(decoder, format);
+ assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
+ assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 0);
+ return metrics;
+ }
+}
+
+class CodecEncoderTestBase extends CodecTestBase {
+ private static final String LOG_TAG = CodecEncoderTestBase.class.getSimpleName();
+
+ // files are in WorkDir.getMediaDirString();
+ private static final String mInputAudioFile = "bbb_2ch_44kHz_s16le.raw";
+ private static final String mInputVideoFile = "bbb_cif_yuv420p_30fps.yuv";
+ private final int INP_FRM_WIDTH = 352;
+ private final int INP_FRM_HEIGHT = 288;
+
+ final String mMime;
+ final String mInputFile;
+ byte[] mInputData;
+ int mNumBytesSubmitted;
+ long mInputOffsetPts;
+
+ int mWidth, mHeight;
+ int mFrameRate;
+ int mMaxBFrames;
+ int mChannels;
+ int mSampleRate;
+
+ CodecEncoderTestBase(String mime) {
+ mMime = mime;
+ mWidth = INP_FRM_WIDTH;
+ mHeight = INP_FRM_HEIGHT;
+ mChannels = 1;
+ mSampleRate = 8000;
+ mFrameRate = 30;
+ mMaxBFrames = 0;
+ if (mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) mFrameRate = 12;
+ else if (mime.equals(MediaFormat.MIMETYPE_VIDEO_H263)) mFrameRate = 12;
+ mAsyncHandle = new CodecAsyncHandler();
+ mIsAudio = mMime.startsWith("audio/");
+ mInputFile = mIsAudio ? mInputAudioFile : mInputVideoFile;
+ }
+
+ @Override
+ void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) {
+ super.resetContext(isAsync, signalEOSWithLastFrame);
+ mNumBytesSubmitted = 0;
+ mInputOffsetPts = 0;
+ }
+
+ @Override
+ void flushCodec() {
+ super.flushCodec();
+ if (mIsAudio) {
+ mInputOffsetPts =
+ (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate);
+ } else {
+ mInputOffsetPts = (mInputCount + 5) * 1000000L / mFrameRate;
+ }
+ mPrevOutputPts = mInputOffsetPts - 1;
+ mNumBytesSubmitted = 0;
+ }
+
+ void setUpSource(String srcFile) throws IOException {
+ String inpPath = mInpPrefix + srcFile;
+ try (FileInputStream fInp = new FileInputStream(inpPath)) {
+ int size = (int) new File(inpPath).length();
+ mInputData = new byte[size];
+ fInp.read(mInputData, 0, size);
+ }
+ }
+
+ void fillImage(Image image) {
+ Assert.assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+ int imageWidth = image.getWidth();
+ int imageHeight = image.getHeight();
+ Image.Plane[] planes = image.getPlanes();
+ int offset = mNumBytesSubmitted;
+ for (int i = 0; i < planes.length; ++i) {
+ ByteBuffer buf = planes[i].getBuffer();
+ int width = imageWidth;
+ int height = imageHeight;
+ int tileWidth = INP_FRM_WIDTH;
+ int tileHeight = INP_FRM_HEIGHT;
+ int rowStride = planes[i].getRowStride();
+ int pixelStride = planes[i].getPixelStride();
+ if (i != 0) {
+ width = imageWidth / 2;
+ height = imageHeight / 2;
+ tileWidth = INP_FRM_WIDTH / 2;
+ tileHeight = INP_FRM_HEIGHT / 2;
+ }
+ if (pixelStride == 1) {
+ if (width == rowStride && width == tileWidth && height == tileHeight) {
+ buf.put(mInputData, offset, width * height);
+ } else {
+ for (int z = 0; z < height; z += tileHeight) {
+ int rowsToCopy = Math.min(height - z, tileHeight);
+ for (int y = 0; y < rowsToCopy; y++) {
+ for (int x = 0; x < width; x += tileWidth) {
+ int colsToCopy = Math.min(width - x, tileWidth);
+ buf.position((z + y) * rowStride + x);
+ buf.put(mInputData, offset + y * tileWidth, colsToCopy);
+ }
+ }
+ }
+ }
+ } else {
+ // do it pixel-by-pixel
+ for (int z = 0; z < height; z += tileHeight) {
+ int rowsToCopy = Math.min(height - z, tileHeight);
+ for (int y = 0; y < rowsToCopy; y++) {
+ int lineOffset = (z + y) * rowStride;
+ for (int x = 0; x < width; x += tileWidth) {
+ int colsToCopy = Math.min(width - x, tileWidth);
+ for (int w = 0; w < colsToCopy; w++) {
+ buf.position(lineOffset + (x + w) * pixelStride);
+ buf.put(mInputData[offset + y * tileWidth + w]);
+ }
+ }
+ }
+ }
+ }
+ offset += tileWidth * tileHeight;
+ }
+ }
+
+ void fillByteBuffer(ByteBuffer inputBuffer) {
+ int offset = 0, frmOffset = mNumBytesSubmitted;
+ for (int plane = 0; plane < 3; plane++) {
+ int width = mWidth;
+ int height = mHeight;
+ int tileWidth = INP_FRM_WIDTH;
+ int tileHeight = INP_FRM_HEIGHT;
+ if (plane != 0) {
+ width = mWidth / 2;
+ height = mHeight / 2;
+ tileWidth = INP_FRM_WIDTH / 2;
+ tileHeight = INP_FRM_HEIGHT / 2;
+ }
+ for (int k = 0; k < height; k += tileHeight) {
+ int rowsToCopy = Math.min(height - k, tileHeight);
+ for (int j = 0; j < rowsToCopy; j++) {
+ for (int i = 0; i < width; i += tileWidth) {
+ int colsToCopy = Math.min(width - i, tileWidth);
+ inputBuffer.position(offset + (k + j) * width + i);
+ inputBuffer.put(mInputData, frmOffset + j * tileWidth, colsToCopy);
+ }
+ }
+ }
+ offset += width * height;
+ frmOffset += tileWidth * tileHeight;
+ }
+ }
+
+ void enqueueInput(int bufferIndex) {
+ ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
+ if (mNumBytesSubmitted >= mInputData.length) {
+ enqueueEOS(bufferIndex);
+ } else {
+ int size;
+ int flags = 0;
+ long pts = mInputOffsetPts;
+ if (mIsAudio) {
+ pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate);
+ size = Math.min(inputBuffer.capacity(), mInputData.length - mNumBytesSubmitted);
+ inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+ if (mNumBytesSubmitted + size >= mInputData.length && mSignalEOSWithLastFrame) {
+ flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ mNumBytesSubmitted += size;
+ } else {
+ pts += mInputCount * 1000000L / mFrameRate;
+ size = mWidth * mHeight * 3 / 2;
+ int frmSize = INP_FRM_WIDTH * INP_FRM_HEIGHT * 3 / 2;
+ if (mNumBytesSubmitted + frmSize > mInputData.length) {
+ fail("received partial frame to encode");
+ } else {
+ Image img = mCodec.getInputImage(bufferIndex);
+ if (img != null) {
+ fillImage(img);
+ } else {
+ if (mWidth == INP_FRM_WIDTH && mHeight == INP_FRM_HEIGHT) {
+ inputBuffer.put(mInputData, mNumBytesSubmitted, size);
+ } else {
+ fillByteBuffer(inputBuffer);
+ }
+ }
+ }
+ if (mNumBytesSubmitted + frmSize >= mInputData.length && mSignalEOSWithLastFrame) {
+ flags |= MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ mNumBytesSubmitted += frmSize;
+ }
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
+ " flags: " + flags);
+ }
+ mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
+ mOutputBuff.saveInPTS(pts);
+ mInputCount++;
+ }
+ }
+
+ void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
+ if (ENABLE_LOGS) {
+ Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
+ info.size + " timestamp: " + info.presentationTimeUs);
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
+ }
+ if (info.size > 0) {
+ if (mSaveToMem) {
+ ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex);
+ mOutputBuff.saveToMemory(buf, info);
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mOutputBuff.saveOutPTS(info.presentationTimeUs);
+ mOutputCount++;
+ }
+ }
+ mCodec.releaseOutputBuffer(bufferIndex, false);
+ }
+
+ @Override
+ PersistableBundle validateMetrics(String codec, MediaFormat format) {
+ PersistableBundle metrics = super.validateMetrics(codec, format);
+ assertTrue(metrics.getString(MediaCodec.MetricsConstants.MIME_TYPE).equals(mMime));
+ assertTrue(metrics.getInt(MediaCodec.MetricsConstants.ENCODER) == 1);
+ return metrics;
+ }
+}
+
+
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index 9438dcb..bbfa5aa 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -18,9 +18,7 @@
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaFormat;
-import android.os.Build;
import android.util.Log;
import android.util.Pair;
@@ -39,46 +37,36 @@
import static android.media.MediaCodecInfo.CodecProfileLevel.*;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
/**
* Validate profile and level configuration for listed encoder components
*/
@RunWith(Parameterized.class)
-public class EncoderProfileLevelTest extends CodecTestBase {
+public class EncoderProfileLevelTest extends CodecEncoderTestBase {
private static final String LOG_TAG = EncoderProfileLevelTest.class.getSimpleName();
private static final HashMap<String, int[]> mProfileMap = new HashMap<>();
private static final HashMap<String, Pair<int[], Integer>> mProfileLevelCdd = new HashMap<>();
- private final String mMime;
private MediaFormat mConfigFormat;
- private int mNumBytesSubmitted;
-
- private int mHeight;
- private int mWidth;
- private int mChannels;
- private int mRate;
public EncoderProfileLevelTest(String mime, int bitrate, int encoderInfo1, int encoderInfo2,
int frameRate) {
- mMime = mime;
- mAsyncHandle = new CodecAsyncHandler();
+ super(mime);
mConfigFormat = new MediaFormat();
- mIsAudio = mMime.startsWith("audio/");
mConfigFormat.setString(MediaFormat.KEY_MIME, mMime);
mConfigFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
if (mIsAudio) {
- mRate = encoderInfo1;
+ mSampleRate = encoderInfo1;
mChannels = encoderInfo2;
- mConfigFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mRate);
+ mConfigFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, mSampleRate);
mConfigFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, mChannels);
} else {
mWidth = encoderInfo1;
mHeight = encoderInfo2;
- mRate = frameRate;
+ mFrameRate = frameRate;
mConfigFormat.setInteger(MediaFormat.KEY_WIDTH, mWidth);
mConfigFormat.setInteger(MediaFormat.KEY_HEIGHT, mHeight);
- mConfigFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mRate);
+ mConfigFormat.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
mConfigFormat.setFloat(MediaFormat.KEY_I_FRAME_INTERVAL, 1.0f);
mConfigFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
@@ -204,41 +192,7 @@
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 176, 144, 20},
{MediaFormat.MIMETYPE_VIDEO_VP8, 512000, 480, 360, 20},
});
-
- ArrayList<String> mimes = new ArrayList<>();
- MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaCodecInfo[] codecInfos = codecList.getCodecInfos();
- for (MediaCodecInfo codecInfo : codecInfos) {
- if (!codecInfo.isEncoder()) continue;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
- String[] types = codecInfo.getSupportedTypes();
- for (String type : types) {
- if (!mimes.contains(type)) mimes.add(type);
- }
- }
- for (String mime : cddRequiredMimeList) {
- if (!mimes.contains(mime)) {
- fail("media codec encoder list doesn't contain " + mime +
- " as required by cdd");
- }
- }
- final List<Object[]> argsList = new ArrayList<>();
- for (String mime : mimes) {
- boolean miss = true;
- for (int i = 0; i < exhaustiveArgsList.size(); i++) {
- if (mime.equals(exhaustiveArgsList.get(i)[0])) {
- argsList.add(exhaustiveArgsList.get(i));
- miss = false;
- }
- }
- if (miss) {
- if (cddRequiredMimeList.contains(mime)) {
- fail("no test vectors available for " + mime);
- }
- Log.w(LOG_TAG, "no test vectors available for " + mime);
- }
- }
- return argsList;
+ return prepareParamList(cddRequiredMimeList, exhaustiveArgsList, true);
}
static {
@@ -653,45 +607,6 @@
}
@Override
- void enqueueInput(int bufferIndex) {
- ByteBuffer inputBuffer = mCodec.getInputBuffer(bufferIndex);
- int size;
- int flags = 0;
- long pts;
- if (mIsAudio) {
- pts = mNumBytesSubmitted * 1000000L / (2 * mChannels * mRate);
- size = inputBuffer.capacity();
- byte[] data = new byte[size];
- inputBuffer.put(data);
- mNumBytesSubmitted += size;
- } else {
- pts = mInputCount * 1000000L / mRate;
- size = mWidth * mHeight * 3 / 2;
- byte[] data = new byte[size];
- inputBuffer.put(data);
- mNumBytesSubmitted += size;
- }
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "input: id: " + bufferIndex + " size: " + size + " pts: " + pts +
- " flags: " + flags);
- }
- mCodec.queueInputBuffer(bufferIndex, 0, size, pts, flags);
- mInputCount++;
- }
-
- @Override
- void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) {
- if (ENABLE_LOGS) {
- Log.v(LOG_TAG, "output: id: " + bufferIndex + " flags: " + info.flags + " size: " +
- info.size + " timestamp: " + info.presentationTimeUs);
- }
- if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
- mSawOutputEOS = true;
- }
- mCodec.releaseOutputBuffer(bufferIndex, false);
- }
-
- @Override
boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) {
if (!super.isFormatSimilar(inpFormat, outFormat)) {
Log.e(LOG_TAG, "Basic channel-rate/resolution comparisons failed");
@@ -778,6 +693,7 @@
boolean[] boolStates = {true, false};
MediaFormat format = mConfigFormat;
mOutputBuff = new OutputManager();
+ setUpSource(mInputFile);
int supportedCddCount = listOfEncoders.size() * (cddSupportedMime ? profileCdd.length : 1);
for (String encoder : listOfEncoders) {
mCodec = MediaCodec.createByCodecName(encoder);
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index d954f01..e57b1ee 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -46,6 +46,7 @@
import java.util.List;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -57,7 +58,9 @@
class MuxerTestHelper {
private static final String LOG_TAG = MuxerTestHelper.class.getSimpleName();
private static final boolean ENABLE_LOGS = false;
- static final int STTS_TOLERANCE = 100;
+ // Stts values within 0.1ms(100us) difference are fudged to save too
+ // many stts entries in MPEG4Writer.
+ static final int STTS_TOLERANCE_US = 100;
private String mSrcPath;
private String mMime;
private int mTrackCount;
@@ -69,6 +72,7 @@
private int mFrameLimit;
// combineMedias() uses local version of this variable
private HashMap<Integer, Integer> mOutIndexMap = new HashMap<>();
+ private boolean mRemoveCSD;
private void splitMediaToMuxerParameters() throws IOException {
// Set up MediaExtractor to read from the source.
@@ -80,6 +84,16 @@
for (int trackID = 0; trackID < extractor.getTrackCount(); trackID++) {
extractor.selectTrack(trackID);
MediaFormat format = extractor.getTrackFormat(trackID);
+ if (mRemoveCSD) {
+ for (int i = 0; ; ++i) {
+ String csdKey = "csd-" + i;
+ if (format.containsKey(csdKey)) {
+ format.removeKey(csdKey);
+ } else {
+ break;
+ }
+ }
+ }
if (mMime == null) {
mTrackCount++;
mFormat.add(format);
@@ -219,24 +233,29 @@
muxer.stop();
}
- MuxerTestHelper(String srcPath, String mime, int frameLimit) throws IOException {
+ MuxerTestHelper(String srcPath, String mime, int frameLimit, boolean aRemoveCSD) throws IOException {
mSrcPath = srcPath;
mMime = mime;
if (frameLimit < 0) frameLimit = Integer.MAX_VALUE;
mFrameLimit = frameLimit;
+ mRemoveCSD = aRemoveCSD;
splitMediaToMuxerParameters();
}
MuxerTestHelper(String srcPath, String mime) throws IOException {
- this(srcPath, mime, -1);
+ this(srcPath, mime, -1, false);
}
MuxerTestHelper(String srcPath, int frameLimit) throws IOException {
- this(srcPath, null, frameLimit);
+ this(srcPath, null, frameLimit, false);
+ }
+
+ MuxerTestHelper(String srcPath, boolean aRemoveCSD) throws IOException {
+ this(srcPath, null, -1, aRemoveCSD);
}
MuxerTestHelper(String srcPath) throws IOException {
- this(srcPath, null, -1);
+ this(srcPath, null, -1, false);
}
int getTrackCount() {
@@ -253,10 +272,9 @@
}
}
- @Override
// returns true if 'this' stream is a subset of 'o'. That is all tracks in current media
// stream are present in ref media stream
- public boolean equals(Object o) {
+ boolean isSubsetOf(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MuxerTestHelper that = (MuxerTestHelper) o;
@@ -273,43 +291,37 @@
if (thisMime != null && thisMime.equals(thatMime)) {
if (!ExtractorTest.isCSDIdentical(thisFormat, thatFormat)) continue;
if (mBufferInfo.get(i).size() == that.mBufferInfo.get(j).size()) {
- int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
- for (int k = 0; k < mBufferInfo.get(i).size(); k++) {
+ long tolerance = thisMime.startsWith("video/") ? STTS_TOLERANCE_US : 0;
+ int k = 0;
+ for (; k < mBufferInfo.get(i).size(); k++) {
MediaCodec.BufferInfo thisInfo = mBufferInfo.get(i).get(k);
MediaCodec.BufferInfo thatInfo = that.mBufferInfo.get(j).get(k);
if (thisInfo.flags != thatInfo.flags) {
- flagsDiff++;
+ break;
}
if (thisInfo.size != thatInfo.size) {
- sizeDiff++;
+ break;
} else {
mBuff.position(thisInfo.offset);
mBuff.get(refBuffer, 0, thisInfo.size);
that.mBuff.position(thatInfo.offset);
that.mBuff.get(testBuffer, 0, thatInfo.size);
- for (int count = 0; count < thisInfo.size; count++) {
+ int count = 0;
+ for (; count < thisInfo.size; count++) {
if (refBuffer[count] != testBuffer[count]) {
- buffDiff++;
break;
}
}
+ if (count != thisInfo.size) break;
}
if (Math.abs(
thisInfo.presentationTimeUs - thatInfo.presentationTimeUs) >
- STTS_TOLERANCE) {
- tsDiff++;
+ tolerance) {
+ break;
}
}
- if (flagsDiff != 0 || sizeDiff != 0 || tsDiff != 0 || buffDiff != 0) {
- if (ENABLE_LOGS) {
- Log.d(LOG_TAG, "For track: " + thisMime +
- " Total Samples: " + mBufferInfo.get(i).size() +
- " flagsDiff: " + flagsDiff +
- " sizeDiff: " + sizeDiff +
- " tsDiff: " + tsDiff +
- " buffDiff: " + buffDiff);
- }
- } else break;
+ // all samples are identical. successful match found. move to next track
+ if (k == mBufferInfo.get(i).size()) break;
} else {
if (ENABLE_LOGS) {
Log.d(LOG_TAG, "Mime matched but sample count different." +
@@ -742,7 +754,7 @@
try {
mediaInfoA.combineMedias(muxer, mediaInfoB, new int[]{1, 1});
refInfo = new MuxerTestHelper(mRefPath);
- if (!mediaInfoA.equals(refInfo) || !mediaInfoB.equals(refInfo)) {
+ if (!mediaInfoA.isSubsetOf(refInfo) || !mediaInfoB.isSubsetOf(refInfo)) {
fail(msg + "error ! muxing src A and src B failed");
}
} catch (Exception e) {
@@ -761,7 +773,7 @@
try {
mediaInfoA.combineMedias(muxer, mediaInfoB, numTrack);
MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
- if (!outInfo.equals(refInfo)) {
+ if (!outInfo.isSubsetOf(refInfo)) {
fail(msg + " error ! muxing src A: " + numTrack[0] + " src B: " +
numTrack[1] + "failed");
}
@@ -856,7 +868,7 @@
mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM);
Assume.assumeTrue("TODO(b/146421018)",
mOutFormat != MediaMuxer.OutputFormat.MUXER_OUTPUT_OGG);
- assertTrue(OFFSET_TS > MuxerTestHelper.STTS_TOLERANCE);
+ assertTrue(OFFSET_TS > MuxerTestHelper.STTS_TOLERANCE_US);
MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath);
for (int trackID = 0; trackID < mediaInfo.getTrackCount(); trackID++) {
for (int i = 0; i < mOffsetIndices.length; i++) {
@@ -866,7 +878,7 @@
mediaInfo.muxMedia(muxer);
muxer.release();
MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
- if (!outInfo.equals(mediaInfo)) {
+ if (!outInfo.isSubsetOf(mediaInfo)) {
String msg = String.format(
"testOffsetPresentationTime: inp: %s, fmt: %d, trackID %d", mSrcFile,
mOutFormat, trackID);
@@ -954,6 +966,14 @@
return result;
}
+ private boolean doesCodecRequireCSD(String aMime) {
+ return (aMime == MediaFormat.MIMETYPE_VIDEO_AVC ||
+ aMime == MediaFormat.MIMETYPE_VIDEO_HEVC ||
+ aMime == MediaFormat.MIMETYPE_VIDEO_MPEG4 ||
+ aMime == MediaFormat.MIMETYPE_AUDIO_AAC);
+
+ }
+
private native boolean nativeTestSimpleMux(String srcPath, String outPath, String mime,
String selector);
@@ -1010,7 +1030,7 @@
try {
mediaInfo.muxMedia(muxer);
MuxerTestHelper outInfo = new MuxerTestHelper(mOutPath);
- if (!mediaInfo.equals(outInfo)) {
+ if (!mediaInfo.isSubsetOf(outInfo)) {
fail(msg + "error! output != clone(input)");
}
} catch (Exception e) {
@@ -1023,6 +1043,39 @@
}
}
+ /* Does MediaMuxer throw IllegalStateException on missing codec specific data when required.
+ * Check if relevant exception is thrown for AAC, AVC, HEVC, and MPEG4
+ * codecs that require CSD in MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4.
+ * TODO(b/156767190): Need to evaluate what all codecs need CSD and also what all formats
+ * can contain these codecs, and add test cases accordingly.
+ * TODO(b/156767190): Add similar tests in the native side/NDK as well.
+ * TODO(b/156767190): Make a separate class, like TestNoCSDMux, instead of being part of
+ * TestSimpleMux?
+ */
+ @Test
+ public void testNoCSDMux() throws IOException {
+ Assume.assumeTrue(doesCodecRequireCSD(mMime));
+ MuxerTestHelper mediaInfo = new MuxerTestHelper(mInpPath, true);
+ for (int format = MUXER_OUTPUT_FIRST; format <= MUXER_OUTPUT_LAST; format++) {
+ // TODO(b/156767190)
+ if(format != MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4) continue;
+ MediaMuxer muxer = new MediaMuxer(mOutPath, format);
+ Exception expected = null;
+ String msg = String.format("testNoCSDMux: inp: %s, mime %s, fmt: %s", mSrcFile,
+ mMime, formatStringPair.get(format));
+ try {
+ mediaInfo.muxMedia(muxer);
+ } catch (IllegalStateException e) {
+ expected = e;
+ } catch (Exception e) {
+ fail(msg + ", unexpected exception:" + e.getMessage());
+ } finally {
+ assertNotNull(msg, expected);
+ muxer.release();
+ }
+ }
+ }
+
@Test
public void testSimpleMuxNative() {
Assume.assumeTrue("TODO(b/146421018)",
diff --git a/tests/providerui/AndroidManifest.xml b/tests/providerui/AndroidManifest.xml
index b967cff..2f1f791 100644
--- a/tests/providerui/AndroidManifest.xml
+++ b/tests/providerui/AndroidManifest.xml
@@ -28,6 +28,20 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <!--
+ final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("*/*");
+
+ -->
+ <queries>
+ <intent>
+ <action android:name="android.intent.action.OPEN_DOCUMENT" />
+ <category android:name="android.intent.category.OPENABLE" />
+ <data android:mimeType="*/*" />
+ </intent>
+ </queries>
+
<application android:requestLegacyExternalStorage = "true">
<uses-library android:name="android.test.runner"/>
<activity android:name="android.providerui.cts.GetResultActivity" />
diff --git a/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
index 7ad9a24..b6844de 100644
--- a/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
+++ b/tests/rollback/src/com/android/cts/rollback/RollbackManagerTest.java
@@ -106,4 +106,27 @@
Rollback.from(TestApp.A2).to(TestApp.A1));
assertThat(committed).causePackagesContainsExactly(TestApp.A2);
}
+
+ @Test
+ public void testGetRollbackDataPolicy() throws Exception {
+ // TODO: To change to the following statement when
+ // PackageManager.RollbackDataPolicy.WIPE is available.
+ // final int rollBackDataPolicy = PackageManager.RollbackDataPolicy.WIPE;
+ final int rollBackDataPolicy = 1;
+
+ Install.single(TestApp.A1).commit();
+ assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
+
+ // Enable rollback with rollBackDataPolicy
+ final int sessionId = Install.single(TestApp.A2).setEnableRollback(
+ rollBackDataPolicy).createSession();
+
+ try {
+ assertThat(InstallUtils.getPackageInstaller().getSessionInfo(
+ sessionId).getRollbackDataPolicy()).isEqualTo(rollBackDataPolicy);
+ } finally {
+ // Abandon the session
+ InstallUtils.getPackageInstaller().abandonSession(sessionId);
+ }
+ }
}
diff --git a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/HingeAngleVerification.java b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/HingeAngleVerification.java
index b129e6a..13f52c3 100644
--- a/tests/sensor/src/android/hardware/cts/helpers/sensorverification/HingeAngleVerification.java
+++ b/tests/sensor/src/android/hardware/cts/helpers/sensorverification/HingeAngleVerification.java
@@ -49,6 +49,8 @@
boolean success = true;
StringBuilder builder = new StringBuilder("Hinge Angle | failures:");
+ Assert.assertTrue("The hinge angle sensor must output at least two sensor events",
+ mEvents.size() >= 2);
for (int i = 0; i < mEvents.size(); i++) {
TestSensorEvent event = mEvents.get(i);
if (lastEvent != null && lastEvent.values[0] == event.values[0]) {
diff --git a/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java b/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java
index 1ec4d91..93cf8e4 100644
--- a/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java
+++ b/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java
@@ -53,7 +53,6 @@
import java.util.regex.Pattern;
@RunWith(AndroidJUnit4.class)
-@SystemUserOnly(reason = "b/150741315: Needs stabilization on secondary user")
public class DialogTests {
private static final String TEST_APP_LABEL = "Suspend Test App";
private static final long UI_TIMEOUT_MS = 30_000;
@@ -66,10 +65,11 @@
private UiDevice mUiDevice;
@Before
- public void setUp() {
+ public void setUp() throws Exception {
mContext = InstrumentationRegistry.getTargetContext();
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
mTestAppInterface = new TestAppInterface(mContext);
+ turnScreenOn();
}
private void turnScreenOn() throws Exception {
@@ -81,7 +81,6 @@
@Test
public void testInterceptorActivity_unsuspend() throws Exception {
- turnScreenOn();
final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
.setIcon(R.drawable.ic_settings)
.setTitle(R.string.dialog_title)
@@ -119,7 +118,7 @@
// 3. Unsuspend the test app
unsuspendButton.click();
- final Intent incomingIntent = sIncomingIntent.poll(10, TimeUnit.SECONDS);
+ final Intent incomingIntent = sIncomingIntent.poll(30, TimeUnit.SECONDS);
assertNotNull(incomingIntent);
assertEquals(Intent.ACTION_PACKAGE_UNSUSPENDED_MANUALLY, incomingIntent.getAction());
assertEquals("Did not receive correct unsuspended package name", TEST_APP_PACKAGE_NAME,
@@ -135,7 +134,6 @@
@Test
public void testInterceptorActivity_moreDetails() throws Exception {
- turnScreenOn();
final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
.setIcon(R.drawable.ic_settings)
.setTitle(R.string.dialog_title)
@@ -171,7 +169,7 @@
// Tapping on the neutral button should start the correct intent.
moreDetailsButton.click();
- final Intent incomingIntent = sIncomingIntent.poll(10, TimeUnit.SECONDS);
+ final Intent incomingIntent = sIncomingIntent.poll(30, TimeUnit.SECONDS);
assertNotNull(incomingIntent);
assertEquals(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS, incomingIntent.getAction());
assertEquals("Wrong package name sent with " + Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS,
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index 6044820..9a08bcf 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -437,7 +437,12 @@
@AppModeFull
public void testAppSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
- if (!shouldTestThisNetworkType(i, MINUTE/2)) {
+ // Use tolerance value that large enough to make sure stats of at
+ // least one bucket is included. However, this is possible that
+ // the test will see data of different app but with the same UID
+ // that created before testing.
+ // TODO: Consider query stats before testing and use the difference to verify.
+ if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
continue;
}
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index c27a3f8..4440010 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -1240,7 +1240,7 @@
// Usage should be attributed to the test app package
assertAppOrTokenUsed(TaskRootActivity.TEST_APP_PKG, true);
- mUiDevice.pressHome();
+ SystemUtil.runWithShellPermissionIdentity(() -> mAm.forceStopPackage(TEST_APP_PKG));
setUsageSourceSetting(Integer.toString(UsageStatsManager.USAGE_SOURCE_TASK_ROOT_ACTIVITY));
launchSubActivity(TaskRootActivity.class);
diff --git a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
index 9c1985c..fe9b79d 100644
--- a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
+++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java
@@ -28,7 +28,6 @@
import static android.appenumeration.cts.Constants.ACTION_START_FOR_RESULT;
import static android.appenumeration.cts.Constants.ACTIVITY_CLASS_DUMMY_ACTIVITY;
import static android.appenumeration.cts.Constants.ACTIVITY_CLASS_TEST;
-import static android.appenumeration.cts.Constants.ALL_QUERIES_TARGETING_R_PACKAGES;
import static android.appenumeration.cts.Constants.EXTRA_DATA;
import static android.appenumeration.cts.Constants.EXTRA_ERROR;
import static android.appenumeration.cts.Constants.EXTRA_FLAGS;
@@ -95,7 +94,6 @@
import org.hamcrest.core.IsNull;
import org.junit.AfterClass;
import org.junit.Assert;
-import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
@@ -138,12 +136,6 @@
sResponseHandler = new Handler(sResponseThread.getLooper());
}
- @Before
- public void setupTest() {
- if (!sGlobalFeatureEnabled) return;
- setFeatureEnabledForAll(true);
- }
-
@AfterClass
public static void tearDown() {
if (!sGlobalFeatureEnabled) return;
@@ -177,8 +169,14 @@
public void startExplicitly_cannotStartNonVisible() throws Exception {
assertNotVisible(QUERIES_NOTHING, TARGET_FILTERS);
try {
- startExplicitIntent(QUERIES_NOTHING, TARGET_FILTERS);
- fail("Package cannot start a package it cannot see");
+ startExplicitIntentViaComponent(QUERIES_NOTHING, TARGET_FILTERS);
+ fail("Package cannot start a package it cannot see via component name");
+ } catch (ActivityNotFoundException e) {
+ // hooray!
+ }
+ try {
+ startExplicitIntentViaPackageName(QUERIES_NOTHING, TARGET_FILTERS);
+ fail("Package cannot start a package it cannot see via package name");
} catch (ActivityNotFoundException e) {
// hooray!
}
@@ -187,7 +185,8 @@
@Test
public void startExplicitly_canStartVisible() throws Exception {
assertVisible(QUERIES_ACTIVITY_ACTION, TARGET_FILTERS);
- startExplicitIntent(QUERIES_ACTIVITY_ACTION, TARGET_FILTERS);
+ startExplicitIntentViaComponent(QUERIES_ACTIVITY_ACTION, TARGET_FILTERS);
+ startExplicitIntentViaPackageName(QUERIES_ACTIVITY_ACTION, TARGET_FILTERS);
}
@Test
@@ -203,13 +202,6 @@
}
@Test
- public void queriesNothing_featureOff_canSeeAll() throws Exception {
- setFeatureEnabledForAll(QUERIES_NOTHING, false);
- assertVisible(QUERIES_NOTHING, TARGET_NO_API);
- assertVisible(QUERIES_NOTHING, TARGET_FILTERS);
- }
-
- @Test
public void queriesNothingTargetsQ_canSeeAll() throws Exception {
assertVisible(QUERIES_NOTHING_Q, TARGET_FORCEQUERYABLE);
assertVisible(QUERIES_NOTHING_Q, TARGET_NO_API);
@@ -438,19 +430,6 @@
}
}
- private void setFeatureEnabledForAll(Boolean enabled) {
- for (String pkgName : ALL_QUERIES_TARGETING_R_PACKAGES) {
- setFeatureEnabledForAll(pkgName, enabled);
- }
- setFeatureEnabledForAll(QUERIES_NOTHING_Q, enabled == null ? null : false);
- }
-
- private void setFeatureEnabledForAll(String packageName, Boolean enabled) {
- SystemUtil.runShellCommand(
- "am compat " + (enabled == null ? "reset" : enabled ? "enable" : "disable")
- + " 135549675 " + packageName);
- }
-
private void assertNotVisible(String sourcePackageName, String targetPackageName)
throws Exception {
if (!sGlobalFeatureEnabled) return;
@@ -550,12 +529,19 @@
return response.getStringArray(Intent.EXTRA_RETURN_RESULT);
}
- private void startExplicitIntent(String sourcePackage, String targetPackage) throws Exception {
+ private void startExplicitIntentViaComponent(String sourcePackage, String targetPackage)
+ throws Exception {
sendCommandBlocking(sourcePackage, targetPackage,
new Intent().setComponent(new ComponentName(targetPackage,
ACTIVITY_CLASS_DUMMY_ACTIVITY)),
ACTION_START_DIRECTLY);
}
+ private void startExplicitIntentViaPackageName(String sourcePackage, String targetPackage)
+ throws Exception {
+ sendCommandBlocking(sourcePackage, targetPackage,
+ new Intent().setPackage(targetPackage),
+ ACTION_START_DIRECTLY);
+ }
private void startImplicitIntent(String sourcePackage) throws Exception {
sendCommandBlocking(sourcePackage, TARGET_FILTERS, new Intent(ACTION_MANIFEST_ACTIVITY),
diff --git a/tests/tests/appop/Android.bp b/tests/tests/appop/Android.bp
index 293c62e..20ab4bf 100644
--- a/tests/tests/appop/Android.bp
+++ b/tests/tests/appop/Android.bp
@@ -40,6 +40,7 @@
srcs: ["src/**/*.kt"],
static_libs: [
+ "bluetooth-test-util-lib",
"appops-test-util-lib",
"AppOpsUserServiceAidl",
"AppOpsForegroundControlServiceAidl",
diff --git a/tests/tests/appop/AndroidTest.xml b/tests/tests/appop/AndroidTest.xml
index 386feb4..29f01e0 100644
--- a/tests/tests/appop/AndroidTest.xml
+++ b/tests/tests/appop/AndroidTest.xml
@@ -23,6 +23,7 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsAppOpsTestCases.apk" />
<option name="test-file-name" value="CtsAppThatUsesAppOps.apk" />
+ <option name="test-file-name" value="AppInBackground.apk" />
<option name="test-file-name" value="AppThatCanBeForcedIntoForegroundStates.apk" />
</target_preparer>
diff --git a/tests/tests/appop/AppInBackground/Android.bp b/tests/tests/appop/AppInBackground/Android.bp
new file mode 100644
index 0000000..b46e877
--- /dev/null
+++ b/tests/tests/appop/AppInBackground/Android.bp
@@ -0,0 +1,23 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test_helper_app {
+ name: "AppInBackground",
+
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ ]
+}
diff --git a/tests/tests/appop/AppInBackground/AndroidManifest.xml b/tests/tests/appop/AppInBackground/AndroidManifest.xml
new file mode 100644
index 0000000..00d9ab8
--- /dev/null
+++ b/tests/tests/appop/AppInBackground/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.app.appops.cts.appinbackground">
+ <attribution android:tag="testAttribution" android:label="@string/dummyLabel" />
+
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+
+ <application />
+</manifest>
diff --git a/tests/tests/appop/AppInBackground/res/values/strings.xml b/tests/tests/appop/AppInBackground/res/values/strings.xml
new file mode 100644
index 0000000..c99e65f
--- /dev/null
+++ b/tests/tests/appop/AppInBackground/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<resources>
+ <string name="dummyLabel">A feature</string>
+</resources>
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt
index 103a4b2..106f111 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpEventCollectionTest.kt
@@ -34,11 +34,14 @@
import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.UiDevice
import com.google.common.truth.Truth.assertThat
+import org.junit.Assume.assumeNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import java.lang.Thread.sleep
+private const val BACKGROUND_PACKAGE = "android.app.appops.cts.appinbackground"
+
class AppOpEventCollectionTest {
private val instrumentation = InstrumentationRegistry.getInstrumentation()
private val context = instrumentation.targetContext
@@ -105,30 +108,87 @@
val attributionOpEntry = opEntry.attributedOpEntries[TEST_ATTRIBUTION_TAG]!!
assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isIn(before..after)
// Access should should also show up in the combined state for all op-flags
assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isIn(before..after)
- assertThat(opEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isIn(before..after)
// Foreground access should should also show up in the combined state for fg and bg
assertThat(attributionOpEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
assertThat(opEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(attributionOpEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
// The access was in foreground, hence there is no background access
- assertThat(attributionOpEntry.getLastBackgroundDuration(OP_FLAG_SELF)).isLessThan(before)
- assertThat(opEntry.getLastBackgroundDuration(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isLessThan(before)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isLessThan(before)
// The access was for a attribution, hence there is no access for the default attribution
if (null in opEntry.attributedOpEntries) {
assertThat(opEntry.attributedOpEntries[null]!!
- .getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
+ .getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
}
// The access does not show up for other op-flags
- assertThat(attributionOpEntry.getLastAccessForegroundTime(
- OP_FLAGS_ALL and OP_FLAG_SELF.inv())).isLessThan(before)
- assertThat(opEntry.getLastAccessForegroundTime(
- OP_FLAGS_ALL and OP_FLAG_SELF.inv())).isLessThan(before)
+ assertThat(
+ attributionOpEntry.getLastAccessForegroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv())
+ ).isLessThan(before)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv()))
+ .isLessThan(before)
+ }
+
+ @AppModeFull(reason = "instant apps cannot see other packages")
+ @Test
+ fun noteInBackgroundWithAttributionAndCheckOpEntries() {
+ val uid = context.packageManager.getPackageUid(BACKGROUND_PACKAGE, 0)
+
+ val before = System.currentTimeMillis()
+ assertThat(
+ runWithShellPermissionIdentity {
+ appOpsManager.noteOp(
+ OPSTR_WIFI_SCAN, uid, BACKGROUND_PACKAGE, TEST_ATTRIBUTION_TAG, null
+ )
+ }
+ ).isEqualTo(AppOpsManager.MODE_ALLOWED)
+ val after = System.currentTimeMillis()
+
+ val opEntry = getOpEntry(uid, BACKGROUND_PACKAGE, OPSTR_WIFI_SCAN)!!
+ val attributionOpEntry = opEntry.attributedOpEntries[TEST_ATTRIBUTION_TAG]!!
+
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAG_SELF)).isIn(before..after)
+
+ // Access should should also show up in the combined state for all op-flags
+ assertThat(attributionOpEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL)).isIn(before..after)
+
+ // Background access should should also show up in the combined state for fg and bg
+ assertThat(attributionOpEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(opEntry.getLastAccessTime(OP_FLAG_SELF)).isIn(before..after)
+ assertThat(attributionOpEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+ assertThat(opEntry.getLastAccessTime(OP_FLAGS_ALL)).isIn(before..after)
+
+ // The access was in background, hence there is no foreground access
+ assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAG_SELF)).isLessThan(before)
+ assertThat(attributionOpEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isLessThan(before)
+ assertThat(opEntry.getLastAccessForegroundTime(OP_FLAGS_ALL)).isLessThan(before)
+
+ // The access was for a attribution, hence there is no access for the default attribution
+ if (null in opEntry.attributedOpEntries) {
+ assertThat(opEntry.attributedOpEntries[null]!!
+ .getLastAccessBackgroundTime(OP_FLAG_SELF)).isLessThan(before)
+ }
+
+ // The access does not show up for other op-flags
+ assertThat(
+ attributionOpEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv())
+ ).isLessThan(before)
+ assertThat(opEntry.getLastAccessBackgroundTime(OP_FLAGS_ALL and OP_FLAG_SELF.inv()))
+ .isLessThan(before)
}
@Test
@@ -191,9 +251,12 @@
fun noteFromTwoProxiesAndVerifyProxyInfo() {
// Find another app to blame
val otherAppInfo = context.packageManager
- .resolveActivity(Intent(ACTION_APPLICATION_PREFERENCES), 0)!!
- .activityInfo.applicationInfo
- val otherPkg = otherAppInfo.packageName
+ .resolveActivity(Intent(ACTION_APPLICATION_PREFERENCES), 0)
+ ?.activityInfo?.applicationInfo
+
+ assumeNotNull(otherAppInfo)
+
+ val otherPkg = otherAppInfo!!.packageName
val otherUid = otherAppInfo.uid
// Using the shell identity causes a trusted proxy note
diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
index 5891055..4be7ac2 100644
--- a/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsLoggingTest.kt
@@ -33,6 +33,8 @@
import android.app.WallpaperManager
import android.app.WallpaperManager.FLAG_SYSTEM
import android.bluetooth.BluetoothManager
+import android.bluetooth.cts.BTAdapterUtils.enableAdapter as enableBTAdapter
+import android.bluetooth.cts.BTAdapterUtils.disableAdapter as disableBTAdapter
import android.bluetooth.le.ScanCallback
import android.content.BroadcastReceiver
import android.content.ComponentName
@@ -402,16 +404,22 @@
assumeTrue("Device does not support bluetooth",
context.packageManager.hasSystemFeature(FEATURE_BLUETOOTH))
- val btManager = context.createAttributionContext(TEST_ATTRIBUTION_TAG)
- .getSystemService(BluetoothManager::class.java)
+ val testContext = context.createAttributionContext(TEST_ATTRIBUTION_TAG)
+ val btAdapter = testContext.getSystemService(BluetoothManager::class.java).adapter
- btManager.adapter.startDiscovery()
+ val wasEnabled = enableBTAdapter(btAdapter, testContext)
+ assumeTrue("Need to be able enable BT", wasEnabled)
try {
- assertThat(noted[0].first.op).isEqualTo(OPSTR_FINE_LOCATION)
- assertThat(noted[0].first.attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
- assertThat(noted[0].second.map { it.methodName }).contains("getBTScanResults")
+ btAdapter.startDiscovery()
+ try {
+ assertThat(noted[0].first.op).isEqualTo(OPSTR_FINE_LOCATION)
+ assertThat(noted[0].first.attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
+ assertThat(noted[0].second.map { it.methodName }).contains("getBTScanResults")
+ } finally {
+ btAdapter.cancelDiscovery()
+ }
} finally {
- btManager.adapter.cancelDiscovery()
+ disableBTAdapter(btAdapter, testContext)
}
}
@@ -423,22 +431,29 @@
assumeTrue("Device does not support LE bluetooth",
context.packageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE))
- val btScanner = context.createAttributionContext(TEST_ATTRIBUTION_TAG)
- .getSystemService(BluetoothManager::class.java).adapter.bluetoothLeScanner
+ val testContext = context.createAttributionContext(TEST_ATTRIBUTION_TAG)
+ val btAdapter = testContext.getSystemService(BluetoothManager::class.java).adapter
- val scanCallback = object : ScanCallback() {}
-
- btScanner.startScan(scanCallback)
+ val wasEnabled = enableBTAdapter(btAdapter, testContext)
+ assumeTrue("Need to be able enable BT", wasEnabled)
try {
- eventually {
- assertThat(noted[0].first.op).isEqualTo(OPSTR_FINE_LOCATION)
- assertThat(noted[0].first.attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
- // startScan calls into the system server which then calls back into the app to
- // start the scan. I.e. the backtrace points back to a callback from the system
- // server
+ val btScanner = btAdapter.bluetoothLeScanner
+ val scanCallback = object : ScanCallback() {}
+
+ btScanner.startScan(scanCallback)
+ try {
+ eventually {
+ assertThat(noted[0].first.op).isEqualTo(OPSTR_FINE_LOCATION)
+ assertThat(noted[0].first.attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
+ // startScan calls into the system server which then calls back into the app to
+ // start the scan. I.e. the backtrace points back to a callback from the system
+ // server
+ }
+ } finally {
+ btScanner.stopScan(scanCallback)
}
} finally {
- btScanner.stopScan(scanCallback)
+ disableBTAdapter(btAdapter, testContext)
}
}
diff --git a/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt b/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt
index 71a4263..1d14a77 100644
--- a/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt
+++ b/tests/tests/appop/src/android/app/appops/cts/RuntimeMessageCollectionTest.kt
@@ -65,13 +65,13 @@
runWithShellPermissionIdentity {
val message = appOpsManager.collectRuntimeAppOpAccessMessage()
- if (message != null && message.packageName.equals(APP_PKG)) {
+ if (message != null && message.packageName.equals(APP_PKG) &&
+ message.samplingStrategy !=
+ RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM) {
assertThat(message.op).isEqualTo(AppOpsManager.OPSTR_READ_CONTACTS)
assertThat(message.uid).isEqualTo(appUid)
assertThat(message.attributionTag).isEqualTo(TEST_ATTRIBUTION_TAG)
assertThat(message.message).isEqualTo(MESSAGE)
- assertThat(message.samplingStrategy)
- .isNotEqualTo(RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM)
return
}
}
diff --git a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java
index 56fce94..bb50071 100644
--- a/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java
+++ b/tests/tests/batterysaving/src/android/os/cts/batterysaving/BatterySaverTest.java
@@ -17,8 +17,11 @@
import static com.android.compatibility.common.util.BatteryUtils.enableBatterySaver;
import static com.android.compatibility.common.util.BatteryUtils.runDumpsysBatteryUnplug;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.TestUtils.waitUntil;
+import static junit.framework.Assert.fail;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -99,6 +102,13 @@
/** Tests that Battery Saver exemptions activate when car mode is active. */
@Test
public void testCarModeExceptions() throws Exception {
+ final String nightModeText = runShellCommand("cmd uimode night");
+ final String[] nightModeSplit = nightModeText.split(":");
+ if (nightModeSplit.length != 2) {
+ fail("Failed to get initial night mode value from " + nightModeText);
+ }
+ final String initialNightMode = nightModeSplit[1].trim();
+ runShellCommand("cmd uimode night no");
UiModeManager uiModeManager = getContext().getSystemService(UiModeManager.class);
uiModeManager.disableCarMode(0);
@@ -147,6 +157,7 @@
powerManager.getLocationPowerSaveMode());
} finally {
uiModeManager.disableCarMode(0);
+ runShellCommand("cmd uimode night " + initialNightMode);
SettingsUtils.delete(SettingsUtils.NAMESPACE_GLOBAL, "battery_saver_constants");
}
}
diff --git a/tests/tests/bluetooth/Android.bp b/tests/tests/bluetooth/Android.bp
index 13e9742..13ee58e 100644
--- a/tests/tests/bluetooth/Android.bp
+++ b/tests/tests/bluetooth/Android.bp
@@ -15,7 +15,10 @@
android_test {
name: "CtsBluetoothTestCases",
defaults: ["cts_defaults"],
- static_libs: ["ctstestrunner-axt"],
+ static_libs: [
+ "ctstestrunner-axt",
+ "bluetooth-test-util-lib",
+ ],
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
diff --git a/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp b/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp
new file mode 100644
index 0000000..0257a41
--- /dev/null
+++ b/tests/tests/bluetooth/bluetoothTestUtilLib/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+java_library {
+ name: "bluetooth-test-util-lib",
+
+ static_libs: [
+ "junit",
+ ],
+
+ srcs: ["src/**/*.java"],
+
+ sdk_version: "current",
+}
diff --git a/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
new file mode 100644
index 0000000..9954f054
--- /dev/null
+++ b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth.cts;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.le.ScanRecord;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Utility for controlling the Bluetooth adapter from CTS test.
+ */
+public class BTAdapterUtils {
+ private static final String TAG = "BTAdapterUtils";
+
+ // ADAPTER_ENABLE_TIMEOUT_MS = AdapterState.BLE_START_TIMEOUT_DELAY +
+ // AdapterState.BREDR_START_TIMEOUT_DELAY
+ private static final int ADAPTER_ENABLE_TIMEOUT_MS = 8000;
+ // ADAPTER_DISABLE_TIMEOUT_MS = AdapterState.BLE_STOP_TIMEOUT_DELAY +
+ // AdapterState.BREDR_STOP_TIMEOUT_DELAY
+ private static final int ADAPTER_DISABLE_TIMEOUT_MS = 5000;
+
+ private static BroadcastReceiver mAdapterIntentReceiver;
+
+ private static Condition mConditionAdapterIsEnabled;
+ private static ReentrantLock mAdapterStateEnablinglock;
+
+ private static Condition mConditionAdapterIsDisabled;
+ private static ReentrantLock mAdapterStateDisablinglock;
+ private static boolean mAdapterVarsInitialized;
+
+ private static class AdapterIntentReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
+ int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
+ int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ Log.d(TAG, "Previous state: " + previousState + " New state: " + newState);
+
+ if (newState == BluetoothAdapter.STATE_ON) {
+ mAdapterStateEnablinglock.lock();
+ try {
+ Log.d(TAG, "Signaling to mConditionAdapterIsEnabled");
+ mConditionAdapterIsEnabled.signal();
+ } finally {
+ mAdapterStateEnablinglock.unlock();
+ }
+ } else if (newState == BluetoothAdapter.STATE_OFF) {
+ mAdapterStateDisablinglock.lock();
+ try {
+ Log.d(TAG, "Signaling to mConditionAdapterIsDisabled");
+ mConditionAdapterIsDisabled.signal();
+ } finally {
+ mAdapterStateDisablinglock.unlock();
+ }
+ }
+ }
+ }
+ }
+
+ /** Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled. */
+ public static boolean enableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+ if (!mAdapterVarsInitialized) {
+ initAdapterStateVariables(context);
+ }
+
+ if (bluetoothAdapter.isEnabled()) return true;
+
+ Log.d(TAG, "Enabling bluetooth adapter");
+ bluetoothAdapter.enable();
+ mAdapterStateEnablinglock.lock();
+ try {
+ // Wait for the Adapter to be enabled
+ while (!bluetoothAdapter.isEnabled()) {
+ if (!mConditionAdapterIsEnabled.await(
+ ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ // Timeout
+ Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable");
+ break;
+ } // else spurious wakeups
+ }
+ } catch(InterruptedException e) {
+ Log.e(TAG, "enableAdapter: interrrupted");
+ } finally {
+ mAdapterStateEnablinglock.unlock();
+ }
+ return bluetoothAdapter.isEnabled();
+ }
+
+ /** Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled. */
+ public static boolean disableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
+ if (!mAdapterVarsInitialized) {
+ initAdapterStateVariables(context);
+ }
+
+ if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
+
+ Log.d(TAG, "Disabling bluetooth adapter");
+ bluetoothAdapter.disable();
+ mAdapterStateDisablinglock.lock();
+ try {
+ // Wait for the Adapter to be disabled
+ while (bluetoothAdapter.getState() != BluetoothAdapter.STATE_OFF) {
+ if (!mConditionAdapterIsDisabled.await(
+ ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ // Timeout
+ Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable");
+ break;
+ } // else spurious wakeups
+ }
+ } catch(InterruptedException e) {
+ Log.e(TAG, "enableAdapter: interrrupted");
+ } finally {
+ mAdapterStateDisablinglock.unlock();
+ }
+ return bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF;
+ }
+
+ // Initialize variables required for TestUtils#enableAdapter and TestUtils#disableAdapter
+ private static void initAdapterStateVariables(Context context) {
+ Log.d(TAG, "Initializing adapter state variables");
+ mAdapterIntentReceiver = new AdapterIntentReceiver();
+ IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
+ context.registerReceiver(mAdapterIntentReceiver, filter);
+
+ mAdapterStateEnablinglock = new ReentrantLock();
+ mConditionAdapterIsEnabled = mAdapterStateEnablinglock.newCondition();
+ mAdapterStateDisablinglock = new ReentrantLock();
+ mConditionAdapterIsDisabled = mAdapterStateDisablinglock.newCondition();
+
+ mAdapterVarsInitialized = true;
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
index 65be7f8..2eab364 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BasicAdapterTest.java
@@ -141,8 +141,8 @@
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
for (int i=0; i<5; i++) {
- assertTrue(TestUtils.disableAdapter(adapter, mContext));
- assertTrue(TestUtils.enableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.disableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
}
}
@@ -152,7 +152,7 @@
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- assertTrue(TestUtils.enableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
assertTrue(BluetoothAdapter.checkBluetoothAddress(adapter.getAddress()));
}
@@ -163,7 +163,7 @@
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- assertTrue(TestUtils.enableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
String name = adapter.getName();
assertNotNull(name);
@@ -188,7 +188,7 @@
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- assertTrue(TestUtils.enableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
Set<BluetoothDevice> devices = adapter.getBondedDevices();
assertNotNull(devices);
@@ -204,7 +204,7 @@
}
// getRemoteDevice() should work even with Bluetooth disabled
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- assertTrue(TestUtils.disableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.disableAdapter(adapter, mContext));
// test bad addresses
try {
@@ -240,7 +240,7 @@
return;
}
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- assertTrue(TestUtils.enableAdapter(adapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(adapter, mContext));
BluetoothServerSocket socket = adapter.listenUsingRfcommWithServiceRecord(
"test", UUID.randomUUID());
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
index ac5ab45..103816e 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeScanTest.java
@@ -80,7 +80,7 @@
Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
if (!mBluetoothAdapter.isEnabled()) {
- assertTrue(TestUtils.enableAdapter(mBluetoothAdapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext));
}
mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mLocationOn = TestUtils.isLocationOn(getContext());
@@ -101,7 +101,7 @@
if (!mLocationOn) {
TestUtils.disableLocation(getContext());
}
- assertTrue(TestUtils.disableAdapter(mBluetoothAdapter, mContext));
+ assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
}
/**
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
index de26d4c..4e1419a 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/HearingAidProfileTest.java
@@ -80,7 +80,7 @@
Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();
- if (!TestUtils.enableAdapter(mBluetoothAdapter, mContext)) {
+ if (!BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext)) {
Log.e(TAG, "Unable to enable Bluetooth Adapter!");
assertTrue(mBluetoothAdapter.isEnabled());
}
@@ -99,7 +99,7 @@
public void tearDown() {
if (!mIsBleSupported) return;
- if (!TestUtils.disableAdapter(mBluetoothAdapter, mContext)) {
+ if (!BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext)) {
Log.e(TAG, "Unable to disable Bluetooth Adapter!");
assertTrue(mBluetoothAdapter.isEnabled());
}
@@ -224,10 +224,10 @@
mContext.registerReceiver(mIntentReceiver, filter);
Log.d(TAG, "test_getConnectionStateChangedIntent: disable adapter and wait");
- assertTrue(TestUtils.disableAdapter(mBluetoothAdapter, mContext));
+ assertTrue(BTAdapterUtils.disableAdapter(mBluetoothAdapter, mContext));
Log.d(TAG, "test_getConnectionStateChangedIntent: enable adapter and wait");
- assertTrue(TestUtils.enableAdapter(mBluetoothAdapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(mBluetoothAdapter, mContext));
int sanityCount = WAIT_FOR_INTENT_TIMEOUT_MS;
while ((numDevices != mIntentCallbackDeviceList.size()) && (sanityCount > 0)) {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
index 1546bd0..b9a93a1 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/LeL2capSocketTest.java
@@ -39,7 +39,7 @@
assertNotNull("BluetoothAdapter.getDefaultAdapter() returned null. "
+ "Does this device have a Bluetooth adapter?", mAdapter);
if (!mAdapter.isEnabled()) {
- assertTrue(TestUtils.enableAdapter(mAdapter, mContext));
+ assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
}
}
@@ -48,7 +48,7 @@
if (!TestUtils.isBleSupported(getContext())) {
return;
}
- assertTrue(TestUtils.disableAdapter(mAdapter, mContext));
+ assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
mAdapter = null;
super.tearDown();
}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
index 310c836..7c4c454 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/TestUtils.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -38,127 +38,7 @@
/**
* Utility class for Bluetooth CTS test.
*/
-public class TestUtils {
- private static final String TAG = "BluetoothTestUtils";
-
- // ADAPTER_ENABLE_TIMEOUT_MS = AdapterState.BLE_START_TIMEOUT_DELAY +
- // AdapterState.BREDR_START_TIMEOUT_DELAY
- private static final int ADAPTER_ENABLE_TIMEOUT_MS = 8000;
- // ADAPTER_DISABLE_TIMEOUT_MS = AdapterState.BLE_STOP_TIMEOUT_DELAY +
- // AdapterState.BREDR_STOP_TIMEOUT_DELAY
- private static final int ADAPTER_DISABLE_TIMEOUT_MS = 5000;
-
- private static BroadcastReceiver mAdapterIntentReceiver;
-
- private static Condition mConditionAdapterIsEnabled;
- private static ReentrantLock mAdapterStateEnablinglock;
-
- private static Condition mConditionAdapterIsDisabled;
- private static ReentrantLock mAdapterStateDisablinglock;
- private static boolean mAdapterVarsInitialized;
-
- private static class AdapterIntentReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) {
- int previousState = intent.getIntExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, -1);
- int newState = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
- Log.d(TAG, "Previous state: " + previousState + " New state: " + newState);
-
- if (newState == BluetoothAdapter.STATE_ON) {
- mAdapterStateEnablinglock.lock();
- try {
- Log.d(TAG, "Signaling to mConditionAdapterIsEnabled");
- mConditionAdapterIsEnabled.signal();
- } finally {
- mAdapterStateEnablinglock.unlock();
- }
- } else if (newState == BluetoothAdapter.STATE_OFF) {
- mAdapterStateDisablinglock.lock();
- try {
- Log.d(TAG, "Signaling to mConditionAdapterIsDisabled");
- mConditionAdapterIsDisabled.signal();
- } finally {
- mAdapterStateDisablinglock.unlock();
- }
- }
- }
- }
- }
-
- // Enables the Bluetooth Adapter. Return true if it is already enabled or is enabled.
- public static boolean enableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
- if (!mAdapterVarsInitialized) {
- initAdapterStateVariables(context);
- }
-
- if (bluetoothAdapter.isEnabled()) return true;
-
- Log.d(TAG, "Enabling bluetooth adapter");
- bluetoothAdapter.enable();
- mAdapterStateEnablinglock.lock();
- try {
- // Wait for the Adapter to be enabled
- while (!bluetoothAdapter.isEnabled()) {
- if (!mConditionAdapterIsEnabled.await(
- ADAPTER_ENABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- // Timeout
- Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter enable");
- break;
- } // else spurious wakeups
- }
- } catch(InterruptedException e) {
- Log.e(TAG, "enableAdapter: interrrupted");
- } finally {
- mAdapterStateEnablinglock.unlock();
- }
- return bluetoothAdapter.isEnabled();
- }
-
- // Disable the Bluetooth Adapter. Return true if it is already disabled or is disabled.
- public static boolean disableAdapter(BluetoothAdapter bluetoothAdapter, Context context) {
- if (!mAdapterVarsInitialized) {
- initAdapterStateVariables(context);
- }
-
- if (bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
-
- Log.d(TAG, "Disabling bluetooth adapter");
- bluetoothAdapter.disable();
- mAdapterStateDisablinglock.lock();
- try {
- // Wait for the Adapter to be disabled
- while (bluetoothAdapter.getState() != BluetoothAdapter.STATE_OFF) {
- if (!mConditionAdapterIsDisabled.await(
- ADAPTER_DISABLE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- // Timeout
- Log.e(TAG, "Timeout while waiting for the Bluetooth Adapter disable");
- break;
- } // else spurious wakeups
- }
- } catch(InterruptedException e) {
- Log.e(TAG, "enableAdapter: interrrupted");
- } finally {
- mAdapterStateDisablinglock.unlock();
- }
- return bluetoothAdapter.getState() == BluetoothAdapter.STATE_OFF;
- }
-
- // Initialize variables required for TestUtils#enableAdapter and TestUtils#disableAdapter
- private static void initAdapterStateVariables(Context context) {
- Log.d(TAG, "Initializing adapter state variables");
- mAdapterIntentReceiver = new AdapterIntentReceiver();
- IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
- context.registerReceiver(mAdapterIntentReceiver, filter);
-
- mAdapterStateEnablinglock = new ReentrantLock();
- mConditionAdapterIsEnabled = mAdapterStateEnablinglock.newCondition();
- mAdapterStateDisablinglock = new ReentrantLock();
- mConditionAdapterIsDisabled = mAdapterStateDisablinglock.newCondition();
-
- mAdapterVarsInitialized = true;
- }
-
+class TestUtils {
/**
* Utility method to call hidden ScanRecord.parseFromBytes method.
*/
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index e8e08b9..a06403a 100644
--- a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -193,7 +193,7 @@
int height = uiDevice.getDisplayHeight();
return uiDevice.swipe(
width / 2 /* startX */,
- height - 1 /* startY */,
+ height / 2 /* startY */,
width / 2 /* endX */,
1 /* endY */,
50 /* numberOfSteps */);
diff --git a/tests/tests/content/src/android/content/cts/IntentFilterTest.java b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
index 096068b..757f4bd 100644
--- a/tests/tests/content/src/android/content/cts/IntentFilterTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentFilterTest.java
@@ -572,18 +572,18 @@
new String[]{"authority1"}, new String[]{"100"});
checkMatches(filter,
MatchCondition.data(IntentFilter.NO_MATCH_DATA, null, true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme1:*", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme1://*/", true),
- MatchCondition.data(IntentFilter.MATCH_CATEGORY_PORT, "scheme1://*:100/", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme1://*:200/", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*:foo", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*://authority1/", true),
- MatchCondition.data(IntentFilter.MATCH_CATEGORY_PORT, "*://authority1:100/", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*://authority1:200/", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*:*", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*://*/", true),
- MatchCondition.data(IntentFilter.MATCH_CATEGORY_PORT, "*://*:100/", true),
- MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*://*:200/", true));
+ MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme1:*", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "scheme1://*/", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "scheme1://*:100/", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "scheme1://*:200/", true),
+ MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*:foo", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "*://authority1/", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "*://authority1:100/", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "*://authority1:200/", true),
+ MatchCondition.data(IntentFilter.NO_MATCH_DATA, "*:*", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "*://*/", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "*://*:100/", true),
+ MatchCondition.data(IntentFilter.MATCH_CATEGORY_HOST, "*://*:200/", true));
checkMatches(filter,
MatchCondition.data(IntentFilter.NO_MATCH_DATA, "scheme1://*/", false),
@@ -683,6 +683,20 @@
}
}
+ public void testAppEnumerationMatchesMimeGroups() {
+ IntentFilter filter = new Match(new String[]{ACTION}, null, null, new String[]{"scheme1"},
+ new String[]{"authority1"}, null).addMimeGroups(new String[]{"test"});
+
+ // assume any mime type or no mime type matches a filter with a mimegroup defined.
+ checkMatches(filter,
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE,
+ ACTION, null, "img/jpeg", "scheme1://authority1", true),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE,
+ ACTION, null, null, "scheme1://authority1", true),
+ new MatchCondition(IntentFilter.MATCH_CATEGORY_TYPE,
+ ACTION, null, "*/*", "scheme1://authority1", true));
+ }
+
public void testMatchData() throws MalformedMimeTypeException {
int expected = IntentFilter.MATCH_CATEGORY_EMPTY + IntentFilter.MATCH_ADJUSTMENT_NORMAL;
assertEquals(expected, mIntentFilter.matchData(null, null, null));
@@ -881,6 +895,48 @@
true));
}
+ public void testAppEnumerationNoHostMatchesWildcardHost() throws Exception {
+ IntentFilter filter = new Match(
+ new String[]{Intent.ACTION_VIEW},
+ new String[]{Intent.CATEGORY_BROWSABLE},
+ null,
+ new String[]{"http", "https"},
+ new String[]{"*"},
+ null /*ports*/);
+ checkMatches(filter,
+ new MatchCondition(MATCH_CATEGORY_HOST,
+ Intent.ACTION_VIEW,
+ new String[]{Intent.CATEGORY_BROWSABLE},
+ null,
+ "https://*",
+ true));
+
+ checkMatches(filter,
+ new MatchCondition(MATCH_CATEGORY_HOST,
+ Intent.ACTION_VIEW,
+ new String[]{Intent.CATEGORY_BROWSABLE},
+ null,
+ "https://",
+ true));
+ }
+
+ public void testAppEnumerationNoPortMatchesPortFilter() throws Exception {
+ IntentFilter filter = new Match(
+ new String[]{Intent.ACTION_VIEW},
+ new String[]{Intent.CATEGORY_BROWSABLE},
+ null,
+ new String[]{"http", "https"},
+ new String[]{"*"},
+ new String[]{"81"});
+ checkMatches(filter,
+ new MatchCondition(MATCH_CATEGORY_HOST,
+ Intent.ACTION_VIEW,
+ new String[]{Intent.CATEGORY_BROWSABLE},
+ null,
+ "https://something",
+ true));
+ }
+
public void testWriteToXml() throws IllegalArgumentException, IllegalStateException,
IOException, MalformedMimeTypeException, XmlPullParserException {
XmlSerializer xml;
@@ -1276,6 +1332,13 @@
return this;
}
+ Match addMimeGroups(String[] mimeGroups) {
+ for (int i = 0; i < mimeGroups.length; i++) {
+ addMimeGroup(mimeGroups[i]);
+ }
+ return this;
+ }
+
Match addMimeTypes(String[] mimeTypes) {
for (int i = 0; i < mimeTypes.length; i++) {
try {
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 c6f4faf..54a5bc0 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java
@@ -29,8 +29,10 @@
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.AppModeFull;
import android.service.dataloader.DataLoaderService;
+import android.text.TextUtils;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
@@ -140,15 +142,38 @@
assertEquals(binder, service.onBind(new Intent()));
}
+ @LargeTest
@Test
public void testInstallSysTrace() throws Exception {
+ // Async atrace dump uses less resources but requires periodic pulls.
+ // Overall timeout of 30secs in 100ms intervals should be enough.
+ final int atraceDumpIterations = 300;
+ final int atraceDumpDelayMs = 100;
+
final String expected = "|page_read:";
final ByteArrayOutputStream result = new ByteArrayOutputStream();
- final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand("atrace adb");
final Thread readFromProcess = new Thread(() -> {
- try (InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(stdout)) {
- final String found = waitForSubstring(inputStream, expected);
- result.write(found.getBytes());
+ try {
+ executeShellCommand("atrace --async_start -b 1024 -c adb");
+ try {
+ for (int i = 0; i < atraceDumpIterations; ++i) {
+ final ParcelFileDescriptor stdout = getUiAutomation().executeShellCommand(
+ "atrace --async_dump");
+ try (InputStream inputStream =
+ new ParcelFileDescriptor.AutoCloseInputStream(
+ stdout)) {
+ final String found = waitForSubstring(inputStream, expected);
+ if (!TextUtils.isEmpty(found)) {
+ result.write(found.getBytes());
+ return;
+ }
+ Thread.currentThread().sleep(atraceDumpDelayMs);
+ } catch (InterruptedException ignored) {
+ }
+ }
+ } finally {
+ executeShellCommand("atrace --async_stop");
+ }
} catch (IOException ignored) {
}
});
@@ -158,7 +183,7 @@
assertTrue(isAppInstalled(TEST_APP_PACKAGE));
readFromProcess.join();
- assertNotEquals("", result.toString());
+ assertNotEquals(0, result.size());
}
private boolean isAppInstalled(String packageName) throws IOException {
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
index 41d1688..38e5b3f 100644
--- a/tests/tests/display/AndroidManifest.xml
+++ b/tests/tests/display/AndroidManifest.xml
@@ -26,6 +26,8 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!-- For testing pushing brightness curves. -->
<uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS" />
+ <!-- For querying to see if packages exist with the brightness permissions. -->
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/display/OWNERS b/tests/tests/display/OWNERS
index 6b9f7be..041736b 100644
--- a/tests/tests/display/OWNERS
+++ b/tests/tests/display/OWNERS
@@ -1,2 +1,3 @@
# Bug component: 46788
-michaelwr@google.com
\ No newline at end of file
+michaelwr@google.com
+santoscordon@google.com
diff --git a/tests/tests/display/src/android/display/cts/BrightnessTest.java b/tests/tests/display/src/android/display/cts/BrightnessTest.java
index 7b207b9..7bbe88d 100644
--- a/tests/tests/display/src/android/display/cts/BrightnessTest.java
+++ b/tests/tests/display/src/android/display/cts/BrightnessTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.Manifest;
import android.app.UiAutomation;
@@ -88,10 +89,9 @@
@Test
public void testBrightnessSliderTracking() throws InterruptedException {
- if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
- // Don't run as there is no app that has permission to access slider usage.
- return;
- }
+ // Don't run as there is no app that has permission to access slider usage.
+ assumeTrue(
+ numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0);
int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
int previousBrightnessMode =
@@ -139,10 +139,10 @@
@Test
public void testNoTrackingForManualBrightness() throws InterruptedException {
- if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
- // Don't run as there is no app that has permission to access slider usage.
- return;
- }
+ // Don't run as there is no app that has permission to access slider usage.
+ assumeTrue(
+ numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0);
+
int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
int previousBrightnessMode =
getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
@@ -171,6 +171,52 @@
}
@Test
+ public void testNoColorSampleData() throws InterruptedException {
+ // Don't run as there is no app that has permission to access slider usage.
+ assumeTrue(
+ numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0);
+
+ // Don't run as there is no app that has permission to push curves.
+ assumeTrue(numberOfSystemAppsWithPermission(
+ Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0);
+
+ int previousBrightness = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS);
+ int previousBrightnessMode =
+ getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+ try {
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+ int mode = getSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE);
+ assertEquals(Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC, mode);
+
+ grantPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE);
+ grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
+
+ // Set brightness config to not sample color.
+ BrightnessConfiguration config =
+ new BrightnessConfiguration.Builder(
+ new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
+ .setShouldCollectColorSamples(false).build();
+ mDisplayManager.setBrightnessConfiguration(config);
+
+ // Setup and generate one slider event.
+ recordSliderEvents();
+ waitForFirstSliderEvent();
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, 20);
+ List<BrightnessChangeEvent> newEvents = getNewEvents(1);
+
+ // No color samples.
+ assertEquals(0, newEvents.get(0).colorSampleDuration);
+ assertNull(newEvents.get(0).colorValueBuckets);
+
+ // No test for sampling color as support is optional.
+ } finally {
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS, previousBrightness);
+ setSystemSetting(Settings.System.SCREEN_BRIGHTNESS_MODE, previousBrightnessMode);
+ }
+ }
+
+ @Test
public void testSliderUsagePermission() {
revokePermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE);
@@ -203,14 +249,14 @@
@Test
public void testSetGetSimpleCurve() {
- if (numberOfSystemAppsWithPermission(
- Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) == 0) {
- // Don't run as there is no app that has permission to push curves.
- return;
- }
+ // Don't run as there is no app that has permission to push curves.
+ assumeTrue(numberOfSystemAppsWithPermission(
+ Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0);
grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
+ BrightnessConfiguration defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration();
+
BrightnessConfiguration config =
new BrightnessConfiguration.Builder(
new float[]{0.0f, 1000.0f},new float[]{20.0f, 500.0f})
@@ -218,16 +264,28 @@
BrightnessCorrection.createScaleAndTranslateLog(0.80f, 0.2f))
.addCorrectionByPackageName("some.package.name",
BrightnessCorrection.createScaleAndTranslateLog(0.70f, 0.1f))
+ .setShortTermModelTimeoutMillis(
+ defaultConfig.getShortTermModelTimeoutMillis() + 1000L)
+ .setShortTermModelLowerLuxMultiplier(
+ defaultConfig.getShortTermModelLowerLuxMultiplier() + 0.2f)
+ .setShortTermModelUpperLuxMultiplier(
+ defaultConfig.getShortTermModelUpperLuxMultiplier() + 0.3f)
.setDescription("some test").build();
mDisplayManager.setBrightnessConfiguration(config);
BrightnessConfiguration returnedConfig = mDisplayManager.getBrightnessConfiguration();
assertEquals(config, returnedConfig);
- assertEquals(config.getCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE),
+ assertEquals(returnedConfig.getCorrectionByCategory(ApplicationInfo.CATEGORY_IMAGE),
BrightnessCorrection.createScaleAndTranslateLog(0.80f, 0.2f));
- assertEquals(config.getCorrectionByPackageName("some.package.name"),
+ assertEquals(returnedConfig.getCorrectionByPackageName("some.package.name"),
BrightnessCorrection.createScaleAndTranslateLog(0.70f, 0.1f));
- assertNull(config.getCorrectionByCategory(ApplicationInfo.CATEGORY_GAME));
- assertNull(config.getCorrectionByPackageName("someother.package.name"));
+ assertNull(returnedConfig.getCorrectionByCategory(ApplicationInfo.CATEGORY_GAME));
+ assertNull(returnedConfig.getCorrectionByPackageName("someother.package.name"));
+ assertEquals(defaultConfig.getShortTermModelTimeoutMillis() + 1000L,
+ returnedConfig.getShortTermModelTimeoutMillis());
+ assertEquals(defaultConfig.getShortTermModelLowerLuxMultiplier() + 0.2f,
+ returnedConfig.getShortTermModelLowerLuxMultiplier(), 0.001f);
+ assertEquals(defaultConfig.getShortTermModelUpperLuxMultiplier() + 0.3f,
+ returnedConfig.getShortTermModelUpperLuxMultiplier(), 0.001f);
// After clearing the curve we should get back the default curve.
mDisplayManager.setBrightnessConfiguration(null);
@@ -237,10 +295,9 @@
@Test
public void testGetDefaultCurve() {
- if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
- // Don't run as there is no app that has permission to push curves.
- return;
- }
+ // Don't run as there is no app that has permission to push curves.
+ assumeTrue(numberOfSystemAppsWithPermission(
+ Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0);
grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
@@ -256,20 +313,24 @@
assertEquals(0.0, curve.first[0], 0.1);
assertMonotonic(curve.first, true /*strictly increasing*/, "lux");
assertMonotonic(curve.second, false /*strictly increasing*/, "nits");
+ assertTrue(defaultConfig.getShortTermModelLowerLuxMultiplier() > 0.0f);
+ assertTrue(defaultConfig.getShortTermModelLowerLuxMultiplier() < 10.0f);
+ assertTrue(defaultConfig.getShortTermModelUpperLuxMultiplier() > 0.0f);
+ assertTrue(defaultConfig.getShortTermModelUpperLuxMultiplier() < 10.0f);
+ assertTrue(defaultConfig.getShortTermModelTimeoutMillis() > 0L);
+ assertTrue(defaultConfig.getShortTermModelTimeoutMillis() < 24 * 60 * 60 * 1000L);
+ assertFalse(defaultConfig.shouldCollectColorSamples());
}
@Test
public void testSliderEventsReflectCurves() throws InterruptedException {
- if (numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) == 0) {
- // Don't run as there is no app that has permission to access slider usage.
- return;
- }
- if (numberOfSystemAppsWithPermission(
- Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) == 0) {
- // Don't run as there is no app that has permission to push curves.
- return;
- }
+ // Don't run as there is no app that has permission to access slider usage.
+ assumeTrue(
+ numberOfSystemAppsWithPermission(Manifest.permission.BRIGHTNESS_SLIDER_USAGE) > 0);
+ // Don't run as there is no app that has permission to push curves.
+ assumeTrue(numberOfSystemAppsWithPermission(
+ Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0);
BrightnessConfiguration config =
new BrightnessConfiguration.Builder(
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index 1ded288..aeb5ce0 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -39,7 +39,6 @@
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
-import android.test.InstrumentationTestCase;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Display.HdrCapabilities;
@@ -98,16 +97,18 @@
@Rule
public ActivityTestRule<DisplayTestActivity> mDisplayTestActivity =
- new ActivityTestRule<>(DisplayTestActivity.class,
- false /* initialTouchMode */, false /* launchActivity */);
+ new ActivityTestRule<>(
+ DisplayTestActivity.class,
+ false /* initialTouchMode */,
+ false /* launchActivity */);
@Before
public void setUp() throws Exception {
mScreenOnActivity = launchScreenOnActivity();
mContext = InstrumentationRegistry.getInstrumentation().getContext();
- mDisplayManager = (DisplayManager)mContext.getSystemService(Context.DISPLAY_SERVICE);
- mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
- mUiModeManager = (UiModeManager)mContext.getSystemService(Context.UI_MODE_SERVICE);
+ mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ mUiModeManager = (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
mDefaultDisplay = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
mSupportedWideGamuts = mDefaultDisplay.getSupportedWideColorGamut();
}
@@ -208,6 +209,8 @@
assertFalse(cap.getDesiredMaxLuminance() < -1.0f);
assertFalse(cap.getDesiredMinLuminance() < -1.0f);
assertFalse(cap.getDesiredMaxAverageLuminance() < -1.0f);
+ assertTrue(cap.getDesiredMinLuminance() <= cap.getDesiredMaxAverageLuminance());
+ assertTrue(cap.getDesiredMaxAverageLuminance() <= cap.getDesiredMaxLuminance());
if (hdrTypes.length > 0) {
assertTrue(display.isHdr());
} else {
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index ed1d0c9..d8fad93 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -2344,16 +2344,16 @@
private Object[] parametersForTestNdkInfo() {
return new Object[] {
- new Object[] { Config.ALPHA_8, 8 /* ANDROID_BITMAP_FORMAT_A_8 */ },
- new Object[] { Config.ARGB_8888, 1 /* ANDROID_BITMAP_FORMAT_RGBA_8888 */ },
- new Object[] { Config.RGB_565, 4 /* ANDROID_BITMAP_FORMAT_RGB_565 */ },
- new Object[] { Config.RGBA_F16, 9 /* ANDROID_BITMAP_FORMAT_RGBA_F16*/ },
+ new Object[] { Config.ALPHA_8, ANDROID_BITMAP_FORMAT_A_8 },
+ new Object[] { Config.ARGB_8888, ANDROID_BITMAP_FORMAT_RGBA_8888 },
+ new Object[] { Config.RGB_565, ANDROID_BITMAP_FORMAT_RGB_565 },
+ new Object[] { Config.RGBA_F16, ANDROID_BITMAP_FORMAT_RGBA_F16 },
};
}
@Test
@Parameters(method = "parametersForTestNdkInfo")
- public void testNdkInfo(Config config, int androidBitmapFormat) {
+ public void testNdkInfo(Config config, final int expectedFormat) {
// Arbitrary width and height.
final int width = 13;
final int height = 7;
@@ -2362,17 +2362,27 @@
for (boolean premultiplied : trueFalse) {
Bitmap bm = Bitmap.createBitmap(width, height, config, hasAlpha);
bm.setPremultiplied(premultiplied);
- nTestInfo(bm, androidBitmapFormat, width, height, bm.hasAlpha(),
+ nTestInfo(bm, expectedFormat, width, height, bm.hasAlpha(),
bm.isPremultiplied(), false);
- bm = bm.copy(Bitmap.Config.HARDWARE, false);
+ Bitmap hwBitmap = bm.copy(Bitmap.Config.HARDWARE, false);
if (config == Bitmap.Config.ALPHA_8) {
// ALPHA_8 is not supported in HARDWARE. b/141480329
- assertNull(bm);
+ assertNull(hwBitmap);
} else {
- assertNotNull(bm);
- nTestInfo(bm, androidBitmapFormat, width, height, bm.hasAlpha(),
- bm.isPremultiplied(), true);
+ assertNotNull(hwBitmap);
+
+ // Some devices do not support F16 + HARDWARE. These fall back to 8888, and can
+ // be identified by their use of SRGB instead of EXTENDED_SRGB.
+ int tempExpectedFormat = expectedFormat;
+ if (config == Config.RGBA_F16 && hwBitmap.getColorSpace() == ColorSpace.get(
+ ColorSpace.Named.SRGB)) {
+ tempExpectedFormat = ANDROID_BITMAP_FORMAT_RGBA_8888;
+ }
+ nTestInfo(hwBitmap, tempExpectedFormat, width, height, hwBitmap.hasAlpha(),
+ hwBitmap.isPremultiplied(), true);
+ hwBitmap.recycle();
}
+ bm.recycle();
}
}
}
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 7a00734..e316b4d 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -32,6 +32,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INSTANT_APP_FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -47,8 +48,8 @@
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
- <activity android:name="android.media.cts.AudioPlaybackCaptureActivity"
- android:label="AudioPlaybackCaptureActivity"
+ <activity android:name="android.media.cts.MediaProjectionActivity"
+ android:label="MediaProjectionActivity"
android:screenOrientation="locked"/>
<activity android:name="android.media.cts.AudioManagerStub"
android:label="AudioManagerStub"/>
diff --git a/tests/tests/media/AndroidTest.xml b/tests/tests/media/AndroidTest.xml
index 70def5b..a04bcfc 100644
--- a/tests/tests/media/AndroidTest.xml
+++ b/tests/tests/media/AndroidTest.xml
@@ -31,6 +31,10 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaTestCases.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <!-- MediaProjectionTest needs this one to not be granted, SuiteApkInstaller grants all of them by default.-->
+ <option name="run-command" value="pm revoke android.media.cts android.permission.SYSTEM_ALERT_WINDOW"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="device" />
<option name="config-filename" value="CtsMediaTestCases" />
diff --git a/tests/tests/media/src/android/media/cts/AudioHelper.java b/tests/tests/media/src/android/media/cts/AudioHelper.java
index a0f8e47..695fb9d 100644
--- a/tests/tests/media/src/android/media/cts/AudioHelper.java
+++ b/tests/tests/media/src/android/media/cts/AudioHelper.java
@@ -261,8 +261,9 @@
private static final double TEST_STD_JITTER_MS_WARN = 1.; // CDD requirement warning
// CDD 5.6 100ms track startup latency
- private final double TEST_STARTUP_TIME_MS_ALLOWED; // CDD requirement error
- private static final double TEST_STARTUP_TIME_MS_WARN = 100.; // CDD requirement warning
+ private static final double TEST_STARTUP_TIME_MS_ALLOWED = 500.; // error
+ private final double TEST_STARTUP_TIME_MS_WARN; // warning
+ private static final double TEST_STARTUP_TIME_MS_INFO = 100.; // informational
private static final int MILLIS_PER_SECOND = 1000;
private static final long NANOS_PER_MILLISECOND = 1000000;
@@ -286,8 +287,8 @@
boolean isProAudioDevice) {
mTag = tag; // Log accepts null
mSampleRate = sampleRate;
- // For pro audio, allow slightly higher than MUST value to account for variability.
- TEST_STARTUP_TIME_MS_ALLOWED = isProAudioDevice ? 300. /* MUST is 200. */ : 500.;
+ // Warning if higher than MUST value for pro audio. Zero means ignore.
+ TEST_STARTUP_TIME_MS_WARN = isProAudioDevice ? 200. : 0.;
}
public int getJitterCount() { return mJitterCount; }
@@ -357,13 +358,16 @@
final double stdJitterMs = getStdJitterMs();
// Check startup time
- if (startupTimeMs > TEST_STARTUP_TIME_MS_WARN) {
+ assertTrue("expect startupTimeMs " + startupTimeMs
+ + " <= " + TEST_STARTUP_TIME_MS_ALLOWED,
+ startupTimeMs <= TEST_STARTUP_TIME_MS_ALLOWED);
+ if (TEST_STARTUP_TIME_MS_WARN > 0 && startupTimeMs > TEST_STARTUP_TIME_MS_WARN) {
Log.w(mTag, "CDD warning: startup time " + startupTimeMs
+ " > " + TEST_STARTUP_TIME_MS_WARN);
+ } else if (startupTimeMs > TEST_STARTUP_TIME_MS_INFO) {
+ Log.i(mTag, "CDD informational: startup time " + startupTimeMs
+ + " > " + TEST_STARTUP_TIME_MS_INFO);
}
- assertTrue("expect startupTimeMs " + startupTimeMs
- + " < " + TEST_STARTUP_TIME_MS_ALLOWED,
- startupTimeMs < TEST_STARTUP_TIME_MS_ALLOWED);
// Check maximum jitter
assertTrue("expect maxAbsJitterMs(" + mMaxAbsJitterMs + ") < "
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
index 7b00c9a..b67ec58 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureTest.java
@@ -60,7 +60,7 @@
* Test audio playback capture through MediaProjection.
*
* The tests do the following:
- * - retrieve a MediaProjection through AudioPlaybackCaptureActivity
+ * - retrieve a MediaProjection through MediaProjectionActivity
* - play some audio
* - use that MediaProjection to record the audio playing
* - check that some audio was recorded.
@@ -77,11 +77,11 @@
private AudioManager mAudioManager;
private boolean mPlaybackBeforeCapture;
private int mUid; //< UID of this test
- private AudioPlaybackCaptureActivity mActivity;
+ private MediaProjectionActivity mActivity;
private MediaProjection mMediaProjection;
@Rule
- public ActivityTestRule<AudioPlaybackCaptureActivity> mActivityRule =
- new ActivityTestRule<>(AudioPlaybackCaptureActivity.class);
+ public ActivityTestRule<MediaProjectionActivity> mActivityRule =
+ new ActivityTestRule<>(MediaProjectionActivity.class);
private static class APCTestConfig {
public @AttributeUsage int[] matchingUsages;
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
index 0327e2b..7f60d5f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecBlockModelTest.java
@@ -24,9 +24,12 @@
import android.media.MediaCodec.CodecException;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
+import android.media.MediaCrypto;
+import android.media.MediaDrm;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.cts.R;
+import android.net.Uri;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresDevice;
import android.test.AndroidTestCase;
@@ -40,9 +43,11 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;;
+import java.util.UUID;;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -52,9 +57,6 @@
/**
* MediaCodec tests with CONFIGURE_FLAG_USE_BLOCK_MODEL.
*/
-@Presubmit
-@SmallTest
-@RequiresDevice
public class MediaCodecBlockModelTest extends AndroidTestCase {
private static final String TAG = "MediaCodecBlockModelTest";
private static final boolean VERBOSE = false; // lots of logging
@@ -72,12 +74,15 @@
// The test should fail if the codec never produces output frames for the truncated input.
// Time out processing, as we have no way to query whether the decoder will produce output.
- private static final int TIMEOUT_MS = 2000;
+ private static final int TIMEOUT_MS = 60000; // 1 minute
/**
* Tests whether decoding a short group-of-pictures succeeds. The test queues a few video frames
* then signals end-of-stream. The test fails if the decoder doesn't output the queued frames.
*/
+ @Presubmit
+ @SmallTest
+ @RequiresDevice
public void testDecodeShortVideo() throws InterruptedException {
runThread(() -> runDecodeShortVideo(
INPUT_RESOURCE_ID,
@@ -90,9 +95,24 @@
}
/**
+ * Tests whether decoding a short encrypted group-of-pictures succeeds.
+ * The test queues a few encrypted video frames
+ * then signals end-of-stream. The test fails if the decoder doesn't output the queued frames.
+ */
+ public void testDecodeShortEncryptedVideo() throws InterruptedException {
+ runThread(() -> runDecodeShortEncryptedVideo(
+ true /* obtainBlockForEachBuffer */));
+ runThread(() -> runDecodeShortEncryptedVideo(
+ false /* obtainBlockForEachBuffer */));
+ }
+
+ /**
* Tests whether decoding a short audio succeeds. The test queues a few audio frames
* then signals end-of-stream. The test fails if the decoder doesn't output the queued frames.
*/
+ @Presubmit
+ @SmallTest
+ @RequiresDevice
public void testDecodeShortAudio() throws InterruptedException {
runThread(() -> runDecodeShortAudio(
INPUT_RESOURCE_ID,
@@ -108,6 +128,9 @@
* Tests whether encoding a short audio succeeds. The test queues a few audio frames
* then signals end-of-stream. The test fails if the encoder doesn't output the queued frames.
*/
+ @Presubmit
+ @SmallTest
+ @RequiresDevice
public void testEncodeShortAudio() throws InterruptedException {
runThread(() -> runEncodeShortAudio());
}
@@ -116,18 +139,28 @@
* Tests whether encoding a short video succeeds. The test queues a few video frames
* then signals end-of-stream. The test fails if the encoder doesn't output the queued frames.
*/
+ @Presubmit
+ @SmallTest
+ @RequiresDevice
public void testEncodeShortVideo() throws InterruptedException {
runThread(() -> runEncodeShortVideo());
}
+ /**
+ * Test whether format change happens as expected in block model mode.
+ */
+ @Presubmit
+ @SmallTest
+ @RequiresDevice
public void testFormatChange() throws InterruptedException {
List<FormatChangeEvent> events = new ArrayList<>();
runThread(() -> runDecodeShortVideo(
- INPUT_RESOURCE_ID,
+ getMediaExtractorForMimeType(INPUT_RESOURCE_ID, "video/"),
LAST_BUFFER_TIMESTAMP_US,
true /* obtainBlockForEachBuffer */,
MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 320, 240),
- events));
+ events,
+ null /* sessionId */));
int width = 320;
int height = 240;
for (FormatChangeEvent event : events) {
@@ -173,15 +206,60 @@
}
private static class ExtractorInputSlotListener implements InputSlotListener {
- public ExtractorInputSlotListener(
+ public static class Builder {
+ public Builder setExtractor(MediaExtractor extractor) {
+ mExtractor = extractor;
+ return this;
+ }
+
+ public Builder setLastBufferTimestampUs(Long timestampUs) {
+ mLastBufferTimestampUs = timestampUs;
+ return this;
+ }
+
+ public Builder setObtainBlockForEachBuffer(boolean enabled) {
+ mObtainBlockForEachBuffer = enabled;
+ return this;
+ }
+
+ public Builder setTimestampQueue(List<Long> list) {
+ mTimestampList = list;
+ return this;
+ }
+
+ public Builder setContentEncrypted(boolean encrypted) {
+ mContentEncrypted = encrypted;
+ return this;
+ }
+
+ public ExtractorInputSlotListener build() {
+ if (mExtractor == null) {
+ throw new IllegalStateException("Extractor must be set");
+ }
+ return new ExtractorInputSlotListener(
+ mExtractor, mLastBufferTimestampUs,
+ mObtainBlockForEachBuffer, mTimestampList,
+ mContentEncrypted);
+ }
+
+ private MediaExtractor mExtractor = null;
+ private Long mLastBufferTimestampUs = null;
+ private boolean mObtainBlockForEachBuffer = false;
+ private List<Long> mTimestampList = null;
+ private boolean mContentEncrypted = false;
+ }
+
+ private ExtractorInputSlotListener(
MediaExtractor extractor,
- long lastBufferTimestampUs,
+ Long lastBufferTimestampUs,
boolean obtainBlockForEachBuffer,
- LinkedBlockingQueue<Long> timestampQueue) {
+ List<Long> timestampList,
+ boolean contentEncrypted) {
mExtractor = extractor;
mLastBufferTimestampUs = lastBufferTimestampUs;
mObtainBlockForEachBuffer = obtainBlockForEachBuffer;
- mTimestampQueue = timestampQueue;
+ mTimestampList = timestampList;
+ mContentEncrypted = contentEncrypted;
}
@Override
@@ -192,6 +270,9 @@
}
long size = mExtractor.getSampleSize();
String[] codecNames = new String[]{ codec.getName() };
+ if (mContentEncrypted) {
+ codecNames[0] = codecNames[0] + ".secure";
+ }
if (mObtainBlockForEachBuffer) {
input.block.recycle();
input.block = MediaCodec.LinearBlock.obtain(Math.toIntExact(size), codecNames);
@@ -220,11 +301,21 @@
}
long timestampUs = mExtractor.getSampleTime();
int written = mExtractor.readSampleData(input.buffer, input.offset);
+ boolean encrypted =
+ (mExtractor.getSampleFlags() & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0;
+ if (encrypted) {
+ mExtractor.getSampleCryptoInfo(mCryptoInfo);
+ }
mExtractor.advance();
mSignaledEos = mExtractor.getSampleTrackIndex() == -1
- || timestampUs >= mLastBufferTimestampUs;
+ || (mLastBufferTimestampUs != null && timestampUs >= mLastBufferTimestampUs);
MediaCodec.QueueRequest request = codec.getQueueRequest(index);
- request.setLinearBlock(input.block, input.offset, written);
+ if (encrypted) {
+ request.setEncryptedLinearBlock(
+ input.block, input.offset, written, mCryptoInfo);
+ } else {
+ request.setLinearBlock(input.block, input.offset, written);
+ }
request.setPresentationTimeUs(timestampUs);
request.setFlags(mSignaledEos ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
if (mSetParams) {
@@ -237,17 +328,19 @@
}
request.queue();
input.offset += written;
- if (mTimestampQueue != null) {
- mTimestampQueue.offer(timestampUs);
+ if (mTimestampList != null) {
+ mTimestampList.add(timestampUs);
}
}
private final MediaExtractor mExtractor;
- private final long mLastBufferTimestampUs;
+ private final Long mLastBufferTimestampUs;
private final boolean mObtainBlockForEachBuffer;
- private final LinkedBlockingQueue<Long> mTimestampQueue;
+ private final List<Long> mTimestampList;
private boolean mSignaledEos = false;
private boolean mSetParams = true;
+ private final MediaCodec.CryptoInfo mCryptoInfo = new MediaCodec.CryptoInfo();
+ private final boolean mContentEncrypted;
}
private static interface OutputSlotListener {
@@ -256,9 +349,9 @@
}
private static class DummyOutputSlotListener implements OutputSlotListener {
- public DummyOutputSlotListener(boolean graphic, LinkedBlockingQueue<Long> timestampQueue) {
+ public DummyOutputSlotListener(boolean graphic, List<Long> timestampList) {
mGraphic = graphic;
- mTimestampQueue = timestampQueue;
+ mTimestampList = timestampList;
}
@Override
@@ -273,10 +366,7 @@
frame.getLinearBlock().recycle();
}
- Long ts = mTimestampQueue.peek();
- if (ts != null && ts == frame.getPresentationTimeUs()) {
- mTimestampQueue.poll();
- }
+ mTimestampList.remove(frame.getPresentationTimeUs());
codec.releaseOutputBuffer(index, false);
@@ -284,16 +374,16 @@
}
private final boolean mGraphic;
- private final LinkedBlockingQueue<Long> mTimestampQueue;
+ private final List<Long> mTimestampList;
}
private static class SurfaceOutputSlotListener implements OutputSlotListener {
public SurfaceOutputSlotListener(
OutputSurface surface,
- LinkedBlockingQueue<Long> timestampQueue,
+ List<Long> timestampList,
List<FormatChangeEvent> events) {
mOutputSurface = surface;
- mTimestampQueue = timestampQueue;
+ mTimestampList = timestampList;
mEvents = (events != null) ? events : new ArrayList<>();
}
@@ -308,13 +398,11 @@
render = true;
}
- Long ts = mTimestampQueue.peek();
- if (ts != null && ts == frame.getPresentationTimeUs()) {
- mTimestampQueue.poll();
- }
+ mTimestampList.remove(frame.getPresentationTimeUs());
if (!frame.getChangedKeys().isEmpty()) {
- mEvents.add(new FormatChangeEvent(ts, frame.getChangedKeys(), frame.getFormat()));
+ mEvents.add(new FormatChangeEvent(
+ frame.getPresentationTimeUs(), frame.getChangedKeys(), frame.getFormat()));
}
codec.releaseOutputBuffer(index, render);
@@ -326,7 +414,7 @@
}
private final OutputSurface mOutputSurface;
- private final LinkedBlockingQueue<Long> mTimestampQueue;
+ private final List<Long> mTimestampList;
private final List<FormatChangeEvent> mEvents;
}
@@ -344,7 +432,74 @@
long lastBufferTimestampUs,
boolean obtainBlockForEachBuffer) {
return runDecodeShortVideo(
- inputResourceId, lastBufferTimestampUs, obtainBlockForEachBuffer, null, null);
+ getMediaExtractorForMimeType(inputResourceId, "video/"),
+ lastBufferTimestampUs, obtainBlockForEachBuffer, null, null, null);
+ }
+
+ private static final UUID CLEARKEY_SCHEME_UUID =
+ new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+
+ private static final byte[] CLEAR_KEY_CENC = convert(new int[] {
+ 0x3f, 0x0a, 0x33, 0xf3, 0x40, 0x98, 0xb9, 0xe2,
+ 0x2b, 0xc0, 0x78, 0xe0, 0xa1, 0xb5, 0xe8, 0x54 });
+
+ private static final byte[] DRM_INIT_DATA = convert(new int[] {
+ // BMFF box header (4 bytes size + 'pssh')
+ 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+ // Full box header (version = 1 flags = 0)
+ 0x01, 0x00, 0x00, 0x00,
+ // SystemID
+ 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c,
+ 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+ // Number of key ids
+ 0x00, 0x00, 0x00, 0x01,
+ // Key id
+ 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
+ 0x30, 0x30, 0x30, 0x30, 0x30,
+ // size of data, must be zero
+ 0x00, 0x00, 0x00, 0x00 });
+
+ private static final long ENCRYPTED_CONTENT_FIRST_BUFFER_TIMESTAMP_US = 12083333;
+ private static final long ENCRYPTED_CONTENT_LAST_BUFFER_TIMESTAMP_US = 15041666;
+
+ private static byte[] convert(int[] intArray) {
+ byte[] byteArray = new byte[intArray.length];
+ for (int i = 0; i < intArray.length; ++i) {
+ byteArray[i] = (byte)intArray[i];
+ }
+ return byteArray;
+ }
+
+ private boolean runDecodeShortEncryptedVideo(boolean obtainBlockForEachBuffer) {
+ MediaExtractor extractor = new MediaExtractor();
+
+ try (final MediaDrm drm = new MediaDrm(CLEARKEY_SCHEME_UUID)) {
+ Uri uri = Uri.parse(Utils.getMediaPath() + "/clearkey/llama_h264_main_720p_8000.mp4");
+ extractor.setDataSource(uri.toString(), null);
+ extractor.selectTrack(0);
+ extractor.seekTo(12083333, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ drm.setOnEventListener(
+ (MediaDrm mediaDrm, byte[] sessionId, int event, int extra, byte[] data) -> {
+ if (event == MediaDrm.EVENT_KEY_REQUIRED
+ || event == MediaDrm.EVENT_KEY_EXPIRED) {
+ MediaDrmClearkeyTest.retrieveKeys(
+ mediaDrm, "cenc", sessionId, DRM_INIT_DATA,
+ MediaDrm.KEY_TYPE_STREAMING,
+ new byte[][] { CLEAR_KEY_CENC });
+ }
+ });
+ byte[] sessionId = drm.openSession();
+ MediaDrmClearkeyTest.retrieveKeys(
+ drm, "cenc", sessionId, DRM_INIT_DATA, MediaDrm.KEY_TYPE_STREAMING,
+ new byte[][] { CLEAR_KEY_CENC });
+ boolean result = runDecodeShortVideo(
+ extractor, ENCRYPTED_CONTENT_LAST_BUFFER_TIMESTAMP_US,
+ obtainBlockForEachBuffer, null /* format */, null /* events */, sessionId);
+ drm.closeSession(sessionId);
+ return result;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
private static class FormatChangeEvent {
@@ -366,18 +521,19 @@
}
private boolean runDecodeShortVideo(
- int inputResourceId,
- long lastBufferTimestampUs,
+ MediaExtractor mediaExtractor,
+ Long lastBufferTimestampUs,
boolean obtainBlockForEachBuffer,
MediaFormat format,
- List<FormatChangeEvent> events) {
+ List<FormatChangeEvent> events,
+ byte[] sessionId) {
OutputSurface outputSurface = null;
- MediaExtractor mediaExtractor = null;
MediaCodec mediaCodec = null;
+ MediaCrypto crypto = null;
try {
outputSurface = new OutputSurface(1, 1);
- mediaExtractor = getMediaExtractorForMimeType(inputResourceId, "video/");
- MediaFormat mediaFormat = mediaExtractor.getTrackFormat(mediaExtractor.getSampleTrackIndex());
+ MediaFormat mediaFormat = mediaExtractor.getTrackFormat(
+ mediaExtractor.getSampleTrackIndex());
if (format != null) {
// copy CSD
for (int i = 0; i < 3; ++i) {
@@ -396,21 +552,28 @@
}
mediaCodec = MediaCodec.createByCodecName(codecs[0]);
- LinkedBlockingQueue<Long> timestampQueue = new LinkedBlockingQueue<>();
+ if (sessionId != null) {
+ crypto = new MediaCrypto(CLEARKEY_SCHEME_UUID, new byte[0] /* initData */);
+ crypto.setMediaDrmSession(sessionId);
+ }
+ List<Long> timestampList = Collections.synchronizedList(new ArrayList<>());
boolean result = runComponentWithLinearInput(
mediaCodec,
+ crypto,
mediaFormat,
outputSurface.getSurface(),
false, // encoder
- new ExtractorInputSlotListener(
- mediaExtractor,
- lastBufferTimestampUs,
- obtainBlockForEachBuffer,
- timestampQueue),
- new SurfaceOutputSlotListener(outputSurface, timestampQueue, events));
+ new ExtractorInputSlotListener.Builder()
+ .setExtractor(mediaExtractor)
+ .setLastBufferTimestampUs(lastBufferTimestampUs)
+ .setObtainBlockForEachBuffer(obtainBlockForEachBuffer)
+ .setTimestampQueue(timestampList)
+ .setContentEncrypted(sessionId != null)
+ .build(),
+ new SurfaceOutputSlotListener(outputSurface, timestampList, events));
if (result) {
- assertTrue("Timestamp should match between input / output",
- timestampQueue.isEmpty());
+ assertTrue("Timestamp should match between input / output: " + timestampList,
+ timestampList.isEmpty());
}
return result;
} catch (IOException e) {
@@ -428,6 +591,9 @@
if (outputSurface != null) {
outputSurface.release();
}
+ if (crypto != null) {
+ crypto.release();
+ }
}
}
@@ -449,21 +615,23 @@
}
mediaCodec = MediaCodec.createByCodecName(codecs[0]);
- LinkedBlockingQueue<Long> timestampQueue = new LinkedBlockingQueue<>();
+ List<Long> timestampList = Collections.synchronizedList(new ArrayList<>());
boolean result = runComponentWithLinearInput(
mediaCodec,
+ null, // crypto
mediaFormat,
null, // surface
false, // encoder
- new ExtractorInputSlotListener(
- mediaExtractor,
- lastBufferTimestampUs,
- obtainBlockForEachBuffer,
- timestampQueue),
- new DummyOutputSlotListener(false /* graphic */, timestampQueue));
+ new ExtractorInputSlotListener.Builder()
+ .setExtractor(mediaExtractor)
+ .setLastBufferTimestampUs(lastBufferTimestampUs)
+ .setObtainBlockForEachBuffer(obtainBlockForEachBuffer)
+ .setTimestampQueue(timestampList)
+ .build(),
+ new DummyOutputSlotListener(false /* graphic */, timestampList));
if (result) {
- assertTrue("Timestamp should match between input / output",
- timestampQueue.isEmpty());
+ assertTrue("Timestamp should match between input / output: " + timestampList,
+ timestampList.isEmpty());
}
return result;
} catch (IOException e) {
@@ -499,21 +667,22 @@
}
mediaCodec = MediaCodec.createByCodecName(codecs[0]);
- LinkedBlockingQueue<Long> timestampQueue = new LinkedBlockingQueue<>();
+ List<Long> timestampList = Collections.synchronizedList(new ArrayList<>());
boolean result = runComponentWithLinearInput(
mediaCodec,
+ null, // crypto
mediaFormat,
null, // surface
true, // encoder
- new ExtractorInputSlotListener(
- mediaExtractor,
- LAST_BUFFER_TIMESTAMP_US,
- false,
- timestampQueue),
- new DummyOutputSlotListener(false /* graphic */, timestampQueue));
+ new ExtractorInputSlotListener.Builder()
+ .setExtractor(mediaExtractor)
+ .setLastBufferTimestampUs(LAST_BUFFER_TIMESTAMP_US)
+ .setTimestampQueue(timestampList)
+ .build(),
+ new DummyOutputSlotListener(false /* graphic */, timestampList));
if (result) {
- assertTrue("Timestamp should match between input / output",
- timestampQueue.isEmpty());
+ assertTrue("Timestamp should match between input / output: " + timestampList,
+ timestampList.isEmpty());
}
return result;
} catch (IOException e) {
@@ -563,7 +732,7 @@
return true;
}
- LinkedBlockingQueue<Long> timestampQueue = new LinkedBlockingQueue<>();
+ List<Long> timestampList = Collections.synchronizedList(new ArrayList<>());
final LinkedBlockingQueue<SlotEvent> queue = new LinkedBlockingQueue<>();
mediaCodec.setCallback(new MediaCodec.Callback() {
@@ -642,7 +811,7 @@
if (frameIndex >= 32) {
signaledEos = true;
}
- timestampQueue.offer(timestampUs);
+ timestampList.add(timestampUs);
mediaCodec.getQueueRequest(event.index)
.setHardwareBuffer(buffer)
.setPresentationTimeUs(timestampUs)
@@ -658,18 +827,15 @@
frame.getLinearBlock().recycle();
}
- Long ts = timestampQueue.peek();
- if (ts != null && ts == frame.getPresentationTimeUs()) {
- timestampQueue.poll();
- }
+ timestampList.remove(frame.getPresentationTimeUs());
mediaCodec.releaseOutputBuffer(event.index, false);
}
}
- if (!timestampQueue.isEmpty()) {
- assertTrue("Timestamp should match between input / output",
- timestampQueue.isEmpty());
+ if (!timestampList.isEmpty()) {
+ assertTrue("Timestamp should match between input / output: " + timestampList,
+ timestampList.isEmpty());
}
return eos;
} catch (IOException e) {
@@ -691,6 +857,7 @@
private boolean runComponentWithLinearInput(
MediaCodec mediaCodec,
+ MediaCrypto crypto,
MediaFormat mediaFormat,
Surface surface,
boolean encoder,
@@ -723,6 +890,9 @@
assertTrue("Google default c2.* codecs are copy-free compatible with LinearBlocks",
MediaCodec.LinearBlock.isCodecCopyFreeCompatible(codecNames));
}
+ if (crypto != null) {
+ codecNames[0] = codecNames[0] + ".secure";
+ }
input.block = MediaCodec.LinearBlock.obtain(
APP_BUFFER_SIZE, codecNames);
assertTrue("Blocks obtained through LinearBlock.obtain must be mappable",
@@ -734,7 +904,7 @@
if (encoder) {
flags |= MediaCodec.CONFIGURE_FLAG_ENCODE;
}
- mediaCodec.configure(mediaFormat, surface, null, flags);
+ mediaCodec.configure(mediaFormat, surface, crypto, flags);
mediaCodec.start();
boolean eos = false;
boolean signaledEos = false;
@@ -757,15 +927,13 @@
return eos;
}
- private MediaExtractor getMediaExtractorForMimeType(int resourceId, String mimeTypePrefix)
- throws IOException {
+ private MediaExtractor getMediaExtractorForMimeType(int resourceId, String mimeTypePrefix) {
MediaExtractor mediaExtractor = new MediaExtractor();
- AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(resourceId);
- try {
+ try (AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(resourceId)) {
mediaExtractor.setDataSource(
afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- } finally {
- afd.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
int trackIndex;
for (trackIndex = 0; trackIndex < mediaExtractor.getTrackCount(); trackIndex++) {
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
index 5f9b499..f648a3c 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmClearkeyTest.java
@@ -138,7 +138,7 @@
*
* @return size of keyIds vector that contains the key ids, 0 for error
*/
- private int getKeyIds(byte[] keyRequestBlob, Vector<String> keyIds) {
+ private static int getKeyIds(byte[] keyRequestBlob, Vector<String> keyIds) {
if (0 == keyRequestBlob.length || keyIds == null)
return 0;
@@ -163,7 +163,8 @@
*
* @return JSON Web Key string.
*/
- private String createJsonWebKeySet(Vector<String> keyIds, Vector<String> keys, int keyType) {
+ private static String createJsonWebKeySet(
+ Vector<String> keyIds, Vector<String> keys, int keyType) {
String jwkSet = "{\"keys\":[";
for (int i = 0; i < keyIds.size(); ++i) {
String id = new String(keyIds.get(i).getBytes(Charset.forName("UTF-8")));
@@ -184,8 +185,10 @@
/**
* Retrieves clear key ids from getKeyRequest(), create JSON Web Key
* set and send it to the CDM via provideKeyResponse().
+ *
+ * @return key set ID
*/
- private void getKeys(MediaDrm drm, String initDataType,
+ static byte[] retrieveKeys(MediaDrm drm, String initDataType,
byte[] sessionId, byte[] drmInitData, int keyType, byte[][] clearKeyIds) {
MediaDrm.KeyRequest drmRequest = null;
try {
@@ -197,19 +200,19 @@
}
if (drmRequest == null) {
Log.e(TAG, "Failed getKeyRequest");
- return;
+ return null;
}
Vector<String> keyIds = new Vector<String>();
if (0 == getKeyIds(drmRequest.getData(), keyIds)) {
Log.e(TAG, "No key ids found in initData");
- return;
+ return null;
}
if (clearKeyIds.length != keyIds.size()) {
Log.e(TAG, "Mismatch number of key ids and keys: ids=" +
keyIds.size() + ", keys=" + clearKeyIds.length);
- return;
+ return null;
}
// Base64 encodes clearkeys. Keys are known to the application.
@@ -225,7 +228,7 @@
try {
try {
- mKeySetId = drm.provideKeyResponse(sessionId, jsonResponse);
+ return drm.provideKeyResponse(sessionId, jsonResponse);
} catch (IllegalStateException e) {
Log.e(TAG, "Failed to provide key response: " + e.toString());
}
@@ -233,6 +236,16 @@
e.printStackTrace();
Log.e(TAG, "Failed to provide key response: " + e.toString());
}
+ return null;
+ }
+
+ /**
+ * Retrieves clear key ids from getKeyRequest(), create JSON Web Key
+ * set and send it to the CDM via provideKeyResponse().
+ */
+ private void getKeys(MediaDrm drm, String initDataType,
+ byte[] sessionId, byte[] drmInitData, int keyType, byte[][] clearKeyIds) {
+ mKeySetId = retrieveKeys(drm, initDataType, sessionId, drmInitData, keyType, clearKeyIds);
}
private @NonNull MediaDrm startDrm(final byte[][] clearKeyIds, final String initDataType,
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureActivity.java b/tests/tests/media/src/android/media/cts/MediaProjectionActivity.java
similarity index 97%
rename from tests/tests/media/src/android/media/cts/AudioPlaybackCaptureActivity.java
rename to tests/tests/media/src/android/media/cts/MediaProjectionActivity.java
index d74772b..8ec5499 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackCaptureActivity.java
+++ b/tests/tests/media/src/android/media/cts/MediaProjectionActivity.java
@@ -43,8 +43,8 @@
// This is a partial copy of android.view.cts.surfacevalidator.CapturedActivity.
// Common code should be move in a shared library
/** Start this activity to retrieve a MediaProjection through waitForMediaProjection() */
-public class AudioPlaybackCaptureActivity extends Activity {
- private static final String TAG = "AudioPlaybackCaptureActivity";
+public class MediaProjectionActivity extends Activity {
+ private static final String TAG = "MediaProjectionActivity";
private static final int PERMISSION_CODE = 1;
private static final int PERMISSION_DIALOG_WAIT_MS = 1000;
private static final String ACCEPT_RESOURCE_ID = "android:id/button1";
diff --git a/tests/tests/media/src/android/media/cts/MediaProjectionTest.java b/tests/tests/media/src/android/media/cts/MediaProjectionTest.java
new file mode 100644
index 0000000..7bd9c64
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaProjectionTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.media.projection.MediaProjection;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test MediaProjection lifecycle.
+ *
+ * This test starts and stops a MediaProjection screen capture session using
+ * MediaProjectionActivity.
+ *
+ * Currently we check that we are able to draw overlay windows during the session but not before
+ * or after. (We request SYATEM_ALERT_WINDOW permission, but it is not granted, so by default we
+ * cannot.)
+ *
+ * Note that there are other tests verifying that screen capturing actually works correctly in
+ * CtsWindowManagerDeviceTestCases.
+ */
+@NonMediaMainlineTest
+public class MediaProjectionTest {
+ @Rule
+ public ActivityTestRule<MediaProjectionActivity> mActivityRule =
+ new ActivityTestRule<>(MediaProjectionActivity.class, false, false);
+
+ private MediaProjectionActivity mActivity;
+ private MediaProjection mMediaProjection;
+ private Context mContext;
+
+ @Before
+ public void setUp() {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
+ @Test
+ public void testOverlayAllowedDuringScreenCapture() throws Exception {
+ assertFalse(Settings.canDrawOverlays(mContext));
+
+ startMediaProjection();
+ assertTrue(Settings.canDrawOverlays(mContext));
+
+ stopMediaProjection();
+ assertFalse(Settings.canDrawOverlays(mContext));
+ }
+
+ private void startMediaProjection() throws Exception {
+ mActivityRule.launchActivity(null);
+ mActivity = mActivityRule.getActivity();
+ mMediaProjection = mActivity.waitForMediaProjection();
+ }
+
+ private void stopMediaProjection() throws Exception {
+ final int STOP_TIMEOUT_MS = 1000;
+ CountDownLatch stoppedLatch = new CountDownLatch(1);
+
+ mMediaProjection.registerCallback(new MediaProjection.Callback() {
+ public void onStop() {
+ stoppedLatch.countDown();
+ }
+ }, new Handler(Looper.getMainLooper()));
+ mMediaProjection.stop();
+
+ assertTrue("Could not stop the MediaProjection in " + STOP_TIMEOUT_MS + "ms",
+ stoppedLatch.await(STOP_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
index 2d1998e..b6be506 100644
--- a/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
+++ b/tests/tests/media/src/android/media/cts/MediaRouter2Test.java
@@ -121,6 +121,15 @@
}
}
+ @Test
+ public void testGetRoutesAfterCreation() {
+ List<MediaRoute2Info> initialRoutes = mRouter2.getRoutes();
+ assertFalse(initialRoutes.isEmpty());
+ for (MediaRoute2Info route : initialRoutes) {
+ assertTrue(route.isSystemRoute());
+ }
+ }
+
/**
* Tests if we get proper routes for application that has special route type.
*/
diff --git a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
index d62bc6a..7df46ea 100644
--- a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
+++ b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MediaParserTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
+import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaParser;
import android.os.Build;
@@ -29,8 +30,8 @@
import androidx.test.platform.app.InstrumentationRegistry;
import com.google.android.exoplayer2.testutil.FakeExtractorInput;
-import com.google.android.exoplayer2.testutil.FakeExtractorOutput;
import com.google.android.exoplayer2.testutil.TestUtil;
+import com.google.android.exoplayer2.util.Util;
import org.junit.Before;
import org.junit.Ignore;
@@ -38,18 +39,19 @@
import org.junit.runner.RunWith;
import java.io.IOException;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
@RunWith(AndroidJUnit4.class)
public class MediaParserTest {
+ private static final String PARAMETER_IN_BAND_CRYPTO_INFO =
+ "android.media.mediaparser.inBandCryptoInfo";
+
@Before
public void setUp() {
- String version = Build.VERSION.CODENAME;
- // Avoid running these tests before R, on which MediaParser was defined.
- // These check is inlined from BuildCompat to avoid bringing in the entire dependency.
- assumeTrue(version.length() == 1 && version.charAt(0) >= 'R' && version.charAt(0) <= 'Z');
+ assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
}
@Test
@@ -203,8 +205,7 @@
@Test
public void testLackOfSupportForUnsupportedParameter() {
- MediaParser mediaParser =
- MediaParser.create(new MockMediaParserOutputConsumer(new FakeExtractorOutput()));
+ MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
assertThat(mediaParser.supportsParameter("android.media.mediaparser.UNSUPPORTED_PARAMETER"))
.isFalse();
mediaParser.release();
@@ -213,32 +214,32 @@
// OGG.
@Test
- public void testOggBearVorbis() throws IOException, InterruptedException {
- testExtractAsset("ogg/bear_vorbis.ogg");
+ public void testOggBearVorbis() throws IOException {
+ testAssetExtraction("ogg/bear_vorbis.ogg");
}
@Test
- public void testOggBear() throws IOException, InterruptedException {
- testExtractAsset("ogg/bear.opus");
+ public void testOggBear() throws IOException {
+ testAssetExtraction("ogg/bear.opus");
}
@Test
- public void testOggBearFlac() throws IOException, InterruptedException {
- testExtractAsset("ogg/bear_flac.ogg");
+ public void testOggBearFlac() throws IOException {
+ testAssetExtraction("ogg/bear_flac.ogg");
}
@Test
- public void testOggNoFlacSeekTable() throws IOException, InterruptedException {
- testExtractAsset("ogg/bear_flac_noseektable.ogg");
+ public void testOggNoFlacSeekTable() throws IOException {
+ testAssetExtraction("ogg/bear_flac_noseektable.ogg");
}
@Test
- public void testOggFlacHeaderSniff() throws IOException, InterruptedException {
+ public void testOggFlacHeaderSniff() throws IOException {
testSniffAsset("ogg/flac_header", /* expectedExtractorName= */ MediaParser.PARSER_NAME_OGG);
}
@Test
- public void testOggOpusHeaderSniff() throws IOException, InterruptedException {
+ public void testOggOpusHeaderSniff() throws IOException {
try {
testSniffAsset(
"ogg/opus_header", /* expectedExtractorName= */ MediaParser.PARSER_NAME_OGG);
@@ -249,7 +250,7 @@
}
@Test
- public void testOggInvalidHeaderSniff() throws IOException, InterruptedException {
+ public void testOggInvalidHeaderSniff() throws IOException {
try {
testSniffAsset(
"ogg/invalid_ogg_header",
@@ -270,118 +271,116 @@
// FLAC.
@Test
- public void testFlacUncommonSampleRateFlac() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_uncommon_sample_rate.flac");
+ public void testFlacUncommonSampleRateFlac() throws IOException {
+ testAssetExtraction("flac/bear_uncommon_sample_rate.flac");
}
@Test
- public void testFlacNoSeekTableAndNoNumSamples() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_no_seek_table_no_num_samples.flac");
+ public void testFlacNoSeekTableAndNoNumSamples() throws IOException {
+ testAssetExtraction("flac/bear_no_seek_table_no_num_samples.flac");
}
@Test
- public void testFlacWithPicture() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_with_picture.flac");
+ public void testFlacWithPicture() throws IOException {
+ testAssetExtraction("flac/bear_with_picture.flac");
}
@Test
- public void testFlacWithVorbisComments() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_with_vorbis_comments.flac");
+ public void testFlacWithVorbisComments() throws IOException {
+ testAssetExtraction("flac/bear_with_vorbis_comments.flac");
}
@Test
- public void testFlacOneMetadataBlock() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_one_metadata_block.flac");
+ public void testFlacOneMetadataBlock() throws IOException {
+ testAssetExtraction("flac/bear_one_metadata_block.flac");
}
@Test
- public void testFlacNoMinMaxFrameSize() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_no_min_max_frame_size.flac");
+ public void testFlacNoMinMaxFrameSize() throws IOException {
+ testAssetExtraction("flac/bear_no_min_max_frame_size.flac");
}
@Test
- public void testFlacNoNumSamples() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_no_num_samples.flac");
+ public void testFlacNoNumSamples() throws IOException {
+ testAssetExtraction("flac/bear_no_num_samples.flac");
}
@Test
- public void testFlacWithId3() throws IOException, InterruptedException {
- testExtractAsset("flac/bear_with_id3.flac");
+ public void testFlacWithId3() throws IOException {
+ testAssetExtraction("flac/bear_with_id3.flac");
}
@Test
- public void testFlacSample() throws IOException, InterruptedException {
- testExtractAsset("flac/bear.flac");
+ public void testFlacSample() throws IOException {
+ testAssetExtraction("flac/bear.flac");
}
// MP3.
@Test
- public void testMp3WithNoSeekTableVariableFrameSize() throws IOException, InterruptedException {
- testExtractAsset("mp3/bear-cbr-variable-frame-size-no-seek-table.mp3");
+ public void testMp3WithNoSeekTableVariableFrameSize() throws IOException {
+ testAssetExtraction("mp3/bear-cbr-variable-frame-size-no-seek-table.mp3");
}
@Test
- public void testMp3WithVariableBitrateAndXingHeader() throws IOException, InterruptedException {
- testExtractAsset("mp3/bear-vbr-xing-header.mp3");
+ public void testMp3WithVariableBitrateAndXingHeader() throws IOException {
+ testAssetExtraction("mp3/bear-vbr-xing-header.mp3");
}
@Test
- public void testMp3WithNoSeekTableVariableBitrate() throws IOException, InterruptedException {
- testExtractAsset("mp3/bear-vbr-no-seek-table.mp3");
+ public void testMp3WithNoSeekTableVariableBitrate() throws IOException {
+ testAssetExtraction("mp3/bear-vbr-no-seek-table.mp3");
}
@Test
- public void testMp3WithTrimmedSample() throws IOException, InterruptedException {
- testExtractAsset("mp3/play-trimmed.mp3");
+ public void testMp3WithTrimmedSample() throws IOException {
+ testAssetExtraction("mp3/play-trimmed.mp3");
}
@Test
- public void testMp3WithId3() throws IOException, InterruptedException {
- testExtractAsset("mp3/bear-id3.mp3");
+ public void testMp3WithId3() throws IOException {
+ testAssetExtraction("mp3/bear-id3.mp3");
}
// WAV.
@Test
- public void testWavWithImaAdpcm() throws IOException, InterruptedException {
- testExtractAsset("wav/sample_ima_adpcm.wav");
+ public void testWavWithImaAdpcm() throws IOException {
+ testAssetExtraction("wav/sample_ima_adpcm.wav");
}
@Test
- public void testWav() throws IOException, InterruptedException {
- testExtractAsset("wav/sample.wav");
+ public void testWav() throws IOException {
+ testAssetExtraction("wav/sample.wav");
}
// AMR.
@Test
- public void testAmrNarrowBandSamplesWithConstantBitrateSeeking()
- throws IOException, InterruptedException {
- testExtractAsset("amr/sample_nb_cbr.amr");
+ public void testAmrNarrowBandSamplesWithConstantBitrateSeeking() throws IOException {
+ testAssetExtraction("amr/sample_nb_cbr.amr");
}
@Test
- public void testAmrNarrowBandSamples() throws IOException, InterruptedException {
- testExtractAsset("amr/sample_nb.amr");
+ public void testAmrNarrowBandSamples() throws IOException {
+ testAssetExtraction("amr/sample_nb.amr");
}
@Test
- public void testAmrWideBandSamples() throws IOException, InterruptedException {
- testExtractAsset("amr/sample_wb.amr");
+ public void testAmrWideBandSamples() throws IOException {
+ testAssetExtraction("amr/sample_wb.amr");
}
@Test
- public void testAmrWideBandSamplesWithConstantBitrateSeeking()
- throws IOException, InterruptedException {
- testExtractAsset("amr/sample_wb_cbr.amr");
+ public void testAmrWideBandSamplesWithConstantBitrateSeeking() throws IOException {
+ testAssetExtraction("amr/sample_wb_cbr.amr");
}
// FLV.
@Test
- public void testFlv() throws IOException, InterruptedException {
- testExtractAsset("flv/sample.flv");
+ public void testFlv() throws IOException {
+ testAssetExtraction("flv/sample.flv");
}
// PS.
@@ -389,38 +388,37 @@
// TODO: Enable once the timeout is fixed.
@Test
@Ignore
- public void testPsElphantsDream() throws IOException, InterruptedException {
- testExtractAsset("ts/elephants_dream.mpg");
+ public void testPsElphantsDream() throws IOException {
+ testAssetExtraction("ts/elephants_dream.mpg");
}
@Test
- public void testPsWithAc3() throws IOException, InterruptedException {
- testExtractAsset("ts/sample_ac3.ps");
+ public void testPsWithAc3() throws IOException {
+ testAssetExtraction("ts/sample_ac3.ps");
}
@Test
- public void testPsWithH262MpegAudio() throws IOException, InterruptedException {
- testExtractAsset("ts/sample_h262_mpeg_audio.ps");
+ public void testPsWithH262MpegAudio() throws IOException {
+ testAssetExtraction("ts/sample_h262_mpeg_audio.ps");
}
// ADTS.
@Test
- public void testAdtsTruncatedWithConstantBitrateSeeking()
- throws IOException, InterruptedException {
- testExtractAsset(
+ public void testAdtsTruncatedWithConstantBitrateSeeking() throws IOException {
+ testAssetExtraction(
"ts/sample_cbs_truncated.adts",
Collections.singletonMap(MediaParser.PARAMETER_ADTS_ENABLE_CBR_SEEKING, true));
}
@Test
- public void testAdts() throws IOException, InterruptedException {
- testExtractAsset("ts/sample.adts");
+ public void testAdts() throws IOException {
+ testAssetExtraction("ts/sample.adts");
}
@Test
- public void testAdtsWithConstantBitrateSeeking() throws IOException, InterruptedException {
- testExtractAsset(
+ public void testAdtsWithConstantBitrateSeeking() throws IOException {
+ testAssetExtraction(
"ts/sample_cbs.adts",
Collections.singletonMap(MediaParser.PARAMETER_ADTS_ENABLE_CBR_SEEKING, true));
}
@@ -428,150 +426,195 @@
// AC-3.
@Test
- public void testAc3() throws IOException, InterruptedException {
- testExtractAsset("ts/sample.ac3");
+ public void testAc3() throws IOException {
+ testAssetExtraction("ts/sample.ac3");
}
// AC-4.
@Test
- public void testAc4() throws IOException, InterruptedException {
- testExtractAsset("ts/sample.ac4");
+ public void testAc4() throws IOException {
+ testAssetExtraction("ts/sample.ac4");
}
// EAC-3.
@Test
- public void testEac3() throws IOException, InterruptedException {
- testExtractAsset("ts/sample.eac3");
+ public void testEac3() throws IOException {
+ testAssetExtraction("ts/sample.eac3");
}
// TS.
@Test
- public void testTsBigBuckBunny() throws IOException, InterruptedException {
- testExtractAsset("ts/bbb_2500ms.ts");
+ public void testTsBigBuckBunny() throws IOException {
+ testAssetExtraction("ts/bbb_2500ms.ts");
}
@Test
- public void testTsWithH262MpegAudio() throws IOException, InterruptedException {
- testExtractAsset("ts/sample_h262_mpeg_audio.ts");
+ public void testTsWithH262MpegAudio() throws IOException {
+ testAssetExtraction("ts/sample_h262_mpeg_audio.ts");
}
@Test
- public void testTsWithH264MpegAudio() throws IOException, InterruptedException {
- testExtractAsset("ts/sample_h264_mpeg_audio.ts");
+ public void testTsWithH264MpegAudio() throws IOException {
+ testAssetExtraction("ts/sample_h264_mpeg_audio.ts");
}
@Test
- public void testTsWithH264DetectAccessUnits() throws IOException, InterruptedException {
- testExtractAsset(
+ public void testTsWithH264DetectAccessUnits() throws IOException {
+ testAssetExtraction(
"ts/sample_h264_no_access_unit_delimiters.ts",
Collections.singletonMap(MediaParser.PARAMETER_TS_DETECT_ACCESS_UNITS, true));
}
@Test
- public void testTsWithH264DtsAudio() throws IOException, InterruptedException {
- testExtractAsset(
+ public void testTsWithH264DtsAudio() throws IOException {
+ testAssetExtraction(
"ts/sample_h264_dts_audio.ts",
Collections.singletonMap(
MediaParser.PARAMETER_TS_ENABLE_HDMV_DTS_AUDIO_STREAMS, true));
}
@Test
- public void testTsWithLatm() throws IOException, InterruptedException {
- testExtractAsset("ts/sample_latm.ts");
+ public void testTsWithLatm() throws IOException {
+ testAssetExtraction("ts/sample_latm.ts");
}
@Test
- public void testTsWithSdt() throws IOException, InterruptedException {
- testExtractAsset("ts/sample_with_sdt.ts");
+ public void testTsWithSdt() throws IOException {
+ testAssetExtraction("ts/sample_with_sdt.ts");
}
@Test
- public void testTsWithH265() throws IOException, InterruptedException {
- testExtractAsset("ts/sample_h265.ts");
+ public void testTsWithH265() throws IOException {
+ testAssetExtraction("ts/sample_h265.ts");
}
// MKV.
@Test
- public void testMatroskaSubsampleEncryptedNoAltref() throws IOException, InterruptedException {
- testExtractAsset("mkv/subsample_encrypted_noaltref.webm");
+ public void testMatroskaSubsampleEncryptedNoAltref() throws IOException {
+ testAssetExtraction("mkv/subsample_encrypted_noaltref.webm");
}
@Test
- public void testMatroskaFile() throws IOException, InterruptedException {
- testExtractAsset("mkv/sample.mkv");
+ public void testMatroskaFile() throws IOException {
+ testAssetExtraction("mkv/sample.mkv");
}
@Test
- public void testMatroskaFullBlocks() throws IOException, InterruptedException {
- testExtractAsset("mkv/full_blocks.mkv");
+ public void testMatroskaFullBlocks() throws IOException {
+ testAssetExtraction("mkv/full_blocks.mkv");
}
@Test
- public void testMatroskaSubsampleEncryptedAltref() throws IOException, InterruptedException {
- testExtractAsset("mkv/subsample_encrypted_altref.webm");
+ public void testMatroskaSubsampleEncryptedAltref() throws IOException {
+ testAssetExtraction("mkv/subsample_encrypted_altref.webm");
+ }
+
+ @Test
+ public void testMatroskaOutOfBandCrypto() throws IOException {
+ MockMediaParserOutputConsumer outputConsumer = new MockMediaParserOutputConsumer();
+ MockMediaParserInputReader inputReader =
+ getInputReader("mkv/subsample_encrypted_noaltref.webm");
+ MediaParser mediaParser = MediaParser.create(outputConsumer);
+ // Initialization vectors are 16 bytes in size, as per CryptoInfo documentation.
+ MediaCodec.CryptoInfo expectedCryptoInfo = new MediaCodec.CryptoInfo();
+ expectedCryptoInfo.set(
+ /* newNumSubsamples=*/ 1,
+ /* newNumBytesOfClearData= */ new int[] {2},
+ /* newNumBytesOfEncryptedData= */ new int[] {5},
+ /* newKey= */ Arrays.copyOf(
+ Util.getBytesFromHexString("01020304050607080910111213141516"), 16),
+ /* newIv= */ Arrays.copyOf(Util.getBytesFromHexString("0101010101010101"), 16),
+ MediaCodec.CRYPTO_MODE_AES_CTR);
+ advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 1);
+ assertEqual(outputConsumer.getLastOutputCryptoInfo(), expectedCryptoInfo);
+ mediaParser.release();
}
// MP4.
@Test
- public void testMp4Ac4Fragmented() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_ac4_fragmented.mp4");
+ public void testMp4Ac4Fragmented() throws IOException {
+ testAssetExtraction("mp4/sample_ac4_fragmented.mp4");
}
@Test
- public void testMp4AndrdoidSlowMotion() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_android_slow_motion.mp4");
+ public void testMp4AndrdoidSlowMotion() throws IOException {
+ testAssetExtraction("mp4/sample_android_slow_motion.mp4");
}
@Test
- public void testMp4FragmentedSei() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_fragmented_sei.mp4");
+ public void testMp4FragmentedSei() throws IOException {
+ testAssetExtraction("mp4/sample_fragmented_sei.mp4");
}
@Test
- public void testMp4WithAc4() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_ac4.mp4");
+ public void testMp4WithAc4() throws IOException {
+ testAssetExtraction("mp4/sample_ac4.mp4");
}
@Test
- public void testMp4FragmentedSeekable() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_fragmented_seekable.mp4");
+ public void testMp4FragmentedSeekable() throws IOException {
+ testAssetExtraction("mp4/sample_fragmented_seekable.mp4");
}
@Test
- public void testMp4WithProtectedAc4() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_ac4_protected.mp4");
+ public void testMp4WithProtectedAc4() throws IOException {
+ testAssetExtraction("mp4/sample_ac4_protected.mp4");
}
@Test
- public void testMp4() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample.mp4");
+ public void testMp4() throws IOException {
+ testAssetExtraction("mp4/sample.mp4");
}
@Test
- public void testMp4MdatTooLong() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_mdat_too_long.mp4");
+ public void testMp4MdatTooLong() throws IOException {
+ testAssetExtraction("mp4/sample_mdat_too_long.mp4");
}
@Test
- public void testMp4Fragmented() throws IOException, InterruptedException {
- testExtractAsset("mp4/sample_fragmented.mp4");
+ public void testMp4Fragmented() throws IOException {
+ testAssetExtraction("mp4/sample_fragmented.mp4");
+ }
+
+ @Test
+ public void testMp4FragmentedOutOfBandCrypto() throws IOException {
+ MockMediaParserOutputConsumer outputConsumer = new MockMediaParserOutputConsumer();
+ MockMediaParserInputReader inputReader = getInputReader("mp4/sample_ac4_protected.mp4");
+ MediaParser mediaParser = MediaParser.create(outputConsumer);
+ // Initialization vectors are 16 bytes in size, as per CryptoInfo documentation.
+ MediaCodec.CryptoInfo expectedCryptoInfo = new MediaCodec.CryptoInfo();
+ expectedCryptoInfo.set(
+ /* newNumSubsamples=*/ 1,
+ /* newNumBytesOfClearData= */ new int[] {7},
+ /* newNumBytesOfEncryptedData= */ new int[] {360},
+ /* newKey= */ Arrays.copyOf(
+ Util.getBytesFromHexString("91341951696b5e1ba232439ecec1f12a"), 16),
+ /* newIv= */ Arrays.copyOf(Util.getBytesFromHexString("aab4ed0108dd5267"), 16),
+ MediaCodec.CRYPTO_MODE_AES_CTR);
+ advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 1);
+ assertEqual(outputConsumer.getLastOutputCryptoInfo(), expectedCryptoInfo);
+
+ expectedCryptoInfo.iv = Arrays.copyOf(Util.getBytesFromHexString("aab4ed0108dd5272"), 16);
+ expectedCryptoInfo.numBytesOfEncryptedData = new int[] {488};
+ advanceUntilSample(outputConsumer, inputReader, mediaParser, /* sampleNumber= */ 12);
+ assertEqual(outputConsumer.getLastOutputCryptoInfo(), expectedCryptoInfo);
+
+ mediaParser.release();
}
// Internal methods.
private static void testCreationByName(String name) {
- MediaParser.createByName(name, new MockMediaParserOutputConsumer(new FakeExtractorOutput()))
- .release();
+ MediaParser.createByName(name, new MockMediaParserOutputConsumer()).release();
}
private static void assertSupportFor(String parameterName) {
- MediaParser mediaParser =
- MediaParser.create(new MockMediaParserOutputConsumer(new FakeExtractorOutput()));
+ MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
assertThat(mediaParser.supportsParameter(parameterName)).isTrue();
mediaParser.release();
}
@@ -583,8 +626,7 @@
private static void testParameterSetting(
String parameterName, Object value, boolean valueIsIllegal) {
- MediaParser mediaParser =
- MediaParser.create(new MockMediaParserOutputConsumer(new FakeExtractorOutput()));
+ MediaParser mediaParser = MediaParser.create(new MockMediaParserOutputConsumer());
boolean illegalArgument = false;
try {
mediaParser.setParameter(parameterName, value);
@@ -597,37 +639,33 @@
}
private static void testSniffAsset(String assetPath, String expectedParserName)
- throws IOException, InterruptedException {
- extractAsset(assetPath, Collections.emptyMap(), expectedParserName);
+ throws IOException {
+ testAssetExtraction(assetPath, Collections.emptyMap(), expectedParserName);
}
- private static void testExtractAsset(String assetPath)
- throws IOException, InterruptedException {
- testExtractAsset(assetPath, Collections.emptyMap());
+ private static void testAssetExtraction(String assetPath) throws IOException {
+ testAssetExtraction(assetPath, Collections.emptyMap());
}
- private static void testExtractAsset(String assetPath, Map<String, Object> parameters)
- throws IOException, InterruptedException {
- extractAsset(assetPath, parameters, /* expectedParserName= */ null);
+ private static void testAssetExtraction(String assetPath, Map<String, Object> parameters)
+ throws IOException {
+ testAssetExtraction(assetPath, parameters, /* expectedParserName= */ null);
}
- private static void extractAsset(
+ private static void testAssetExtraction(
String assetPath, Map<String, Object> parameters, String expectedParserName)
- throws IOException, InterruptedException {
- byte[] assetBytes =
- TestUtil.getByteArray(
- InstrumentationRegistry.getInstrumentation().getContext(), assetPath);
- MockMediaParserInputReader mockInput =
- new MockMediaParserInputReader(
- new FakeExtractorInput.Builder().setData(assetBytes).build());
+ throws IOException {
+ MockMediaParserInputReader inputReader = getInputReader(assetPath);
+ boolean usingInBandCryptoInfo =
+ (boolean) parameters.getOrDefault(PARAMETER_IN_BAND_CRYPTO_INFO, false);
MockMediaParserOutputConsumer outputConsumer =
- new MockMediaParserOutputConsumer(new FakeExtractorOutput());
+ new MockMediaParserOutputConsumer(usingInBandCryptoInfo);
MediaParser mediaParser = MediaParser.create(outputConsumer);
for (Map.Entry<String, Object> entry : parameters.entrySet()) {
mediaParser.setParameter(entry.getKey(), entry.getValue());
}
- mediaParser.advance(mockInput);
+ mediaParser.advance(inputReader);
if (expectedParserName != null) {
assertThat(expectedParserName).isEqualTo(mediaParser.getParserName());
// We are only checking that the extractor is the right one.
@@ -635,7 +673,7 @@
return;
}
- while (mediaParser.advance(mockInput)) {
+ while (mediaParser.advance(inputReader)) {
// Do nothing.
}
@@ -651,10 +689,10 @@
? 0
: (durationUs * j) / 3;
MediaParser.SeekPoint seekPoint = seekMap.getSeekPoints(timeUs).first;
- mockInput.reset();
- mockInput.setPosition((int) seekPoint.position);
+ inputReader.reset();
+ inputReader.setPosition((int) seekPoint.position);
mediaParser.seek(seekPoint);
- while (mediaParser.advance(mockInput)) {
+ while (mediaParser.advance(inputReader)) {
// Do nothing.
}
if (durationUs == MediaParser.SeekMap.UNKNOWN_DURATION) {
@@ -665,6 +703,39 @@
mediaParser.release();
}
+ private static MockMediaParserInputReader getInputReader(String assetPath) throws IOException {
+ byte[] assetBytes =
+ TestUtil.getByteArray(
+ InstrumentationRegistry.getInstrumentation().getContext(), assetPath);
+ return new MockMediaParserInputReader(
+ new FakeExtractorInput.Builder().setData(assetBytes).build());
+ }
+
+ private static void advanceUntilSample(
+ MockMediaParserOutputConsumer outputConsumer,
+ MockMediaParserInputReader inputReader,
+ MediaParser mediaParser,
+ int sampleNumber)
+ throws IOException {
+ while (outputConsumer.getCompletedSampleCount() != sampleNumber) {
+ assertThat(mediaParser.advance(inputReader)).isTrue();
+ }
+ }
+
+ private static void assertEqual(
+ MediaCodec.CryptoInfo actualCryptoInfo, MediaCodec.CryptoInfo expectedCryptoInfo) {
+ assertThat(actualCryptoInfo.mode).isEqualTo(expectedCryptoInfo.mode);
+ assertThat(actualCryptoInfo.key).isEqualTo(expectedCryptoInfo.key);
+ assertThat(actualCryptoInfo.iv).isEqualTo(expectedCryptoInfo.iv);
+ assertThat(actualCryptoInfo.numSubSamples).isEqualTo(expectedCryptoInfo.numSubSamples);
+ for (int i = 0; i < actualCryptoInfo.numSubSamples; i++) {
+ assertThat(actualCryptoInfo.numBytesOfClearData[i])
+ .isEqualTo(expectedCryptoInfo.numBytesOfClearData[i]);
+ assertThat(actualCryptoInfo.numBytesOfEncryptedData[i])
+ .isEqualTo(expectedCryptoInfo.numBytesOfEncryptedData[i]);
+ }
+ }
+
// Internal classes.
private static FluentMediaParserSubject assertParsers(String... names) {
diff --git a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MockMediaParserOutputConsumer.java b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MockMediaParserOutputConsumer.java
index b87d825..ff7c677 100644
--- a/tests/tests/mediaparser/src/android/media/mediaparser/cts/MockMediaParserOutputConsumer.java
+++ b/tests/tests/mediaparser/src/android/media/mediaparser/cts/MockMediaParserOutputConsumer.java
@@ -38,16 +38,33 @@
public class MockMediaParserOutputConsumer implements MediaParser.OutputConsumer {
+ private final boolean mUsingInBandCryptoInfo;
private final FakeExtractorOutput mFakeExtractorOutput;
private final ArrayList<TrackOutput> mTrackOutputs;
- private MediaParser.SeekMap mSeekMap;
+ @Nullable private MediaParser.SeekMap mSeekMap;
+ private int mCompletedSampleCount;
+ @Nullable private MediaCodec.CryptoInfo mLastOutputCryptoInfo;
- public MockMediaParserOutputConsumer(FakeExtractorOutput fakeExtractorOutput) {
- mFakeExtractorOutput = fakeExtractorOutput;
+ public MockMediaParserOutputConsumer() {
+ this(/* usingInBandCryptoInfo= */ false);
+ }
+
+ public MockMediaParserOutputConsumer(boolean usingInBandCryptoInfo) {
+ mUsingInBandCryptoInfo = usingInBandCryptoInfo;
+ mFakeExtractorOutput = new FakeExtractorOutput();
mTrackOutputs = new ArrayList<>();
}
+ public int getCompletedSampleCount() {
+ return mCompletedSampleCount;
+ }
+
+ @Nullable
+ public MediaCodec.CryptoInfo getLastOutputCryptoInfo() {
+ return mLastOutputCryptoInfo;
+ }
+
public void clearTrackOutputs() {
mFakeExtractorOutput.clearTrackOutputs();
}
@@ -105,7 +122,12 @@
int flags,
int size,
int offset,
- MediaCodec.CryptoInfo cryptoData) {}
+ @Nullable MediaCodec.CryptoInfo cryptoInfo) {
+ mCompletedSampleCount++;
+ if (!mUsingInBandCryptoInfo) {
+ mLastOutputCryptoInfo = cryptoInfo;
+ }
+ }
// Internal methods.
diff --git a/tests/tests/multiuser/AndroidManifest.xml b/tests/tests/multiuser/AndroidManifest.xml
index fc6f3d9..17a71d7 100644
--- a/tests/tests/multiuser/AndroidManifest.xml
+++ b/tests/tests/multiuser/AndroidManifest.xml
@@ -19,6 +19,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.multiuser.cts"
android:targetSandboxVersion="2">
+ <!-- Must be at least 30, otherwise some tests could be invalid. -->
+ <uses-sdk android:targetSdkVersion="30"/>
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java b/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
new file mode 100644
index 0000000..75aa112
--- /dev/null
+++ b/tests/tests/multiuser/src/android/multiuser/cts/UserManagerTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.multiuser.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.content.Context;
+import android.os.UserManager;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UserManagerTest {
+
+ /**
+ * Verify that the isUserAGoat() method always returns false for API level 30. This is
+ * because apps targeting R no longer have access to package queries by default.
+ */
+ @Test
+ public void testUserGoat_api30() {
+ final Context context = getInstrumentation().getContext();
+ assertFalse("isUserAGoat() should return false",
+ context.getSystemService(UserManager.class).isUserAGoat());
+ }
+}
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
index c6f7184..4ff944f 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferGLTest.cpp
@@ -1864,6 +1864,10 @@
desc.width = 100;
desc.height = 100;
desc.usage = AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;
+ if (FormatIsYuv(desc.format)) {
+ // YUV formats are only supported for textures, so add texture usage.
+ desc.usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ }
// This test does not make sense for layered buffers - don't bother testing them.
if (desc.layers > 1) return;
if (!SetUpBuffer(desc)) return;
@@ -1905,6 +1909,10 @@
desc.width = 16;
desc.height = 16;
desc.usage = AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT | AHARDWAREBUFFER_USAGE_CPU_READ_RARELY;
+ if (FormatIsYuv(desc.format)) {
+ // YUV formats are only supported for textures, so add texture usage.
+ desc.usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;
+ }
// This test does not make sense for GL formats. Layered buffers do not support CPU access.
if ((desc.stride & kGlFormat) || desc.layers > 1) {
ALOGI("Test skipped: Test is for single-layer HardwareBuffer formats only.");
@@ -2634,4 +2642,4 @@
AHardwareBuffer_Desc{17, 23, 7, AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT, 0, 0, 0, 0}),
&GetTestName);
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/tests/tests/net/Android.bp b/tests/tests/net/Android.bp
index 46fae33..93a6d91 100644
--- a/tests/tests/net/Android.bp
+++ b/tests/tests/net/Android.bp
@@ -77,6 +77,7 @@
android_test {
name: "CtsNetTestCasesLatestSdk",
defaults: ["CtsNetTestCasesDefaults"],
+ jni_uses_sdk_apis: true,
min_sdk_version: "29",
target_sdk_version: "29",
test_suites: [
diff --git a/tests/tests/net/src/android/net/cts/NetworkAgentTest.kt b/tests/tests/net/src/android/net/cts/NetworkAgentTest.kt
index 03b961b..2824db7 100644
--- a/tests/tests/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/tests/net/src/android/net/cts/NetworkAgentTest.kt
@@ -592,4 +592,50 @@
assertNull(it.uri)
}
}
+
+ @Test
+ fun testTemporarilyUnmeteredCapability() {
+ // This test will create a networks with/without NET_CAPABILITY_TEMPORARILY_NOT_METERED
+ // and check that the callback reflects the capability changes.
+ // First create a request to make sure the network is kept up
+ val request1 = NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .build()
+ val callback1 = TestableNetworkCallback(DEFAULT_TIMEOUT_MS).also {
+ registerNetworkCallback(request1, it)
+ }
+ requestNetwork(request1, callback1)
+
+ // Then file the interesting request
+ val request = NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .build()
+ val callback = TestableNetworkCallback()
+ requestNetwork(request, callback)
+
+ // Connect the network
+ createConnectedNetworkAgent().let { (agent, _) ->
+ callback.expectAvailableThenValidatedCallbacks(agent.network)
+
+ // Send TEMP_NOT_METERED and check that the callback is called appropriately.
+ val nc1 = NetworkCapabilities(agent.nc)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ agent.sendNetworkCapabilities(nc1)
+ callback.expectCapabilitiesThat(agent.network) {
+ it.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ }
+
+ // Remove TEMP_NOT_METERED and check that the callback is called appropriately.
+ val nc2 = NetworkCapabilities(agent.nc)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ agent.sendNetworkCapabilities(nc2)
+ callback.expectCapabilitiesThat(agent.network) {
+ !it.hasCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ }
+ }
+
+ // tearDown() will unregister the requests and agents
+ }
}
diff --git a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
index 5e92b41..e8af1b3 100644
--- a/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/tests/net/src/android/net/cts/NetworkRequestTest.java
@@ -18,6 +18,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -33,11 +34,12 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
-import android.net.TelephonyNetworkSpecifier;
+import android.net.UidRange;
import android.net.wifi.WifiNetworkSpecifier;
import android.os.Build;
-import android.os.Process;
import android.os.PatternMatcher;
+import android.os.Process;
+import android.util.ArraySet;
import androidx.test.runner.AndroidJUnit4;
@@ -59,6 +61,20 @@
private static final String TEST_PACKAGE_NAME = "test.package.name";
private static final MacAddress ARBITRARY_ADDRESS = MacAddress.fromString("3:5:8:12:9:2");
+ private class LocalNetworkSpecifier extends NetworkSpecifier {
+ private final int mId;
+
+ LocalNetworkSpecifier(int id) {
+ mId = id;
+ }
+
+ @Override
+ public boolean canBeSatisfiedBy(NetworkSpecifier other) {
+ return other instanceof LocalNetworkSpecifier
+ && mId == ((LocalNetworkSpecifier) other).mId;
+ }
+ }
+
@Test
public void testCapabilities() {
assertTrue(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build()
@@ -71,6 +87,16 @@
verifyNoCapabilities(nr);
}
+ @Test
+ public void testTemporarilyNotMeteredCapability() {
+ assertTrue(new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+ .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ assertFalse(new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED).build()
+ .hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED));
+ }
+
private void verifyNoCapabilities(NetworkRequest nr) {
// NetworkCapabilities.mNetworkCapabilities is defined as type long
final int MAX_POSSIBLE_CAPABILITY = Long.SIZE;
@@ -129,54 +155,108 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.Q)
public void testCanBeSatisfiedBy() {
- final TelephonyNetworkSpecifier specifier1 = new TelephonyNetworkSpecifier.Builder()
- .setSubscriptionId(1234 /* subId */)
- .build();
- final TelephonyNetworkSpecifier specifier2 = new TelephonyNetworkSpecifier.Builder()
- .setSubscriptionId(5678 /* subId */)
- .build();
- final NetworkCapabilities cap = new NetworkCapabilities()
+ final LocalNetworkSpecifier specifier1 = new LocalNetworkSpecifier(1234 /* id */);
+ final LocalNetworkSpecifier specifier2 = new LocalNetworkSpecifier(5678 /* id */);
+
+ final NetworkCapabilities capCellularMmsInternet = new NetworkCapabilities()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_MMS)
.addCapability(NET_CAPABILITY_INTERNET);
- final NetworkCapabilities capDualTransport = new NetworkCapabilities(cap)
- .addTransportType(TRANSPORT_VPN);
- final NetworkCapabilities capWithSpecifier1 =
- new NetworkCapabilities(cap).setNetworkSpecifier(specifier1);
- final NetworkCapabilities capDiffTransportWithSpecifier1 = new NetworkCapabilities()
+ final NetworkCapabilities capCellularVpnMmsInternet =
+ new NetworkCapabilities(capCellularMmsInternet).addTransportType(TRANSPORT_VPN);
+ final NetworkCapabilities capCellularMmsInternetSpecifier1 =
+ new NetworkCapabilities(capCellularMmsInternet).setNetworkSpecifier(specifier1);
+ final NetworkCapabilities capVpnInternetSpecifier1 = new NetworkCapabilities()
.addCapability(NET_CAPABILITY_INTERNET)
.addTransportType(TRANSPORT_VPN)
.setNetworkSpecifier(specifier1);
+ final NetworkCapabilities capCellularMmsInternetMatchallspecifier =
+ new NetworkCapabilities(capCellularMmsInternet)
+ .setNetworkSpecifier(new MatchAllNetworkSpecifier());
+ final NetworkCapabilities capCellularMmsInternetSpecifier2 =
+ new NetworkCapabilities(capCellularMmsInternet).setNetworkSpecifier(specifier2);
- final NetworkRequest requestWithSpecifier1 = new NetworkRequest.Builder()
+ final NetworkRequest requestCellularInternetSpecifier1 = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.setNetworkSpecifier(specifier1)
.build();
- assertFalse(requestWithSpecifier1.canBeSatisfiedBy(null));
- assertFalse(requestWithSpecifier1.canBeSatisfiedBy(new NetworkCapabilities()));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(new NetworkCapabilities(cap)
- .setNetworkSpecifier(new MatchAllNetworkSpecifier())));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(cap));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(capWithSpecifier1));
- assertTrue(requestWithSpecifier1.canBeSatisfiedBy(capDualTransport));
- assertFalse(requestWithSpecifier1.canBeSatisfiedBy(
- new NetworkCapabilities(cap).setNetworkSpecifier(specifier2)));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(null));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(new NetworkCapabilities()));
+ assertTrue(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+ capCellularMmsInternetMatchallspecifier));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(capCellularMmsInternet));
+ assertTrue(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+ capCellularMmsInternetSpecifier1));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(capCellularVpnMmsInternet));
+ assertFalse(requestCellularInternetSpecifier1.canBeSatisfiedBy(
+ capCellularMmsInternetSpecifier2));
- final NetworkRequest request = new NetworkRequest.Builder()
+ final NetworkRequest requestCellularInternet = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.build();
- assertTrue(request.canBeSatisfiedBy(cap));
- assertTrue(request.canBeSatisfiedBy(capWithSpecifier1));
- assertTrue(request.canBeSatisfiedBy(
- new NetworkCapabilities(cap).setNetworkSpecifier(specifier2)));
- assertFalse(request.canBeSatisfiedBy(capDiffTransportWithSpecifier1));
- assertTrue(request.canBeSatisfiedBy(capDualTransport));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternet));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternetSpecifier1));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularMmsInternetSpecifier2));
+ assertFalse(requestCellularInternet.canBeSatisfiedBy(capVpnInternetSpecifier1));
+ assertTrue(requestCellularInternet.canBeSatisfiedBy(capCellularVpnMmsInternet));
+ }
- assertEquals(requestWithSpecifier1.canBeSatisfiedBy(capWithSpecifier1),
- new NetworkCapabilities(capWithSpecifier1)
- .satisfiedByNetworkCapabilities(capWithSpecifier1));
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testInvariantInCanBeSatisfiedBy() {
+ // Test invariant that result of NetworkRequest.canBeSatisfiedBy() should be the same with
+ // NetworkCapabilities.satisfiedByNetworkCapabilities().
+ final LocalNetworkSpecifier specifier1 = new LocalNetworkSpecifier(1234 /* id */);
+ final int uid = Process.myUid();
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ final NetworkRequest requestCombination = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .setLinkUpstreamBandwidthKbps(1000)
+ .setNetworkSpecifier(specifier1)
+ .setSignalStrength(-123)
+ .setUids(ranges).build();
+ final NetworkCapabilities capCell = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ assertCorrectlySatisfies(false, requestCombination, capCell);
+
+ final NetworkCapabilities capCellInternet = new NetworkCapabilities.Builder(capCell)
+ .addCapability(NET_CAPABILITY_INTERNET).build();
+ assertCorrectlySatisfies(false, requestCombination, capCellInternet);
+
+ final NetworkCapabilities capCellInternetBW =
+ new NetworkCapabilities.Builder(capCellInternet)
+ .setLinkUpstreamBandwidthKbps(1024).build();
+ assertCorrectlySatisfies(false, requestCombination, capCellInternetBW);
+
+ final NetworkCapabilities capCellInternetBWSpecifier1 =
+ new NetworkCapabilities.Builder(capCellInternetBW)
+ .setNetworkSpecifier(specifier1).build();
+ assertCorrectlySatisfies(false, requestCombination, capCellInternetBWSpecifier1);
+
+ final NetworkCapabilities capCellInternetBWSpecifier1Signal =
+ new NetworkCapabilities.Builder(capCellInternetBWSpecifier1)
+ .setSignalStrength(-123).build();
+ assertCorrectlySatisfies(true, requestCombination,
+ capCellInternetBWSpecifier1Signal);
+
+ final NetworkCapabilities capCellInternetBWSpecifier1SignalUid =
+ new NetworkCapabilities.Builder(capCellInternetBWSpecifier1Signal)
+ .setOwnerUid(uid)
+ .setAdministratorUids(new int [] {uid}).build();
+ assertCorrectlySatisfies(true, requestCombination,
+ capCellInternetBWSpecifier1SignalUid);
+ }
+
+ private void assertCorrectlySatisfies(boolean expect, NetworkRequest request,
+ NetworkCapabilities nc) {
+ assertEquals(expect, request.canBeSatisfiedBy(nc));
+ assertEquals(
+ request.canBeSatisfiedBy(nc),
+ request.networkCapabilities.satisfiedByNetworkCapabilities(nc));
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
diff --git a/tests/tests/neuralnetworks/AndroidTest.xml b/tests/tests/neuralnetworks/AndroidTest.xml
index b7894e5..6ec4752 100644
--- a/tests/tests/neuralnetworks/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/AndroidTest.xml
@@ -32,4 +32,8 @@
in code coverage evaluations) -->
<option name="native-test-timeout" value="1200000" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.neuralnetworks" />
+ </object>
</configuration>
diff --git a/tests/tests/neuralnetworks/benchmark/AndroidTest.xml b/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
index e4fea4a..e122f67 100644
--- a/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/benchmark/AndroidTest.xml
@@ -27,4 +27,8 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.nn.benchmark.cts" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.neuralnetworks" />
+ </object>
</configuration>
diff --git a/tests/tests/neuralnetworks/tflite_delegate/AndroidTest.xml b/tests/tests/neuralnetworks/tflite_delegate/AndroidTest.xml
index f1d5cf7..1affa18 100644
--- a/tests/tests/neuralnetworks/tflite_delegate/AndroidTest.xml
+++ b/tests/tests/neuralnetworks/tflite_delegate/AndroidTest.xml
@@ -31,4 +31,8 @@
<!-- test-timeout unit is ms, value = 2 min -->
<option name="native-test-timeout" value="120000" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.neuralnetworks" />
+ </object>
</configuration>
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/AndroidTest.xml b/tests/tests/notificationlegacy/notificationlegacy29/AndroidTest.xml
index 9421128..fbfd309 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/AndroidTest.xml
+++ b/tests/tests/notificationlegacy/notificationlegacy29/AndroidTest.xml
@@ -29,4 +29,8 @@
<option name="runtime-hint" value="5m" />
<option name="hidden-api-checks" value="false" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.extservices" />
+ </object>
</configuration>
diff --git a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
index 5b70d19..52fe892 100644
--- a/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
+++ b/tests/tests/notificationlegacy/notificationlegacy29/src/android/app/notification/legacy29/cts/NotificationManager29Test.java
@@ -106,9 +106,9 @@
private StatusBarNotification findPostedNotification(int id) {
// notification is a bit asynchronous so it may take a few ms to appear in
// getActiveNotifications()
- // we will check for it for up to 300ms before giving up
+ // we will check for it for up to 500ms before giving up
StatusBarNotification n = null;
- for (int tries = 3; tries--> 0;) {
+ for (int tries = 5; tries--> 0;) {
final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
for (StatusBarNotification sbn : sbns) {
Log.d(TAG, "Found " + sbn.getKey());
diff --git a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
index 37f90e9..ef2a0c1 100644
--- a/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -113,15 +113,6 @@
String extensions = mActivity.getExtensionsString();
- final String es30RequiredList[] = {
- "OES_EGL_image_external_essl3"
- };
-
- for (int i = 0; i < es30RequiredList.length; ++i) {
- assertTrue("OpenGL ES version 3.0+ is missing extension " + es30RequiredList[i],
- hasExtension(extensions, es30RequiredList[i]));
- }
-
if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) < 1)
return;
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 39d8f1b..d18479d 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -200,6 +200,34 @@
}
}
+ @AppModeFull(reason = "Uses separate apps for testing")
+ fun testAutoRevoke_whitelistingApis() {
+ withDummyApp {
+ val pm = context.packageManager
+ runWithShellPermissionIdentity {
+ assertFalse(pm.isAutoRevokeWhitelisted(APK_PACKAGE_NAME))
+ }
+
+ runWithShellPermissionIdentity {
+ assertTrue(pm.setAutoRevokeWhitelisted(APK_PACKAGE_NAME, true))
+ }
+ eventually {
+ runWithShellPermissionIdentity {
+ assertTrue(pm.isAutoRevokeWhitelisted(APK_PACKAGE_NAME))
+ }
+ }
+
+ runWithShellPermissionIdentity {
+ assertTrue(pm.setAutoRevokeWhitelisted(APK_PACKAGE_NAME, false))
+ }
+ eventually {
+ runWithShellPermissionIdentity {
+ assertFalse(pm.isAutoRevokeWhitelisted(APK_PACKAGE_NAME))
+ }
+ }
+ }
+ }
+
private fun wakeUpScreen() {
runShellCommand("input keyevent KEYCODE_WAKEUP")
runShellCommand("input keyevent 82")
@@ -343,9 +371,9 @@
}
private fun waitForIdle() {
- instrumentation.uiAutomation.waitForIdle(2000, 5000)
+ instrumentation.uiAutomation.waitForIdle(1000, 10000)
Thread.sleep(500)
- instrumentation.uiAutomation.waitForIdle(2000, 5000)
+ instrumentation.uiAutomation.waitForIdle(1000, 10000)
}
private inline fun <T> eventually(crossinline action: () -> T): T {
diff --git a/tests/tests/packagewatchdog/AndroidTest.xml b/tests/tests/packagewatchdog/AndroidTest.xml
index 8a4cbbc..23113be 100644
--- a/tests/tests/packagewatchdog/AndroidTest.xml
+++ b/tests/tests/packagewatchdog/AndroidTest.xml
@@ -27,4 +27,8 @@
<option name="package" value="android.packagewatchdog.cts" />
<option name="runtime-hint" value="1m" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.extservices" />
+ </object>
</configuration>
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
index de9a30b..99fdadb 100755
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -23,6 +23,7 @@
import static android.Manifest.permission.READ_CALL_LOG;
import static android.Manifest.permission.READ_CONTACTS;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
+import static android.Manifest.permission.READ_PHONE_STATE;
import static android.Manifest.permission.WRITE_CALL_LOG;
import static android.Manifest.permission.WRITE_CONTACTS;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
@@ -55,6 +56,10 @@
private static final int NO_TARGET = Build.VERSION_CODES.CUR_DEVELOPMENT + 1;
+ // Redefined here since it's only present in the system API surface.
+ private static final String READ_PRIVILEGED_PHONE_STATE =
+ "android.permission.READ_PRIVILEGED_PHONE_STATE";
+
private List<SplitPermissionInfo> mSplitPermissions;
@Before
@@ -108,10 +113,13 @@
case READ_EXTERNAL_STORAGE:
assertSplit(split, ACCESS_MEDIA_LOCATION, Build.VERSION_CODES.Q);
break;
+ case READ_PRIVILEGED_PHONE_STATE:
+ assertSplit(split, READ_PHONE_STATE, NO_TARGET);
+ break;
}
}
- assertEquals(7, seenSplits.size());
+ assertEquals(8, seenSplits.size());
}
private void assertSplit(SplitPermissionInfo split, String permission, int targetSdk) {
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index abc63a5..dcc3aef 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -3778,11 +3778,6 @@
<permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
- <!-- Allows an application to manage the companion devices.
- @hide -->
- <permission android:name="android.permission.MANAGE_COMPANION_DEVICES"
- android:protectionLevel="signature" />
-
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index 290bcf8..d88c9a9 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -270,6 +270,11 @@
// Wait for write of the first page.
waitForWriteAdapterCallback(1);
+ runOnMainThread(() -> {
+ addPrinter(FIRST_PRINTER_LOCAL_ID, false);
+ addPrinter(SECOND_PRINTER_LOCAL_ID, false);
+ });
+
runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
@@ -378,6 +383,11 @@
// Wait for write of the first page.
waitForWriteAdapterCallback(1);
+ runOnMainThread(() -> {
+ addPrinter(FIRST_PRINTER_LOCAL_ID, false);
+ addPrinter(SECOND_PRINTER_LOCAL_ID, false);
+ });
+
runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
@@ -468,6 +478,11 @@
// Wait for write of the first page.
waitForWriteAdapterCallback(1);
+ runOnMainThread(() -> {
+ addPrinter(FIRST_PRINTER_LOCAL_ID, false);
+ addPrinter(SECOND_PRINTER_LOCAL_ID, false);
+ });
+
runOnMainThread(() -> assertFalse(sSession.isDestroyed()));
runOnMainThread(() -> assertEquals(0, sSession.getTrackedPrinters().size()));
@@ -669,9 +684,6 @@
assertTrue(sSession.isPrinterDiscoveryStarted());
- addPrinter(FIRST_PRINTER_LOCAL_ID, false);
- addPrinter(SECOND_PRINTER_LOCAL_ID, false);
-
return null;
}, invocation -> {
assertFalse(sSession.isPrinterDiscoveryStarted());
diff --git a/tests/tests/provider/res/raw/testvideo2.mp4 b/tests/tests/provider/res/raw/testvideo2.mp4
new file mode 100644
index 0000000..1be8bee
--- /dev/null
+++ b/tests/tests/provider/res/raw/testvideo2.mp4
Binary files differ
diff --git a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
index 017ef83..feeb87c 100644
--- a/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/ProviderTestUtils.java
@@ -17,12 +17,19 @@
package android.provider.cts;
import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.UiAutomation;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
@@ -329,6 +336,44 @@
}
}
+ /**
+ * Extract the average overall color of the given bitmap.
+ * <p>
+ * Internally takes advantage of gaussian blurring that is naturally applied
+ * when downscaling an image.
+ */
+ public static int extractAverageColor(Bitmap bitmap) {
+ final Bitmap res = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(res);
+ final Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ final Rect dst = new Rect(0, 0, 1, 1);
+ canvas.drawBitmap(bitmap, src, dst, null);
+ return res.getPixel(0, 0);
+ }
+
+ public static void assertColorMostlyEquals(int expected, int actual) {
+ assertTrue("Expected " + Integer.toHexString(expected) + " but was "
+ + Integer.toHexString(actual), isColorMostlyEquals(expected, actual));
+ }
+
+ public static void assertColorMostlyNotEquals(int expected, int actual) {
+ assertFalse("Expected " + Integer.toHexString(expected) + " but was "
+ + Integer.toHexString(actual), isColorMostlyEquals(expected, actual));
+ }
+
+ private static boolean isColorMostlyEquals(int expected, int actual) {
+ final float[] expectedHSV = new float[3];
+ final float[] actualHSV = new float[3];
+ Color.colorToHSV(expected, expectedHSV);
+ Color.colorToHSV(actual, actualHSV);
+
+ // Fail if more than a 10% difference in any component
+ if (Math.abs(expectedHSV[0] - actualHSV[0]) > 36) return false;
+ if (Math.abs(expectedHSV[1] - actualHSV[1]) > 0.1f) return false;
+ if (Math.abs(expectedHSV[2] - actualHSV[2]) > 0.1f) return false;
+ return true;
+ }
+
public static void assertExists(String path) throws IOException {
assertExists(null, path);
}
diff --git a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
index 357e5e8..8a4619e 100644
--- a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
+++ b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java
@@ -128,6 +128,7 @@
@Test
public void mediaOutputPanel_withPackageNameExtra_correctPackage() {
+ assumeTrue(mHasTouchScreen);
launchMediaOutputPanel(TEST_PACKAGE_NAME);
String currentPackage = mDevice.getCurrentPackageName();
@@ -137,6 +138,7 @@
@Test
public void mediaOutputPanel_noPutPackageNameExtra_correctPackage() {
+ assumeTrue(mHasTouchScreen);
launchMediaOutputPanel(null /* packageName */);
String currentPackage = mDevice.getCurrentPackageName();
@@ -155,6 +157,7 @@
@Test
public void mediaOutputPanel_correctTitle() {
+ assumeTrue(mHasTouchScreen);
launchMediaOutputPanel(TEST_PACKAGE_NAME);
final UiObject2 titleView = mDevice.findObject(By.res(mSettingsPackage, RESOURCE_HEADER));
@@ -223,6 +226,7 @@
@Test
public void mediaOutputPanel_doneClosesPanel() {
+ assumeTrue(mHasTouchScreen);
// Launch panel
launchMediaOutputPanel(TEST_PACKAGE_NAME);
String currentPackage = mDevice.getCurrentPackageName();
@@ -312,6 +316,7 @@
@Test
public void mediaOutputPanel_seeMoreButton_doNothing() {
+ assumeTrue(mHasTouchScreen);
// Launch panel
launchMediaOutputPanel(TEST_PACKAGE_NAME);
String currentPackage = mDevice.getCurrentPackageName();
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
index b45d919..0d0ec25 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Audio_PlaylistsTest.java
@@ -118,6 +118,7 @@
values.put(Playlists.NAME, name1);
final Uri playlist = mContentResolver
.insert(MediaStore.Audio.Playlists.getContentUri(mVolumeName), values);
+ MediaStore.waitForIdle(mContentResolver);
try (Cursor c = mContentResolver.query(playlist,
new String[] { Playlists.NAME, MediaColumns.DISPLAY_NAME }, null, null)) {
assertTrue(c.moveToFirst());
@@ -128,6 +129,7 @@
values.clear();
values.put(Playlists.NAME, name2);
mContentResolver.update(playlist, values, null);
+ MediaStore.waitForIdle(mContentResolver);
try (Cursor c = mContentResolver.query(playlist,
new String[] { Playlists.NAME, MediaColumns.DISPLAY_NAME }, null, null)) {
assertTrue(c.moveToFirst());
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
index 0435a45..df47fac 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java
@@ -76,9 +76,6 @@
public class MediaStore_Images_MediaTest {
private static final String MIME_TYPE_JPEG = "image/jpeg";
- private static final File EXTERNAL_STORAGE_DIR = Environment.getExternalStorageDirectory();
- private static final File DCIM_DIR = new File(EXTERNAL_STORAGE_DIR, Environment.DIRECTORY_DCIM);
-
private Context mContext;
private ContentResolver mContentResolver;
@@ -303,16 +300,21 @@
*/
@Test
public void testUpdateAndReplace() throws Exception {
+ Assume.assumeFalse(mVolumeName.equals(MediaStore.VOLUME_EXTERNAL));
+
+ File dir = mContext.getSystemService(StorageManager.class)
+ .getStorageVolume(mExternalImages).getDirectory();
+ File dcimDir = new File(dir, Environment.DIRECTORY_DCIM);
File file = null;
try {
// Create file
- file = copyResourceToFile(R.raw.scenery, DCIM_DIR, "cts" + System.nanoTime() + ".jpg");
+ file = copyResourceToFile(R.raw.scenery, dcimDir, "cts" + System.nanoTime() + ".jpg");
final String externalPath = file.getAbsolutePath();
assertNotNull(MediaStore.scanFile(mContentResolver, file));
// Insert another file, insertedFile doesn't exist in lower file system.
ContentValues values = new ContentValues();
- final File insertedFile = new File(DCIM_DIR, "cts" + System.nanoTime() + ".jpg");
+ final File insertedFile = new File(dcimDir, "cts" + System.nanoTime() + ".jpg");
values.put(Media.DATA, insertedFile.getAbsolutePath());
final Uri uri = mContentResolver.insert(mExternalImages, values);
@@ -337,10 +339,15 @@
@Test
public void testUpsert() throws Exception {
+ Assume.assumeFalse(mVolumeName.equals(MediaStore.VOLUME_EXTERNAL));
+
+ File dir = mContext.getSystemService(StorageManager.class)
+ .getStorageVolume(mExternalImages).getDirectory();
+ File dcimDir = new File(dir, Environment.DIRECTORY_DCIM);
File file = null;
try {
// Create file
- file = copyResourceToFile(R.raw.scenery, DCIM_DIR, "cts" + System.nanoTime() + ".jpg");
+ file = copyResourceToFile(R.raw.scenery, dcimDir, "cts" + System.nanoTime() + ".jpg");
final String externalPath = file.getAbsolutePath();
// Verify a record exists in MediaProvider.
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
index 874d82c..479c3ab 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_ThumbnailsTest.java
@@ -16,8 +16,10 @@
package android.provider.cts.media;
+import static android.provider.cts.ProviderTestUtils.assertColorMostlyEquals;
import static android.provider.cts.ProviderTestUtils.assertExists;
import static android.provider.cts.ProviderTestUtils.assertNotExists;
+import static android.provider.cts.ProviderTestUtils.extractAverageColor;
import static android.provider.cts.media.MediaStoreTest.TAG;
import static org.junit.Assert.assertEquals;
@@ -445,7 +447,7 @@
assertTrue(thumb.getHeight() < full.getHeight());
// Thumbnail should match contents
- assertColorMostlyEquals(Color.RED, thumb.getPixel(16, 16));
+ assertColorMostlyEquals(Color.RED, extractAverageColor(thumb));
}
// Verify legacy APIs still work
@@ -463,7 +465,7 @@
assertTrue(thumb.getHeight() < full.getHeight());
// Thumbnail should match contents
- assertColorMostlyEquals(Color.RED, thumb.getPixel(16, 16));
+ assertColorMostlyEquals(Color.RED, extractAverageColor(thumb));
}
}
@@ -479,7 +481,7 @@
// Thumbnail should match updated contents
ProviderTestUtils.waitForIdle();
final Bitmap thumb = mContentResolver.loadThumbnail(finalUri, new Size(32, 32), null);
- assertColorMostlyEquals(Color.BLUE, thumb.getPixel(16, 16));
+ assertColorMostlyEquals(Color.BLUE, extractAverageColor(thumb));
}
// Delete image contents
@@ -501,12 +503,4 @@
bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
}
- /**
- * Since thumbnails might be bounced through a compression pass, we're okay
- * if they're mostly equal.
- */
- private static void assertColorMostlyEquals(int expected, int actual) {
- assertEquals(Integer.toHexString(expected & 0xF0F0F0F0),
- Integer.toHexString(actual & 0xF0F0F0F0));
- }
}
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
index 4921586..dca7382 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_MediaTest.java
@@ -142,10 +142,6 @@
values.put(Media.MIME_TYPE, "video/3gpp");
values.put(Media.SIZE, numBytes);
values.put(Media.TITLE, "testvideo");
- long dateAdded = System.currentTimeMillis() / 1000;
- values.put(Media.DATE_ADDED, dateAdded);
- long dateModified = videoFile.lastModified() / 1000;
- values.put(Media.DATE_MODIFIED, dateModified);
// insert
Uri uri = mContentResolver.insert(mExternalVideo, values);
@@ -175,9 +171,6 @@
assertEquals("video/3gpp", c.getString(c.getColumnIndex(Media.MIME_TYPE)));
assertEquals("testvideo", c.getString(c.getColumnIndex(Media.TITLE)));
assertEquals(numBytes, c.getInt(c.getColumnIndex(Media.SIZE)));
- long realDateAdded = c.getLong(c.getColumnIndex(Media.DATE_ADDED));
- assertTrue(realDateAdded >= dateAdded);
- assertEquals(dateModified, c.getLong(c.getColumnIndex(Media.DATE_MODIFIED)));
assertTrue(c.isNull(c.getColumnIndex(Media.COLOR_STANDARD)));
assertTrue(c.isNull(c.getColumnIndex(Media.COLOR_TRANSFER)));
assertTrue(c.isNull(c.getColumnIndex(Media.COLOR_RANGE)));
diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
index 31b2e7e..a2859b7 100644
--- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Video_ThumbnailsTest.java
@@ -18,10 +18,11 @@
import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT;
import static android.media.MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH;
+import static android.provider.cts.ProviderTestUtils.assertColorMostlyNotEquals;
+import static android.provider.cts.ProviderTestUtils.extractAverageColor;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -228,7 +229,7 @@
final Bitmap beforeThumb = mResolver.loadThumbnail(finalUri, new Size(64, 64), null);
assertTrue(beforeThumb.getWidth() < full.getWidth());
assertTrue(beforeThumb.getHeight() < full.getHeight());
- final int beforeColor = beforeThumb.getPixel(32, 32);
+ final int beforeColor = extractAverageColor(beforeThumb);
// Verify legacy APIs still work
if (MediaStore.VOLUME_EXTERNAL.equals(mVolumeName)) {
@@ -244,7 +245,7 @@
}
// Edit video contents
- try (InputStream from = mContext.getResources().openRawResource(R.raw.testthumbvideo);
+ try (InputStream from = mContext.getResources().openRawResource(R.raw.testvideo2);
OutputStream to = mResolver.openOutputStream(finalUri)) {
FileUtils.copy(from, to);
}
@@ -252,8 +253,8 @@
// Thumbnail should match updated contents
ProviderTestUtils.waitForIdle();
final Bitmap afterThumb = mResolver.loadThumbnail(finalUri, new Size(64, 64), null);
- final int afterColor = afterThumb.getPixel(32, 32);
- assertNotColorMostlyEquals(beforeColor, afterColor);
+ final int afterColor = extractAverageColor(afterThumb);
+ assertColorMostlyNotEquals(beforeColor, afterColor);
// Delete video contents
mResolver.delete(finalUri, null, null);
@@ -266,13 +267,4 @@
} catch (FileNotFoundException expected) {
}
}
-
- /**
- * Since thumbnails might be bounced through a compression pass, we're okay
- * if they're mostly equal.
- */
- private static void assertNotColorMostlyEquals(int expected, int actual) {
- assertNotEquals(Integer.toHexString(expected & 0xF0F0F0F0),
- Integer.toHexString(actual & 0xF0F0F0F0));
- }
}
diff --git a/tests/tests/sdkextensions/CtsSdkExtensionsTestCases.xml b/tests/tests/sdkextensions/CtsSdkExtensionsTestCases.xml
index 3e8ae7b..a0b2055 100644
--- a/tests/tests/sdkextensions/CtsSdkExtensionsTestCases.xml
+++ b/tests/tests/sdkextensions/CtsSdkExtensionsTestCases.xml
@@ -26,4 +26,12 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.os.ext.cts" />
</test>
+
+ <!-- Run CtsSdkExtensionsTestCases when "com.google.android.sdkext" is on device.
+ Otherwise skip. The logic only applies to situations when CtsSdkExtensionsTestCasesdoes
+ is triggered via mts-tradefed. It is disabled for all other *TS.
+ -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.sdkext" />
+ </object>
</configuration>
diff --git a/tests/tests/secure_element/omapi/Android.bp b/tests/tests/secure_element/omapi/Android.bp
index 925483b..d1e0dab 100644
--- a/tests/tests/secure_element/omapi/Android.bp
+++ b/tests/tests/secure_element/omapi/Android.bp
@@ -12,7 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-android_test {
+//////////////////////////////////////////////////////////////////
+// Signed Package
+
+android_test_import {
+ name: "signed-CtsOmapiTestCases",
+ apk: "apk/signed-CtsOmapiTestCases.apk",
+
+ test_suites: [
+ "cts",
+ "vts10",
+ "general-tests",
+ "cts-instant",
+ ],
+ // Make sure the build system doesn't try to resign the APK
+ presigned: true,
+ dex_preopt: {
+ enabled: false,
+ },
+}
+
+//#################################################################
+// Unsigned Package
+
+android_test_helper_app {
name: "CtsOmapiTestCases",
defaults: ["cts_defaults"],
static_libs: [
diff --git a/tests/tests/secure_element/omapi/AndroidTest.xml b/tests/tests/secure_element/omapi/AndroidTest.xml
index 26b5911..b3e6291 100644
--- a/tests/tests/secure_element/omapi/AndroidTest.xml
+++ b/tests/tests/secure_element/omapi/AndroidTest.xml
@@ -23,7 +23,7 @@
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
- <option name="test-file-name" value="CtsOmapiTestCases.apk"/>
+ <option name="test-file-name" value="signed-CtsOmapiTestCases.apk"/>
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="android.omapi.cts"/>
diff --git a/tests/tests/secure_element/omapi/apk/signed-CtsOmapiTestCases.apk b/tests/tests/secure_element/omapi/apk/signed-CtsOmapiTestCases.apk
new file mode 100644
index 0000000..a6cbfd8
--- /dev/null
+++ b/tests/tests/secure_element/omapi/apk/signed-CtsOmapiTestCases.apk
Binary files differ
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 6c2ac74..ad23b07 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -25,6 +25,10 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <!-- For FileIntegrityManager -->
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/security/res/raw/bug_156261521.dng b/tests/tests/security/res/raw/bug_156261521.dng
new file mode 100644
index 0000000..b838844
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_156261521.dng
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
index 8ffe485..f463855 100644
--- a/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
+++ b/tests/tests/security/src/android/security/cts/BitmapFactorySecurityTests.java
@@ -83,4 +83,11 @@
fail("OOM attempting to decode BMP");
}
}
+
+ @SecurityTest
+ public void test_android_bug_156261521() {
+ // Previously decoding this would crash.
+ FileDescriptor exploitImage = getResource(R.raw.bug_156261521);
+ BitmapFactory.decodeFileDescriptor(exploitImage);
+ }
}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
index a595730..0f5b4ed 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerCtsTestsBase.java
@@ -22,6 +22,7 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
+import android.content.LocusId;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageManager;
@@ -391,10 +392,23 @@
return makeShortcut(id, "Title-" + id);
}
+ /**
+ * Make a shortcut with an ID and explicit rank.
+ */
protected ShortcutInfo makeShortcutWithRank(String id, int rank) {
return makeShortcut(
id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
- makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank);
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), rank, /* locusId =*/ null);
+ }
+
+ /**
+ * Make a shortcut with an ID and a locus ID.
+ */
+ protected ShortcutInfo makeShortcutWithLocusId(String id, String locusId) {
+ return makeShortcut(
+ id, "Title-" + id, /* activity =*/ null, /* icon =*/ null,
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+ new LocusId(locusId));
}
/**
@@ -403,13 +417,15 @@
protected ShortcutInfo makeShortcut(String id, String shortLabel) {
return makeShortcut(
id, shortLabel, /* activity =*/ null, /* icon =*/ null,
- makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+ /* locusId =*/ null);
}
protected ShortcutInfo makeShortcut(String id, ComponentName activity) {
return makeShortcut(
id, "Title-" + id, activity, /* icon =*/ null,
- makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+ /* locusId =*/ null);
}
/**
@@ -418,7 +434,8 @@
protected ShortcutInfo makeShortcutWithIcon(String id, Icon icon) {
return makeShortcut(
id, "Title-" + id, /* activity =*/ null, icon,
- makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0);
+ makeIntent(Intent.ACTION_VIEW, ShortcutActivity.class), /* rank =*/ 0,
+ /* locusId =*/ null);
}
/**
@@ -432,6 +449,19 @@
return ret;
}
+ /**
+ * Makes and array of shortcut IDs.
+ * For example, makeIds("sX", 4, 9) will return {"sX4", "sX5", "sX6", "sX7", "sX8", "sX9"}.
+ */
+ protected String[] makeIds(String prefix, int first, int last) {
+ final int len = last - first + 1;
+ final String[] ret = new String[len];
+ for (int i = 0; i < len; i++) {
+ ret[i] = prefix + (first + i);
+ }
+ return ret;
+ }
+
protected ShortcutInfo.Builder makeShortcutBuilder(String id) {
return new ShortcutInfo.Builder(getCurrentCallerContext(), id);
}
@@ -440,7 +470,7 @@
* Make a shortcut with details.
*/
protected ShortcutInfo makeShortcut(String id, String shortLabel, ComponentName activity,
- Icon icon, Intent intent, int rank) {
+ Icon icon, Intent intent, int rank, LocusId locusId) {
final ShortcutInfo.Builder b = makeShortcutBuilder(id)
.setShortLabel(shortLabel)
.setRank(rank)
@@ -453,6 +483,9 @@
if (icon != null) {
b.setIcon(icon);
}
+ if (locusId != null) {
+ b.setLocusId(locusId);
+ }
return b.build();
}
@@ -550,12 +583,12 @@
}
protected List<ShortcutInfo> getShortcutsAsLauncher(int flags, String packageName) {
- return getShortcutsAsLauncher(flags, packageName, null, 0, null);
+ return getShortcutsAsLauncher(flags, packageName, null, 0, null, null);
}
protected List<ShortcutInfo> getShortcutsAsLauncher(
int flags, String packageName, String activityName,
- long changedSince, List<String> ids) {
+ long changedSince, List<String> ids, List<LocusId> locusIds) {
final ShortcutQuery q = new ShortcutQuery();
q.setQueryFlags(flags);
if (packageName != null) {
@@ -569,6 +602,9 @@
if (ids != null && ids.size() > 0) {
q.setShortcutIds(ids);
}
+ if (locusIds != null && locusIds.size() > 0) {
+ q.setLocusIds(locusIds);
+ }
return getLauncherApps().getShortcuts(q, getUserHandle());
}
}
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
index 67d99fe..33b9c90 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherApiTest.java
@@ -25,6 +25,7 @@
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.retryUntil;
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.setDefaultLauncher;
+import android.content.LocusId;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Icon;
import android.test.suitebuilder.annotation.SmallTest;
@@ -146,6 +147,8 @@
runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().updateShortcuts(list(
+ makeShortcutWithLocusId("s1", "ls1"),
+ makeShortcutWithLocusId("s2", "ls2"),
makeShortcut("s3"))));
setTargetActivityOverride("Launcher_manifest_2");
@@ -179,6 +182,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s3", "s4", "s5")
.areAllNotWithKeyFieldsOnly();
@@ -187,6 +191,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s1", "s2", "s3", "ms1", "ms21")
.areAllNotWithKeyFieldsOnly();
@@ -195,6 +200,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("ms1", "ms21", "ms22")
.areAllNotWithKeyFieldsOnly();
@@ -204,6 +210,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s3", "s4", "s5")
.areAllWithKeyFieldsOnly();
@@ -213,6 +220,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s3", "s4", "s5", "s1", "s2", "s3", "ms1", "ms21")
.areAllNotWithKeyFieldsOnly();
@@ -222,6 +230,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s1", "s2", "s3", "ms1", "ms21", "ms1", "ms21", "ms22")
.areAllNotWithKeyFieldsOnly();
@@ -230,6 +239,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s3", "s4", "s5", "s1", "s2", "ms1", "ms21", "ms1", "ms21", "ms22")
.areAllNotWithKeyFieldsOnly();
@@ -239,6 +249,7 @@
mPackageContext1.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s3", "s4", "s5", "s1", "s2", "ms1", "ms21", "ms1", "ms21", "ms22")
.areAllWithKeyFieldsOnly();
@@ -251,6 +262,7 @@
mPackageContext2.getPackageName(),
null,
0,
+ list(),
list()))
.haveIds("s2", "s3", "s4", "s5", "ms1", "ms31")
;
@@ -261,6 +273,7 @@
mPackageContext1.getPackageName(),
"Launcher_manifest_2",
0,
+ list(),
list()))
.haveIds("ms21", "ms22", "s1", "s5")
.areAllNotWithKeyFieldsOnly();
@@ -271,16 +284,29 @@
mPackageContext1.getPackageName(),
null,
0,
- list("s1", "s2", "ms1")))
+ list("s1", "s2", "ms1"),
+ list()))
.haveIds("s1", "s2", "ms1")
.areAllNotWithKeyFieldsOnly();
+ // With locus ids
+ assertWith(getShortcutsAsLauncher(
+ FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST,
+ mPackageContext1.getPackageName(),
+ null,
+ 0,
+ list(),
+ list(new LocusId("ls1"), new LocusId("ls2"))))
+ .haveIds("s1", "s2")
+ .areAllNotWithKeyFieldsOnly();
+
// With time.
assertWith(getShortcutsAsLauncher(
FLAG_MATCH_DYNAMIC | FLAG_MATCH_PINNED | FLAG_MATCH_MANIFEST,
mPackageContext1.getPackageName(),
null,
time2,
+ list(),
list()))
.haveIds("s4")
.areAllNotWithKeyFieldsOnly();
@@ -291,6 +317,7 @@
mPackageContext1.getPackageName(),
null,
time3,
+ list(),
list()))
.isEmpty();
assertWith(getShortcutsAsLauncher(
@@ -298,6 +325,7 @@
mPackageContext2.getPackageName(),
null,
time3,
+ list(),
list()))
.isEmpty();
});
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
index 5643cad..c19b9bc 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMaxCountTest.java
@@ -34,87 +34,47 @@
public void testNumDynamicShortcuts() {
runWithCallerWithStrictMode(mPackageContext1, () -> {
assertTrue(getManager().setDynamicShortcuts(list(makeShortcut("s1"))));
- assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5"),
- makeShortcut("s6"),
- makeShortcut("s7"),
- makeShortcut("s8"),
- makeShortcut("s9"),
- makeShortcut("s10")
- )));
+
+ assertTrue(getManager().setDynamicShortcuts(makeShortcuts(makeIds("s", 1, 15))));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
+ .haveIds(makeIds("s", 1, 15))
.areAllDynamic()
.areAllEnabled();
- assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcut("s1x"),
- makeShortcut("s2x"),
- makeShortcut("s3x"),
- makeShortcut("s4x"),
- makeShortcut("s5x"),
- makeShortcut("s6x"),
- makeShortcut("s7x"),
- makeShortcut("s8x"),
- makeShortcut("s9x"),
- makeShortcut("s10x")
- )));
+
+ assertTrue(getManager().setDynamicShortcuts(makeShortcuts(makeIds("sx", 1, 15))));
assertDynamicShortcutCountExceeded(() -> {
- getManager().setDynamicShortcuts(list(
- makeShortcut("s1y"),
- makeShortcut("s2y"),
- makeShortcut("s3y"),
- makeShortcut("s4y"),
- makeShortcut("s5y"),
- makeShortcut("s6y"),
- makeShortcut("s7y"),
- makeShortcut("s8y"),
- makeShortcut("s9y"),
- makeShortcut("s10y"),
- makeShortcut("s11y")));
+ getManager().setDynamicShortcuts(makeShortcuts(makeIds("sy", 1, 16)));
});
+
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1x", "s2x", "s3x", "s4x", "s5x", "s6x", "s7x", "s8x", "s9x", "s10x")
+ .haveIds(makeIds("sx", 1, 15))
.areAllDynamic()
.areAllEnabled();
assertDynamicShortcutCountExceeded(() -> {
getManager().addDynamicShortcuts(list(
- makeShortcut("s1y")));
+ makeShortcut("sy1")));
});
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1x", "s2x", "s3x", "s4x", "s5x", "s6x", "s7x", "s8x", "s9x", "s10x")
+ .haveIds(makeIds("sx", 1, 15))
.areAllDynamic()
.areAllEnabled();
- getManager().removeDynamicShortcuts(list("s10x"));
+ getManager().removeDynamicShortcuts(list("sx15"));
assertTrue(getManager().addDynamicShortcuts(list(
- makeShortcut("s1y"))));
+ makeShortcut("sy1"))));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1x", "s2x", "s3x", "s4x", "s5x", "s6x", "s7x", "s8x", "s9x", "s1y")
+ .haveIds("sx1", "sx2", "sx3", "sx4", "sx5", "sx6", "sx7", "sx8", "sx9", "sx10",
+ "sx11", "sx12", "sx13", "sx14", "sy1")
.areAllDynamic()
.areAllEnabled();
getManager().removeAllDynamicShortcuts();
- assertTrue(getManager().addDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5"),
- makeShortcut("s6"),
- makeShortcut("s7"),
- makeShortcut("s8"),
- makeShortcut("s9"),
- makeShortcut("s10")
- )));
+ assertTrue(getManager().setDynamicShortcuts(makeShortcuts(makeIds("s", 1, 15))));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
+ .haveIds(makeIds("s", 1, 15))
.areAllDynamic()
.areAllEnabled();
});
@@ -156,71 +116,42 @@
// Note since max counts is per activity, testNumDynamicShortcuts_single should just pass.
testNumDynamicShortcuts();
- // Launcher_manifest_1 has one manifest, so can only add 9 dynamic shortcuts.
+ // Launcher_manifest_1 has one manifest, so can only add 14 dynamic shortcuts.
runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher_manifest_1");
- assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5"),
- makeShortcut("s6"),
- makeShortcut("s7"),
- makeShortcut("s8"),
- makeShortcut("s9")
- )));
+ assertTrue(getManager().setDynamicShortcuts(makeShortcuts(makeIds("s", 1, 14))));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_1"))
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9")
+ .haveIds(makeIds("s", 1, 14))
.areAllEnabled();
- assertDynamicShortcutCountExceeded(() -> getManager().setDynamicShortcuts(list(
- makeShortcut("s1x"),
- makeShortcut("s2x"),
- makeShortcut("s3x"),
- makeShortcut("s4x"),
- makeShortcut("s5x"),
- makeShortcut("s6x"),
- makeShortcut("s7x"),
- makeShortcut("s8x"),
- makeShortcut("s9x"),
- makeShortcut("s10x")
- )));
+ assertDynamicShortcutCountExceeded(() -> getManager().setDynamicShortcuts(
+ makeShortcuts(makeIds("sx", 1, 15))));
// Not changed.
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_1"))
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9")
+ .haveIds(makeIds("s", 1, 14))
.areAllEnabled();
});
- // Launcher_manifest_2 has two manifests, so can only add 8.
+ // Launcher_manifest_2 has two manifests, so can only add 13.
runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher_manifest_2");
- assertTrue(getManager().addDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5"),
- makeShortcut("s6"),
- makeShortcut("s7"),
- makeShortcut("s8")
- )));
+ assertTrue(getManager().addDynamicShortcuts(makeShortcuts(makeIds("s", 1, 13))));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_2"))
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8")
+ .haveIds(makeIds("s", 1, 13))
.areAllEnabled();
assertDynamicShortcutCountExceeded(() -> getManager().addDynamicShortcuts(list(
- makeShortcut("s1x")
+ makeShortcut("sx1")
)));
// Not added.
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher_manifest_2"))
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8")
+ .haveIds(makeIds("s", 1, 13))
.areAllEnabled();
});
}
@@ -228,47 +159,25 @@
public void testChangeActivity() {
runWithCallerWithStrictMode(mPackageContext1, () -> {
setTargetActivityOverride("Launcher");
- assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5"),
- makeShortcut("s6"),
- makeShortcut("s7"),
- makeShortcut("s8"),
- makeShortcut("s9"),
- makeShortcut("s10")
- )));
+ assertTrue(getManager().setDynamicShortcuts(makeShortcuts(makeIds("s", 1, 15))));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
+ .haveIds(makeIds("s", 1, 15))
.areAllDynamic()
.areAllEnabled();
setTargetActivityOverride("Launcher2");
+ assertTrue(getManager().addDynamicShortcuts(makeShortcuts(makeIds("sb", 1, 15))));
- assertTrue(getManager().addDynamicShortcuts(list(
- makeShortcut("s1b"),
- makeShortcut("s2b"),
- makeShortcut("s3b"),
- makeShortcut("s4b"),
- makeShortcut("s5b"),
- makeShortcut("s6b"),
- makeShortcut("s7b"),
- makeShortcut("s8b"),
- makeShortcut("s9b"),
- makeShortcut("s10b")
- )));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
+ .haveIds(makeIds("s", 1, 15))
.areAllDynamic()
.areAllEnabled()
.revertToOriginalList()
.selectByActivity(getActivity("Launcher2"))
- .haveIds("s1b", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
+ .haveIds(makeIds("sb", 1, 15))
.areAllDynamic()
.areAllEnabled();
@@ -279,31 +188,33 @@
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
+ .haveIds(makeIds("s", 1, 15))
.areAllDynamic()
.areAllEnabled()
.revertToOriginalList()
.selectByActivity(getActivity("Launcher2"))
- .haveIds("s1b", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
+ .haveIds(makeIds("sb", 1, 15))
.areAllDynamic()
.areAllEnabled();
// But swapping shortcuts will work.
assertTrue(getManager().updateShortcuts(list(
makeShortcut("s1", getActivity("Launcher2")),
- makeShortcut("s1b", getActivity("Launcher"))
+ makeShortcut("sb1", getActivity("Launcher"))
)));
assertWith(getManager().getDynamicShortcuts())
.selectByActivity(getActivity("Launcher"))
- .haveIds("s1b", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
+ .haveIds("sb1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
+ "s12", "s13", "s14", "s15")
.areAllDynamic()
.areAllEnabled()
.revertToOriginalList()
.selectByActivity(getActivity("Launcher2"))
- .haveIds("s1", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
+ .haveIds("s1", "sb2", "sb3", "sb4", "sb5", "sb6", "sb7", "sb8", "sb9", "sb10",
+ "sb11", "sb12", "sb13", "sb14", "sb15")
.areAllDynamic()
.areAllEnabled();
});
@@ -311,49 +222,27 @@
public void testWithPinned() {
runWithCallerWithStrictMode(mPackageContext1, () -> {
- assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcut("s1"),
- makeShortcut("s2"),
- makeShortcut("s3"),
- makeShortcut("s4"),
- makeShortcut("s5"),
- makeShortcut("s6"),
- makeShortcut("s7"),
- makeShortcut("s8"),
- makeShortcut("s9"),
- makeShortcut("s10")
- )));
+ assertTrue(getManager().setDynamicShortcuts(makeShortcuts(makeIds("s", 1, 15))));
});
setDefaultLauncher(getInstrumentation(), mLauncherContext1);
runWithCallerWithStrictMode(mLauncherContext1, () -> {
getLauncherApps().pinShortcuts(mPackageContext1.getPackageName(),
- list("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10"),
+ list(makeIds("s", 1, 15)),
getUserHandle());
});
runWithCallerWithStrictMode(mPackageContext1, () -> {
- assertTrue(getManager().setDynamicShortcuts(list(
- makeShortcut("s1b"),
- makeShortcut("s2b"),
- makeShortcut("s3b"),
- makeShortcut("s4b"),
- makeShortcut("s5b"),
- makeShortcut("s6b"),
- makeShortcut("s7b"),
- makeShortcut("s8b"),
- makeShortcut("s9b"),
- makeShortcut("s10b")
- )));
+ assertTrue(getManager().setDynamicShortcuts(makeShortcuts(makeIds("sb", 1, 15))));
assertWith(getManager().getDynamicShortcuts())
- .haveIds("s1b", "s2b", "s3b", "s4b", "s5b", "s6b", "s7b", "s8b", "s9b", "s10b")
+ .haveIds(makeIds("sb", 1, 15))
.areAllEnabled()
.areAllNotPinned();
assertWith(getManager().getPinnedShortcuts())
- .haveIds("s1", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10")
+ .haveIds(makeIds("s", 1, 15))
.areAllEnabled()
.areAllNotDynamic();
});
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java
index b3a6fa1..1d7e8fc 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerMiscTest.java
@@ -34,7 +34,7 @@
public void testMiscApis() throws Exception {
ShortcutManager manager = getTestContext().getSystemService(ShortcutManager.class);
- assertEquals(10, manager.getMaxShortcutCountPerActivity());
+ assertEquals(15, manager.getMaxShortcutCountPerActivity());
// during the test, this process always considered to be in the foreground.
assertFalse(manager.isRateLimitingActive());
diff --git a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
index 1a84f34..cddf6df 100644
--- a/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/WindowInsetsBehaviorTests.java
@@ -86,6 +86,7 @@
"/sdcard/WindowInsetsBehaviorTests";
private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
private static final String ARGUMENT_KEY_FORCE_ENABLE = "force_enable_gesture_navigation";
+ private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
private static final int STEPS = 10;
// The minimum value of the system gesture exclusion limit is 200 dp. The value here should be
@@ -703,6 +704,8 @@
public void swipeOutsideLimit_systemUiVisible_allEventsCanceled() throws Throwable {
assumeTrue(hasSystemGestureFeature());
+ assumeGestureNavigationMode();
+
final int swipeCount = 1;
final boolean insideLimit = false;
testSystemGestureExclusionLimit(swipeCount, insideLimit, SYSTEM_UI_FLAG_VISIBLE);
@@ -779,6 +782,14 @@
assumeTrue("Gesture navigation required.", insets[0].left > 0);
}
+ private void assumeGestureNavigationMode() {
+ // TODO: b/153032202 consider the CTS on GSI case.
+ Resources res = mTargetContext.getResources();
+ int naviMode = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer", "android");
+
+ assumeTrue("Gesture navigation required", naviMode == 2);
+ }
+
/**
* Set system UI visibility and wait for it is applied by the system.
*
diff --git a/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java b/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java
index c493c5b..80ef505 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BackgroundCallAudioTest.java
@@ -549,7 +549,10 @@
call.exitBackgroundAudioProcessing(true);
assertCallState(call, Call.STATE_SIMULATED_RINGING);
waitOnAllHandlers(getInstrumentation());
- assertEquals(AudioManager.MODE_RINGTONE, audioManager.getMode());
+ // We expect the audio mode to stay in CALL_SCREENING when going into simulated ringing.
+ if (doesAudioManagerSupportCallScreening) {
+ assertEquals(MODE_CALL_SCREENING, audioManager.getMode());
+ }
assertConnectionState(connection, Connection.STATE_ACTIVE);
call.answer(VideoProfile.STATE_AUDIO_ONLY);
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
index b7bd6d0..a992b9b 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -407,6 +407,17 @@
assertTrue("NID is required for registered cells", networkId != Integer.MAX_VALUE);
assertTrue("BSID is required for registered cells", basestationId != Integer.MAX_VALUE);
}
+
+ verifyCellIdentityCdmaLocationSanitation(cdma);
+ }
+
+ private void verifyCellIdentityCdmaLocationSanitation(CellIdentityCdma cdma) {
+ CellIdentityCdma sanitized = cdma.sanitizeLocationInfo();
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getNetworkId());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getSystemId());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getBasestationId());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getLongitude());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getLatitude());
}
private void verifyCellIdentityCdmaParcel(CellIdentityCdma cdma) {
@@ -564,6 +575,15 @@
assertTrue("MCC is required for registered cells", nr.getMccString() != null);
assertTrue("MNC is required for registered cells", nr.getMncString() != null);
}
+
+ verifyCellIdentityNrLocationSanitation(nr);
+ }
+
+ private void verifyCellIdentityNrLocationSanitation(CellIdentityNr nr) {
+ CellIdentityNr sanitized = nr.sanitizeLocationInfo();
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getPci());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getTac());
+ assertEquals(CellInfo.UNAVAILABLE_LONG, sanitized.getNci());
}
private void verifyCellSignalStrengthNr(CellSignalStrengthNr nr) {
@@ -817,6 +837,16 @@
assertTrue("MNC is required for registered cells",
wcdma.getMncString() != null || wcdma.getMnc() != Integer.MAX_VALUE);
}
+
+ verifyCellIdentityWcdmaLocationSanitation(wcdma);
+ }
+
+ private void verifyCellIdentityWcdmaLocationSanitation(CellIdentityWcdma wcdma) {
+ CellIdentityWcdma sanitized = wcdma.sanitizeLocationInfo();
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getLac());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getCid());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getPsc());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getUarfcn());
}
private void verifyCellIdentityWcdmaParcel(CellIdentityWcdma wcdma) {
@@ -927,6 +957,16 @@
assertTrue("MNC is required for registered cells",
gsm.getMncString() != null || gsm.getMnc() != Integer.MAX_VALUE);
}
+
+ verifyCellIdentityGsmLocationSanitation(gsm);
+ }
+
+ private void verifyCellIdentityGsmLocationSanitation(CellIdentityGsm gms) {
+ CellIdentityGsm sanitized = gms.sanitizeLocationInfo();
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getLac());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getCid());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getArfcn());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getBsic());
}
private void verifyCellIdentityGsmParcel(CellIdentityGsm gsm) {
@@ -1041,6 +1081,16 @@
assertTrue("MCC is required for registered cells", tdscdma.getMccString() != null);
assertTrue("MNC is required for registered cells", tdscdma.getMncString() != null);
}
+
+ verifyCellIdentityTdscdmaLocationSanitation(tdscdma);
+ }
+
+ private void verifyCellIdentityTdscdmaLocationSanitation(CellIdentityTdscdma tdscdma) {
+ CellIdentityTdscdma sanitized = tdscdma.sanitizeLocationInfo();
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getLac());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getCid());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getCpid());
+ assertEquals(CellInfo.UNAVAILABLE, sanitized.getUarfcn());
}
private void verifyCellIdentityTdscdmaParcel(CellIdentityTdscdma tdscdma) {
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/NetworkRegistrationInfoTest.java b/tests/tests/telephony/current/src/android/telephony/cts/NetworkRegistrationInfoTest.java
index a5e75c5..f5a8477 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/NetworkRegistrationInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/NetworkRegistrationInfoTest.java
@@ -15,11 +15,15 @@
*/
package android.telephony.cts;
+import android.os.Parcel;
import android.telephony.AccessNetworkConstants;
+import android.telephony.CellIdentity;
+import android.telephony.CellIdentityLte;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.TelephonyManager;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
@@ -120,7 +124,7 @@
}
@Test
- public void testRegistrationState() {
+ public void testGetRegistrationState() {
NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
.setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
.build();
@@ -134,4 +138,110 @@
.build();
assertEquals(AccessNetworkConstants.TRANSPORT_TYPE_WWAN, nri.getTransportType());
}
+
+ @Test
+ public void testGetRegisteredPlmn() {
+ final String plmn = "12345";
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRegisteredPlmn(plmn)
+ .build();
+ assertEquals(plmn, nri.getRegisteredPlmn());
+ }
+
+ @Test
+ public void testGetRejectCause() {
+ final int fakeRejectCause = 123;
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRejectCause(fakeRejectCause)
+ .build();
+ assertEquals(fakeRejectCause, nri.getRejectCause());
+ }
+
+ @Test
+ public void testIsEmergencyEnabled() {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setEmergencyOnly(true)
+ .build();
+ assertTrue(nri.isEmergencyEnabled());
+ }
+
+ @Test
+ public void testGetCellIdentity() {
+ final CellIdentity ci = new CellIdentityLte(120 /* MCC */, 260 /* MNC */, 12345 /* CI */,
+ 503 /* PCI */, 54321 /* TAC */);
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setCellIdentity(ci)
+ .build();
+ assertEquals(ci, nri.getCellIdentity());
+ }
+
+ @Test
+ public void testIsRegistered() {
+ final int[] registeredStates = new int[] {NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING};
+ for (int state : registeredStates) {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(state)
+ .build();
+ assertTrue(nri.isRegistered());
+ }
+
+ final int[] unregisteredStates = new int[] {
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING,
+ NetworkRegistrationInfo.REGISTRATION_STATE_DENIED,
+ NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN};
+ for (int state : unregisteredStates) {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(state)
+ .build();
+ assertFalse(nri.isRegistered());
+ }
+ }
+
+ @Test
+ public void testIsSearching() {
+ final int[] isSearchingStates = new int[] {
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_SEARCHING};
+ for (int state : isSearchingStates) {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(state)
+ .build();
+ assertTrue(nri.isSearching());
+ }
+
+ final int[] isNotSearchingStates = new int[] {
+ NetworkRegistrationInfo.REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING,
+ NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING,
+ NetworkRegistrationInfo.REGISTRATION_STATE_HOME,
+ NetworkRegistrationInfo.REGISTRATION_STATE_DENIED,
+ NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN};
+ for (int state : isNotSearchingStates) {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setRegistrationState(state)
+ .build();
+ assertFalse(nri.isSearching());
+ }
+ }
+
+ @Test
+ public void testParcel() {
+ NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
+ .setDomain(NetworkRegistrationInfo.DOMAIN_CS)
+ .setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
+ .setRegistrationState(NetworkRegistrationInfo.REGISTRATION_STATE_HOME)
+ .setAccessNetworkTechnology(TelephonyManager.NETWORK_TYPE_LTE)
+ .setAvailableServices(Arrays.asList(NetworkRegistrationInfo.SERVICE_TYPE_DATA))
+ .setCellIdentity(new CellIdentityLte(120 /* MCC */, 260 /* MNC */, 12345 /* CI */,
+ 503 /* PCI */, 54321 /* TAC */))
+ .setRegisteredPlmn("12345")
+ .build();
+
+ Parcel p = Parcel.obtain();
+ nri.writeToParcel(p, 0);
+ p.setDataPosition(0);
+
+ NetworkRegistrationInfo newNrs = NetworkRegistrationInfo.CREATOR.createFromParcel(p);
+ assertEquals(nri, newNrs);
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
index cf7bfef..dbb769d 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsMessageTest.java
@@ -18,6 +18,7 @@
import static androidx.test.InstrumentationRegistry.getContext;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -384,6 +385,53 @@
assertNotNull(smsPdu);
}
+ @Test
+ public void testGetSubmitPduEncodedMessage() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ String destinationAddress = "18004664411";
+ String message = "This is a test message";
+
+ byte[] gsmMsg = SmsMessage.getSubmitPduEncodedMessage(true, destinationAddress,
+ message,
+ 1, // Encoding code unit size. Pick ENCODING_7BIT here.
+ 0, // GSM national language table. It's usually 0.
+ 0, // GSM national language table. It's usually 0.
+ 1, // Reference number of concatenated SMS. Pick 1 for simplicity.
+ 1, // Sequence number of concatenated SMS. Pick 1 for simplicity.
+ 2); // Count of messages of concatenated SMS. Pick 2 for simplicity.
+
+ // Encoded gsm message.
+ byte[] expectedGsmMsg = {65, 0, 11, -127, -127, 0, 100, 70, 20, -15, 0, 0, 29, 5, 0, 3, 1,
+ 2, 1, -88, -24, -12, 28, -108, -98, -125, -62, 32, 122, 121, 78, 7, -75, -53, -13,
+ 121, -8, 92, 6};
+
+ // See comments for gsmMsg.
+ byte[] cdmaMsg = SmsMessage.getSubmitPduEncodedMessage(false, destinationAddress,
+ message, 1, 0, 0, 1, 1, 2);
+
+ // Encoded cdma message.
+ byte[] expectedCdmaMsg = {0, 0, 16, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 1, 8, 10, 10,
+ 4, 6, 6, 4, 4, 1, 1, 0, 0, 0, 35, 0, 3, 32, 0, 24, 1, 28, 72, -24, 40, 0, 24, 8, 16,
+ 13, 71, 71, -96, -28, -92, -12, 30, 17, 3, -45, -54, 112, 61, -82, 95, -101, -49,
+ -62, -32, 48};
+
+ assertArrayEquals(expectedGsmMsg, gsmMsg);
+ assertArrayEquals(expectedCdmaMsg, cdmaMsg);
+ }
+
+ @Test
+ public void testCreateFromNativeSmsSubmitPdu() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ // Short message with status RECEIVED_READ and size 0. See 3GPP2 C.S0023 3.4.27
+ byte[] submitPdu = {1, 0};
+ SmsMessage sms = SmsMessage.createFromNativeSmsSubmitPdu(submitPdu, true);
+ assertNull(sms);
+ }
+
private final static char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F' };
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index d9ab2f3..af6629d 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -2232,7 +2232,7 @@
return;
}
if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
- fail("This test requires two SIM cards.");
+ return;
}
ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
(tm) -> tm.setPreferredOpportunisticDataSubscription(
@@ -2368,7 +2368,7 @@
return;
}
if (mTelephonyManager.getPhoneCount() == 2 && activeSubscriptionInfoCount != 2) {
- fail("This test requires two SIM cards.");
+ return;
}
List<SubscriptionInfo> subscriptionInfoList =
diff --git a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
index a9097bb..2b4e3ac 100644
--- a/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/euicc/cts/EuiccManagerTest.java
@@ -17,7 +17,9 @@
package android.telephony.euicc.cts;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.PendingIntent;
@@ -39,6 +41,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -311,6 +316,125 @@
assertEquals(EuiccManager.ERROR_OPERATION_BUSY, 10016);
}
+ @Test
+ public void testSetSupportedCountries() {
+ // Get country list for restoring later.
+ List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
+
+ List<String> expectedCountries = Arrays.asList("US", "SG");
+ // Sets supported countries
+ mEuiccManager.setSupportedCountries(expectedCountries);
+
+ // Verify supported countries are expected
+ assertEquals(expectedCountries, mEuiccManager.getSupportedCountries());
+
+ // Restore the original country list
+ mEuiccManager.setSupportedCountries(originalSupportedCountry);
+ }
+
+ @Test
+ public void testSetUnsupportedCountries() {
+ // Get country list for restoring later.
+ List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
+
+ List<String> expectedCountries = Arrays.asList("US", "SG");
+ // Sets unsupported countries
+ mEuiccManager.setUnsupportedCountries(expectedCountries);
+
+ // Verify unsupported countries are expected
+ assertEquals(expectedCountries, mEuiccManager.getUnsupportedCountries());
+
+ // Restore the original country list
+ mEuiccManager.setUnsupportedCountries(originalUnsupportedCountry);
+ }
+
+ @Test
+ public void testIsSupportedCountry_returnsTrue_ifCountryIsOnSupportedList() {
+ // Get country list for restoring later.
+ List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
+
+ // Sets supported countries
+ mEuiccManager.setSupportedCountries(Arrays.asList("US", "SG"));
+
+ // Verify the country is supported
+ assertTrue(mEuiccManager.isSupportedCountry("US"));
+
+ // Restore the original country list
+ mEuiccManager.setSupportedCountries(originalSupportedCountry);
+ }
+
+ @Test
+ public void testIsSupportedCountry_returnsTrue_ifCountryIsNotOnUnsupportedList() {
+ // Get country list for restoring later.
+ List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
+ List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
+
+ // Sets supported countries
+ mEuiccManager.setSupportedCountries(new ArrayList<>());
+ // Sets unsupported countries
+ mEuiccManager.setUnsupportedCountries(Arrays.asList("SG"));
+
+ // Verify the country is supported
+ assertTrue(mEuiccManager.isSupportedCountry("US"));
+
+ // Restore the original country list
+ mEuiccManager.setSupportedCountries(originalSupportedCountry);
+ mEuiccManager.setUnsupportedCountries(originalUnsupportedCountry);
+ }
+
+ @Test
+ public void testIsSupportedCountry_returnsFalse_ifCountryIsNotOnSupportedList() {
+ // Get country list for restoring later.
+ List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
+
+ // Sets supported countries
+ mEuiccManager.setSupportedCountries(Arrays.asList("SG"));
+
+ // Verify the country is not supported
+ assertFalse(mEuiccManager.isSupportedCountry("US"));
+
+ // Restore the original country list
+ mEuiccManager.setSupportedCountries(originalSupportedCountry);
+ }
+
+ @Test
+ public void testIsSupportedCountry_returnsFalse_ifCountryIsOnUnsupportedList() {
+ // Get country list for restoring later.
+ List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
+ List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
+
+ // Sets supported countries
+ mEuiccManager.setSupportedCountries(new ArrayList<>());
+ // Sets unsupported countries
+ mEuiccManager.setUnsupportedCountries(Arrays.asList("US"));
+
+ // Verify the country is not supported
+ assertFalse(mEuiccManager.isSupportedCountry("US"));
+
+ // Restore the original country list
+ mEuiccManager.setSupportedCountries(originalSupportedCountry);
+ mEuiccManager.setUnsupportedCountries(originalUnsupportedCountry);
+ }
+
+ @Test
+ public void testIsSupportedCountry_returnsFalse_ifBothListsAreEmpty() {
+ // Get country list for restoring later.
+ List<String> originalSupportedCountry = mEuiccManager.getSupportedCountries();
+ List<String> originalUnsupportedCountry = mEuiccManager.getUnsupportedCountries();
+
+ // Sets supported countries
+ mEuiccManager.setSupportedCountries(new ArrayList<>());
+ // Sets unsupported countries
+ mEuiccManager.setUnsupportedCountries(new ArrayList<>());
+
+ // Verify the country is supported
+ assertTrue(mEuiccManager.isSupportedCountry("US"));
+
+ // Restore the original country list
+ mEuiccManager.setSupportedCountries(originalSupportedCountry);
+ mEuiccManager.setUnsupportedCountries(originalUnsupportedCountry);
+ }
+
private Context getContext() {
return InstrumentationRegistry.getContext();
}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
index cddf19e..a965d7b 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsMmTelManagerTest.java
@@ -99,7 +99,11 @@
@BeforeClass
public static void beforeAllTests() {
- assumeTrue(ImsUtils.shouldTestImsService());
+ // assumeTrue() in @BeforeClass is not supported by our test runner.
+ // Resort to the early exit.
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
sTestSub = ImsUtils.getPreferredActiveSubId();
@@ -116,6 +120,12 @@
@AfterClass
public static void afterAllTests() {
+ // assumeTrue() in @AfterClass is not supported by our test runner.
+ // Resort to the early exit.
+ if (!ImsUtils.shouldTestImsService()) {
+ return;
+ }
+
if (sReceiver != null) {
getContext().unregisterReceiver(sReceiver);
sReceiver = null;
@@ -124,6 +134,8 @@
@Before
public void beforeTest() {
+ assumeTrue(ImsUtils.shouldTestImsService());
+
if (!SubscriptionManager.isValidSubscriptionId(sTestSub)) {
fail("This test requires that there is a SIM in the device!");
}
diff --git a/tests/tests/tethering/AndroidTest.xml b/tests/tests/tethering/AndroidTest.xml
index d0a2bce..e752e3a 100644
--- a/tests/tests/tethering/AndroidTest.xml
+++ b/tests/tests/tethering/AndroidTest.xml
@@ -28,4 +28,8 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.tethering.cts" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.tethering" />
+ </object>
</configuration>
diff --git a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
index bbb9403..8665c7e 100644
--- a/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/tests/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -26,7 +26,6 @@
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
-import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_FAILED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
@@ -59,7 +58,10 @@
import android.net.cts.util.CtsNetUtils;
import android.net.wifi.WifiManager;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import androidx.annotation.NonNull;
@@ -676,6 +678,26 @@
mTM.requestLatestTetheringEntitlementResult(
TETHERING_WIFI, false, c -> c.run(), null);
} catch (IllegalArgumentException expect) { }
+
+ // Override carrier config to ignore entitlement check.
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putBoolean(CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, false);
+ overrideCarrierConfig(bundle);
+
+ // Verify that requestLatestTetheringEntitlementResult() can get entitlement
+ // result TETHER_ERROR_NO_ERROR due to provisioning bypassed.
+ assertEntitlementResult(listener -> mTM.requestLatestTetheringEntitlementResult(
+ TETHERING_WIFI, false, c -> c.run(), listener), TETHER_ERROR_NO_ERROR);
+
+ // Reset carrier config.
+ overrideCarrierConfig(null);
+ }
+
+ private void overrideCarrierConfig(PersistableBundle bundle) {
+ final CarrierConfigManager configManager = (CarrierConfigManager) mContext
+ .getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ final int subId = SubscriptionManager.getDefaultSubscriptionId();
+ configManager.overrideConfig(subId, bundle);
}
@Test
diff --git a/tests/tests/textclassifier/AndroidTest.xml b/tests/tests/textclassifier/AndroidTest.xml
index 1815643..f7e25ff 100644
--- a/tests/tests/textclassifier/AndroidTest.xml
+++ b/tests/tests/textclassifier/AndroidTest.xml
@@ -30,4 +30,9 @@
<option name="hidden-api-checks" value="false" />
<option name="isolated-storage" value="false" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.extservices" />
+ </object>
+
</configuration>
diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java
index 86e63a5..92e5539 100644
--- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java
+++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerFrontendTest.java
@@ -93,6 +93,7 @@
.setSifStandard(AnalogFrontendSettings.SIF_BG_NICAM)
.build();
+ assertEquals(FrontendSettings.TYPE_ANALOG, settings.getType());
assertEquals(1, settings.getFrequency());
assertEquals(AnalogFrontendSettings.SIGNAL_TYPE_NTSC, settings.getSignalType());
assertEquals(AnalogFrontendSettings.SIF_BG_NICAM, settings.getSifStandard());
@@ -131,6 +132,7 @@
.setPlpSettings(new Atsc3PlpSettings[] {plp1, plp2})
.build();
+ assertEquals(FrontendSettings.TYPE_ATSC3, settings.getType());
assertEquals(2, settings.getFrequency());
assertEquals(Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_6MHZ, settings.getBandwidth());
assertEquals(Atsc3FrontendSettings.MODULATION_MOD_QPSK, settings.getDemodOutputFormat());
@@ -161,6 +163,7 @@
.setModulation(AtscFrontendSettings.MODULATION_MOD_8VSB)
.build();
+ assertEquals(FrontendSettings.TYPE_ATSC, settings.getType());
assertEquals(3, settings.getFrequency());
assertEquals(AtscFrontendSettings.MODULATION_MOD_8VSB, settings.getModulation());
}
@@ -180,6 +183,7 @@
.setSpectralInversion(DvbcFrontendSettings.SPECTRAL_INVERSION_NORMAL)
.build();
+ assertEquals(FrontendSettings.TYPE_DVBC, settings.getType());
assertEquals(4, settings.getFrequency());
assertEquals(DvbcFrontendSettings.MODULATION_MOD_32QAM, settings.getModulation());
assertEquals(FrontendSettings.FEC_8_15, settings.getInnerFec());
@@ -217,6 +221,7 @@
.setVcmMode(DvbsFrontendSettings.VCM_MODE_MANUAL)
.build();
+ assertEquals(FrontendSettings.TYPE_DVBS, settings.getType());
assertEquals(5, settings.getFrequency());
assertEquals(DvbsFrontendSettings.MODULATION_MOD_ACM, settings.getModulation());
assertEquals(3, settings.getSymbolRate());
@@ -256,6 +261,7 @@
.setPlpGroupId(777)
.build();
+ assertEquals(FrontendSettings.TYPE_DVBT, settings.getType());
assertEquals(6, settings.getFrequency());
assertEquals(DvbtFrontendSettings.TRANSMISSION_MODE_8K, settings.getTransmissionMode());
assertEquals(DvbtFrontendSettings.BANDWIDTH_1_7MHZ, settings.getBandwidth());
@@ -287,6 +293,7 @@
.setRolloff(Isdbs3FrontendSettings.ROLLOFF_0_03)
.build();
+ assertEquals(FrontendSettings.TYPE_ISDBS3, settings.getType());
assertEquals(7, settings.getFrequency());
assertEquals(2, settings.getStreamId());
assertEquals(IsdbsFrontendSettings.STREAM_ID_TYPE_ID, settings.getStreamIdType());
@@ -311,6 +318,7 @@
.setRolloff(IsdbsFrontendSettings.ROLLOFF_0_35)
.build();
+ assertEquals(FrontendSettings.TYPE_ISDBS, settings.getType());
assertEquals(8, settings.getFrequency());
assertEquals(3, settings.getStreamId());
assertEquals(
@@ -336,6 +344,7 @@
.setServiceAreaId(10)
.build();
+ assertEquals(FrontendSettings.TYPE_ISDBT, settings.getType());
assertEquals(9, settings.getFrequency());
assertEquals(IsdbtFrontendSettings.MODULATION_MOD_64QAM, settings.getModulation());
assertEquals(IsdbtFrontendSettings.BANDWIDTH_8MHZ, settings.getBandwidth());
@@ -357,6 +366,7 @@
assertTrue(info.getSymbolRateRange().getLower() >= 0);
assertTrue(info.getAcquireRange() > 0);
info.getExclusiveGroupId();
+ info.getStatusCapabilities();
FrontendCapabilities caps = info.getFrontendCapabilities();
assertNotNull(caps);
diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
index 08b2c25..a63d7e3 100644
--- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
+++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java
@@ -17,11 +17,13 @@
package android.media.tv.tuner.cts;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.content.Context;
+
import android.media.tv.tuner.Descrambler;
import android.media.tv.tuner.LnbCallback;
import android.media.tv.tuner.Lnb;
@@ -30,10 +32,20 @@
import android.media.tv.tuner.dvr.DvrRecorder;
import android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener;
import android.media.tv.tuner.dvr.OnRecordStatusChangedListener;
+
+import android.media.tv.tuner.filter.AudioDescriptor;
+import android.media.tv.tuner.filter.DownloadEvent;
import android.media.tv.tuner.filter.FilterCallback;
import android.media.tv.tuner.filter.FilterEvent;
import android.media.tv.tuner.filter.Filter;
+import android.media.tv.tuner.filter.IpPayloadEvent;
+import android.media.tv.tuner.filter.MediaEvent;
+import android.media.tv.tuner.filter.MmtpRecordEvent;
+import android.media.tv.tuner.filter.PesEvent;
+import android.media.tv.tuner.filter.SectionEvent;
+import android.media.tv.tuner.filter.TemiEvent;
import android.media.tv.tuner.filter.TimeFilter;
+import android.media.tv.tuner.filter.TsRecordEvent;
import android.media.tv.tuner.frontend.AnalogFrontendCapabilities;
import android.media.tv.tuner.frontend.AnalogFrontendSettings;
@@ -53,6 +65,7 @@
import android.media.tv.tuner.frontend.FrontendCapabilities;
import android.media.tv.tuner.frontend.FrontendInfo;
import android.media.tv.tuner.frontend.FrontendSettings;
+import android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo;
import android.media.tv.tuner.frontend.FrontendStatus;
import android.media.tv.tuner.frontend.Isdbs3FrontendCapabilities;
import android.media.tv.tuner.frontend.Isdbs3FrontendSettings;
@@ -202,7 +215,14 @@
status.getFreqOffset();
status.getHierarchy();
status.isRfLocked();
- status.getAtsc3PlpTuningInfo();
+ Atsc3PlpTuningInfo[] tuningInfos = status.getAtsc3PlpTuningInfo();
+ if (tuningInfos != null) {
+ for (Atsc3PlpTuningInfo tuningInfo : tuningInfos) {
+ tuningInfo.getPlpId();
+ tuningInfo.isLocked();
+ tuningInfo.getUec();
+ }
+ }
}
@Test
@@ -291,12 +311,114 @@
private FilterCallback getFilterCallback() {
return new FilterCallback() {
@Override
- public void onFilterEvent(Filter filter, FilterEvent[] events) {}
+ public void onFilterEvent(Filter filter, FilterEvent[] events) {
+ for (FilterEvent e : events) {
+ if (e instanceof DownloadEvent) {
+ testDownloadEvent(filter, (DownloadEvent) e);
+ } else if (e instanceof IpPayloadEvent) {
+ testIpPayloadEvent(filter, (IpPayloadEvent) e);
+ } else if (e instanceof MediaEvent) {
+ testMediaEvent(filter, (MediaEvent) e);
+ } else if (e instanceof MmtpRecordEvent) {
+ testMmtpRecordEvent(filter, (MmtpRecordEvent) e);
+ } else if (e instanceof PesEvent) {
+ testPesEvent(filter, (PesEvent) e);
+ } else if (e instanceof SectionEvent) {
+ testSectionEvent(filter, (SectionEvent) e);
+ } else if (e instanceof TemiEvent) {
+ testTemiEvent(filter, (TemiEvent) e);
+ } else if (e instanceof TsRecordEvent) {
+ testTsRecordEvent(filter, (TsRecordEvent) e);
+ }
+ }
+ }
@Override
public void onFilterStatusChanged(Filter filter, int status) {}
};
}
+ private void testDownloadEvent(Filter filter, DownloadEvent e) {
+ e.getItemId();
+ e.getMpuSequenceNumber();
+ e.getItemFragmentIndex();
+ e.getLastItemFragmentIndex();
+ long length = e.getDataLength();
+ if (length > 0) {
+ byte[] buffer = new byte[(int) length];
+ assertNotEquals(0, filter.read(buffer, 0, length));
+ }
+ }
+
+ private void testIpPayloadEvent(Filter filter, IpPayloadEvent e) {
+ long length = e.getDataLength();
+ if (length > 0) {
+ byte[] buffer = new byte[(int) length];
+ assertNotEquals(0, filter.read(buffer, 0, length));
+ }
+ }
+
+ private void testMediaEvent(Filter filter, MediaEvent e) {
+ e.getStreamId();
+ e.isPtsPresent();
+ e.getPts();
+ e.getDataLength();
+ e.getOffset();
+ e.getLinearBlock();
+ e.isSecureMemory();
+ e.getAvDataId();
+ e.getAudioHandle();
+ e.getMpuSequenceNumber();
+ e.isPrivateData();
+ AudioDescriptor ad = e.getExtraMetaData();
+ if (ad != null) {
+ ad.getAdFade();
+ ad.getAdPan();
+ ad.getAdVersionTextTag();
+ ad.getAdGainCenter();
+ ad.getAdGainFront();
+ ad.getAdGainSurround();
+ }
+ }
+
+ private void testMmtpRecordEvent(Filter filter, MmtpRecordEvent e) {
+ e.getScHevcIndexMask();
+ e.getDataLength();
+ }
+
+ private void testPesEvent(Filter filter, PesEvent e) {
+ e.getStreamId();
+ e.getMpuSequenceNumber();
+ long length = e.getDataLength();
+ if (length > 0) {
+ byte[] buffer = new byte[(int) length];
+ assertNotEquals(0, filter.read(buffer, 0, length));
+ }
+ }
+
+ private void testSectionEvent(Filter filter, SectionEvent e) {
+ e.getTableId();
+ e.getVersion();
+ e.getSectionNumber();
+ long length = e.getDataLength();
+ if (length > 0) {
+ byte[] buffer = new byte[(int) length];
+ assertNotEquals(0, filter.read(buffer, 0, length));
+ }
+ }
+
+ private void testTemiEvent(Filter filter, TemiEvent e) {
+ e.getPts();
+ e.getDescriptorTag();
+ e.getDescriptorData();
+ }
+
+ private void testTsRecordEvent(Filter filter, TsRecordEvent e) {
+ e.getPacketId();
+ e.getTsIndexMask();
+ e.getScIndexMask();
+ e.getDataLength();
+ }
+
private OnRecordStatusChangedListener getRecordListener() {
return new OnRecordStatusChangedListener() {
@Override
@@ -496,7 +618,14 @@
public void onAnalogSifStandardReported(int sif) {}
@Override
- public void onAtsc3PlpInfosReported(Atsc3PlpInfo[] atsc3PlpInfos) {}
+ public void onAtsc3PlpInfosReported(Atsc3PlpInfo[] atsc3PlpInfos) {
+ for (Atsc3PlpInfo info : atsc3PlpInfos) {
+ if (info != null) {
+ info.getPlpId();
+ info.getLlsFlag();
+ }
+ }
+ }
@Override
public void onHierarchyReported(int hierarchy) {}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
index db5458f..adc1b06 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
@@ -49,8 +49,10 @@
// Cleanup old tests
// These are removed on uninstall anyway but just in case...
File[] toRemove = sDumpDirectory.listFiles();
- for (File file : toRemove) {
- deleteContentsAndDir(file);
+ if (toRemove != null && toRemove.length > 0) {
+ for (File file : toRemove) {
+ deleteContentsAndDir(file);
+ }
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 66f1cb3..86e674e 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -2928,16 +2928,7 @@
mInstrumentation.waitForIdleSync();
assertTrue(mTextView.isFocused());
- // Tab should not cause focus to leave the multiline textfield.
- CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_TAB);
- mInstrumentation.waitForIdleSync();
- assertTrue(mTextView.isFocused());
-
- // Tab on the singleline TextView should.
- mActivityRule.runOnUiThread(() -> {
- mTextView.setSingleLine(true);
- });
- mInstrumentation.waitForIdleSync();
+ // Tab should
CtsKeyEventUtil.sendKeyDownUp(mInstrumentation, mTextView, KeyEvent.KEYCODE_TAB);
mInstrumentation.waitForIdleSync();
assertFalse(mTextView.isFocused());
diff --git a/tests/tests/wifi/Android.bp b/tests/tests/wifi/Android.bp
index 22db9ad..d3ac794 100644
--- a/tests/tests/wifi/Android.bp
+++ b/tests/tests/wifi/Android.bp
@@ -26,6 +26,7 @@
srcs: [ "src/**/*.java" ],
static_libs: [
+ "androidx.test.rules",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"junit",
@@ -40,5 +41,10 @@
"mts",
],
+
+ data: [
+ ":CtsWifiLocationTestApp",
+ ],
+
platform_apis: true,
}
diff --git a/tests/tests/wifi/AndroidManifest.xml b/tests/tests/wifi/AndroidManifest.xml
index 4ab0e09..0e40a37 100644
--- a/tests/tests/wifi/AndroidManifest.xml
+++ b/tests/tests/wifi/AndroidManifest.xml
@@ -29,6 +29,7 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -39,6 +40,7 @@
(as opposed to HTTPS). -->
<application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
+ <activity android:name=".WaitForResultActivity" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/wifi/AndroidTest.xml b/tests/tests/wifi/AndroidTest.xml
index a258805..dd5246c 100644
--- a/tests/tests/wifi/AndroidTest.xml
+++ b/tests/tests/wifi/AndroidTest.xml
@@ -32,9 +32,22 @@
<option name="run-command" value="settings put secure location_mode 3" />
<option name="teardown-command" value="settings put secure location_mode 0" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts/wifi" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts/wifi"/>
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsWifiLocationTestApp.apk->/data/local/tmp/cts/wifi/CtsWifiLocationTestApp.apk" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.wifi.cts" />
<option name="hidden-api-checks" value="false" />
<option name="isolated-storage" value="false" />
</test>
+
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <option name="mainline-module-package-name" value="com.google.android.wifi" />
+ <option name="mainline-module-package-name" value="com.google.android.tethering" />
+ </object>
</configuration>
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/Android.bp b/tests/tests/wifi/CtsWifiLocationTestApp/Android.bp
new file mode 100644
index 0000000..bc239fa
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/Android.bp
@@ -0,0 +1,26 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "CtsWifiLocationTestApp",
+
+ // Include both the 32 and 64 bit versions
+ compile_multilib: "both",
+
+ sdk_version: "test_current",
+
+ srcs: [
+ "src/**/*.java"
+ ],
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/AndroidManifest.xml b/tests/tests/wifi/CtsWifiLocationTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..96fb0a6
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.net.wifi.cts.app">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+
+ <application android:label="CtsWifiLocationTestApp">
+ <activity
+ android:name=".ScheduleJobActivity"
+ android:exported="true" />
+ <activity
+ android:name=".TriggerScanAndReturnStatusActivity"
+ android:exported="true" />
+ <service
+ android:name=".TriggerScanAndReturnStatusService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true" />
+ <activity
+ android:name=".RetrieveScanResultsAndReturnStatusActivity"
+ android:exported="true" />
+ <service
+ android:name=".RetrieveScanResultsAndReturnStatusService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true" />
+ <activity
+ android:name=".RetrieveConnectionInfoAndReturnStatusActivity"
+ android:exported="true" />
+ <service
+ android:name=".RetrieveConnectionInfoAndReturnStatusService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
+ android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveConnectionInfoAndReturnStatusActivity.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveConnectionInfoAndReturnStatusActivity.java
new file mode 100644
index 0000000..48505ca
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveConnectionInfoAndReturnStatusActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * An activity that retrieves connection info and returns status.
+ */
+public class RetrieveConnectionInfoAndReturnStatusActivity extends Activity {
+ private static final String TAG = "RetrieveConnectionInfoAndReturnStatusActivity";
+ private static final String SCAN_STATUS_EXTRA = "android.net.wifi.cts.app.extra.STATUS";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ WifiManager wifiManager = getSystemService(WifiManager.class);
+ boolean succeeded;
+ try {
+ succeeded = !wifiManager.getConnectionInfo().getSSID().equals(WifiManager.UNKNOWN_SSID);
+ } catch (SecurityException e) {
+ succeeded = false;
+ }
+ if (succeeded) {
+ Log.v(TAG, "SSID from connection info retrieval succeeded");
+ } else {
+ Log.v(TAG, "Failed to retrieve SSID from connection info");
+ }
+ setResult(RESULT_OK, new Intent().putExtra(SCAN_STATUS_EXTRA, succeeded));
+ finish();
+ }
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveConnectionInfoAndReturnStatusService.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveConnectionInfoAndReturnStatusService.java
new file mode 100644
index 0000000..2308054
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveConnectionInfoAndReturnStatusService.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts.app;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.net.wifi.WifiManager;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * A service that retrieves scan results and returns status.
+ */
+public class RetrieveConnectionInfoAndReturnStatusService extends JobService {
+ private static final String TAG = "RetrieveConnectionInfoAndReturnStatusService";
+ private static final String RESULT_RECEIVER_EXTRA =
+ "android.net.wifi.cts.app.extra.RESULT_RECEIVER";
+
+ @Override
+ public boolean onStartJob(JobParameters jobParameters) {
+ ResultReceiver resultReceiver =
+ jobParameters.getTransientExtras().getParcelable(RESULT_RECEIVER_EXTRA);
+ WifiManager wifiManager = getSystemService(WifiManager.class);
+ boolean succeeded;
+ try {
+ succeeded = !wifiManager.getConnectionInfo().getSSID().equals(WifiManager.UNKNOWN_SSID);
+ } catch (SecurityException e) {
+ succeeded = false;
+ }
+ if (succeeded) {
+ Log.v(TAG, "SSID from connection info retrieval succeeded");
+ } else {
+ Log.v(TAG, "Failed to retrieve SSID from connection info");
+ }
+ resultReceiver.send(succeeded ? 1 : 0, null);
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return false;
+ }
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveScanResultsAndReturnStatusActivity.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveScanResultsAndReturnStatusActivity.java
new file mode 100644
index 0000000..2dd3810
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveScanResultsAndReturnStatusActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * An activity that retrieves scan results and returns status.
+ */
+public class RetrieveScanResultsAndReturnStatusActivity extends Activity {
+ private static final String TAG = "RetrieveScanResultsAndReturnStatusActivity";
+ private static final String SCAN_STATUS_EXTRA = "android.net.wifi.cts.app.extra.STATUS";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ WifiManager wifiManager = getSystemService(WifiManager.class);
+ boolean succeeded;
+ try {
+ succeeded = !wifiManager.getScanResults().isEmpty();
+ } catch (SecurityException e) {
+ succeeded = false;
+ }
+ if (succeeded) {
+ Log.v(TAG, "Scan results retrieval succeeded");
+ } else {
+ Log.v(TAG, "Failed to retrieve scan results");
+ }
+ setResult(RESULT_OK, new Intent().putExtra(SCAN_STATUS_EXTRA, succeeded));
+ finish();
+ }
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveScanResultsAndReturnStatusService.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveScanResultsAndReturnStatusService.java
new file mode 100644
index 0000000..4918e39
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/RetrieveScanResultsAndReturnStatusService.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts.app;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.net.wifi.WifiManager;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * A service that triggers a wifi scan and returns status.
+ */
+public class RetrieveScanResultsAndReturnStatusService extends JobService {
+ private static final String TAG = "RetrieveScanResultsAndReturnStatusService";
+ private static final String RESULT_RECEIVER_EXTRA =
+ "android.net.wifi.cts.app.extra.RESULT_RECEIVER";
+
+ @Override
+ public boolean onStartJob(JobParameters jobParameters) {
+ ResultReceiver resultReceiver =
+ jobParameters.getTransientExtras().getParcelable(RESULT_RECEIVER_EXTRA);
+ WifiManager wifiManager = getSystemService(WifiManager.class);
+ boolean succeeded;
+ try {
+ succeeded = !wifiManager.getScanResults().isEmpty();
+ } catch (SecurityException e) {
+ succeeded = false;
+ }
+ if (succeeded) {
+ Log.v(TAG, "Scan results retrieval succeeded");
+ } else {
+ Log.v(TAG, "Failed to retrieve scan results");
+ }
+ resultReceiver.send(succeeded ? 1 : 0, null);
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return false;
+ }
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/ScheduleJobActivity.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/ScheduleJobActivity.java
new file mode 100644
index 0000000..b447878
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/ScheduleJobActivity.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts.app;
+
+import android.app.Activity;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * An activity that can be used to schedule a job inside the app.
+ *
+ * Note: This trampoline is needed since we cannot schedule a job to an app from outside the app.
+ */
+public class ScheduleJobActivity extends Activity {
+ private static final String TAG = "ScheduleJobActivity";
+ public static final int JOB_ID = 1;
+ private static final String RESULT_RECEIVER_EXTRA =
+ "android.net.wifi.cts.app.extra.RESULT_RECEIVER";
+ private static final String SERVICE_COMPONENT_EXTRA =
+ "android.net.wifi.cts.app.extra.SERVICE_COMPONENT";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ ComponentName serviceComponentName =
+ getIntent().getParcelableExtra(SERVICE_COMPONENT_EXTRA);
+ ResultReceiver resultReceiver = getIntent().getParcelableExtra(RESULT_RECEIVER_EXTRA);
+
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(RESULT_RECEIVER_EXTRA, resultReceiver);
+ JobInfo jobInfo = new JobInfo.Builder(JOB_ID, serviceComponentName)
+ .setTransientExtras(bundle)
+ .setMinimumLatency(1)
+ .setOverrideDeadline(1)
+ .build();
+ JobScheduler jobScheduler = getSystemService(JobScheduler.class);
+ jobScheduler.schedule(jobInfo);
+
+ Log.v(TAG,"Job scheduled: " + jobInfo);
+
+ finish();
+ }
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/TriggerScanAndReturnStatusActivity.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/TriggerScanAndReturnStatusActivity.java
new file mode 100644
index 0000000..a6fbdfa
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/TriggerScanAndReturnStatusActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * An activity that triggers a wifi scan and returns status.
+ */
+public class TriggerScanAndReturnStatusActivity extends Activity {
+ private static final String TAG = "TriggerScanAndReturnStatusActivity";
+ private static final String SCAN_STATUS_EXTRA = "android.net.wifi.cts.app.extra.STATUS";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ WifiManager wifiManager = getSystemService(WifiManager.class);
+ boolean succeeded;
+ try {
+ succeeded = wifiManager.startScan();
+ } catch (SecurityException e) {
+ succeeded = false;
+ }
+ if (succeeded) {
+ Log.v(TAG, "Scan trigger succeeded");
+ } else {
+ Log.v(TAG, "Failed to trigger scan");
+ }
+ setResult(RESULT_OK, new Intent().putExtra(SCAN_STATUS_EXTRA, succeeded));
+ finish();
+ }
+}
diff --git a/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/TriggerScanAndReturnStatusService.java b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/TriggerScanAndReturnStatusService.java
new file mode 100644
index 0000000..2f6a2ed
--- /dev/null
+++ b/tests/tests/wifi/CtsWifiLocationTestApp/src/android/net/wifi/cts/app/TriggerScanAndReturnStatusService.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts.app;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.net.wifi.WifiManager;
+import android.os.ResultReceiver;
+import android.util.Log;
+
+/**
+ * A service that triggers a wifi scan and returns status.
+ */
+public class TriggerScanAndReturnStatusService extends JobService {
+ private static final String TAG = "TriggerScanAndReturnStatusService";
+ private static final String RESULT_RECEIVER_EXTRA =
+ "android.net.wifi.cts.app.extra.RESULT_RECEIVER";
+
+ @Override
+ public boolean onStartJob(JobParameters jobParameters) {
+ ResultReceiver resultReceiver =
+ jobParameters.getTransientExtras().getParcelable(RESULT_RECEIVER_EXTRA);
+ WifiManager wifiManager = getSystemService(WifiManager.class);
+ boolean succeeded;
+ try {
+ succeeded = wifiManager.startScan();
+ } catch (SecurityException e) {
+ succeeded = false;
+ }
+ if (succeeded) {
+ Log.v(TAG, "Scan trigger succeeded");
+ } else {
+ Log.v(TAG, "Failed to trigger scan");
+ }
+ resultReceiver.send(succeeded ? 1 : 0, null);
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return false;
+ }
+}
diff --git a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
index a476658..8df75b1 100644
--- a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -69,7 +69,8 @@
private static final String TAG = "WifiAwareCtsTests";
// wait for Wi-Fi Aware state changes & network requests callbacks
- static private final int WAIT_FOR_AWARE_CHANGE_SECS = 15; // 15 seconds
+ private static final int WAIT_FOR_AWARE_CHANGE_SECS = 15; // 15 seconds
+ private static final int INTERVAL_BETWEEN_TESTS_SECS = 3; // 3 seconds
private static final int MIN_DISTANCE_MM = 1 * 1000;
private static final int MAX_DISTANCE_MM = 3 * 1000;
private static final byte[] PMK_VALID = "01234567890123456789012345678901".getBytes();
@@ -412,6 +413,7 @@
}
super.tearDown();
+ Thread.sleep(INTERVAL_BETWEEN_TESTS_SECS * 1000);
}
/**
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java
index 1977378..d7e4a23 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ScanResultTest.java
@@ -299,9 +299,10 @@
return;
}
- // This test case should run while connected to Wifi
final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
assertThat(wifiInfo).isNotNull();
+ assertWithMessage("Wifi should be connected!")
+ .that(wifiInfo.getBSSID()).isNotNull();
ScanResult currentNetwork = null;
for (int i = 0; i < SCAN_FIND_BSSID_MAX_RETRY_COUNT; i++) {
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WaitForResultActivity.java b/tests/tests/wifi/src/android/net/wifi/cts/WaitForResultActivity.java
new file mode 100644
index 0000000..f4f746c
--- /dev/null
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WaitForResultActivity.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 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.net.wifi.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Activity;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.ResultReceiver;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An Activity that can start another Activity and wait for its result.
+ */
+public class WaitForResultActivity extends Activity {
+ private final Object mStatusLock = new Object();
+ private CountDownLatch mLatch;
+ private boolean mStatus = false;
+
+ private static final int REQUEST_CODE_WAIT_FOR_RESULT = 1;
+ private static final String WIFI_LOCATION_TEST_APP_LOCATION_STATUS_EXTRA =
+ "android.net.wifi.cts.app.extra.STATUS";
+
+ public void startActivityToWaitForResult(@NonNull ComponentName componentName) {
+ mLatch = new CountDownLatch(1);
+ synchronized (mStatusLock) {
+ mStatus = false;
+ }
+ Intent intent = new Intent()
+ .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
+ .setComponent(componentName);
+ startActivityForResult(intent, REQUEST_CODE_WAIT_FOR_RESULT);
+ }
+
+ @NonNull
+ public boolean waitForActivityResult(long timeoutMillis)
+ throws InterruptedException {
+ assertThat(mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS)).isTrue();
+ synchronized (mStatusLock) {
+ return mStatus;
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ if (requestCode == REQUEST_CODE_WAIT_FOR_RESULT) {
+ assertThat(resultCode).isEqualTo(RESULT_OK);
+ assertThat(data.hasExtra(WIFI_LOCATION_TEST_APP_LOCATION_STATUS_EXTRA)).isTrue();
+ synchronized (mStatusLock) {
+ mStatus = data.getBooleanExtra(
+ WIFI_LOCATION_TEST_APP_LOCATION_STATUS_EXTRA, false);
+ }
+ mLatch.countDown();
+ } else {
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ private static final String WIFI_LOCATION_TEST_APP_PACKAGE_NAME =
+ "android.net.wifi.cts.app";
+ private static final String WIFI_LOCATION_TEST_APP_SCHEDULE_JOB_ACTIVITY =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".ScheduleJobActivity";
+ private static final String RESULT_RECEIVER_EXTRA =
+ "android.net.wifi.cts.app.extra.RESULT_RECEIVER";
+ private static final String SERVICE_COMPONENT_EXTRA =
+ "android.net.wifi.cts.app.extra.SERVICE_COMPONENT";
+
+ private static ResultReceiver convertToGeneric(ResultReceiver receiver) {
+ Parcel parcel = Parcel.obtain();
+ receiver.writeToParcel(parcel,0);
+ parcel.setDataPosition(0);
+ ResultReceiver receiverGeneric = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return receiverGeneric;
+ }
+
+ public void startServiceToWaitForResult(@NonNull ComponentName serviceComponent) {
+ mLatch = new CountDownLatch(1);
+ synchronized (mStatusLock) {
+ mStatus = false;
+ }
+ ResultReceiver resultReceiver = new ResultReceiver(null) {
+ @Override
+ public void onReceiveResult(int resultCode, Bundle data) {
+ synchronized (mStatusLock) {
+ mStatus = resultCode == 1;
+ }
+ mLatch.countDown();
+ }
+ };
+ Intent intent = new Intent()
+ .setComponent(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_SCHEDULE_JOB_ACTIVITY))
+ .putExtra(RESULT_RECEIVER_EXTRA, convertToGeneric(resultReceiver))
+ .putExtra(SERVICE_COMPONENT_EXTRA, serviceComponent);
+ startActivity(intent);
+ }
+
+ @NonNull
+ public boolean waitForServiceResult(long timeoutMillis)
+ throws InterruptedException {
+ assertThat(mLatch.await(timeoutMillis, TimeUnit.MILLISECONDS)).isTrue();
+ synchronized (mStatusLock) {
+ return mStatus;
+ }
+ }
+
+}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
new file mode 100644
index 0000000..0ca73fc
--- /dev/null
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiLocationInfoTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts;
+
+import static android.Manifest.permission.ACCESS_BACKGROUND_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+/**
+ * Tests location sensitive APIs exposed by Wi-Fi.
+ * Ensures that permissions on these APIs are properly enforced.
+ */
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class WifiLocationInfoTest {
+ private static final String TAG = "WifiLocationInfoTest";
+
+ private static final String WIFI_LOCATION_TEST_APP_APK_PATH =
+ "/data/local/tmp/cts/wifi/CtsWifiLocationTestApp.apk";
+ private static final String WIFI_LOCATION_TEST_APP_PACKAGE_NAME =
+ "android.net.wifi.cts.app";
+ private static final String WIFI_LOCATION_TEST_APP_TRIGGER_SCAN_ACTIVITY =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".TriggerScanAndReturnStatusActivity";
+ private static final String WIFI_LOCATION_TEST_APP_TRIGGER_SCAN_SERVICE =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".TriggerScanAndReturnStatusService";
+ private static final String WIFI_LOCATION_TEST_APP_RETRIEVE_SCAN_RESULTS_ACTIVITY =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveScanResultsAndReturnStatusActivity";
+ private static final String WIFI_LOCATION_TEST_APP_RETRIEVE_SCAN_RESULTS_SERVICE =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveScanResultsAndReturnStatusService";
+ private static final String WIFI_LOCATION_TEST_APP_RETRIEVE_CONNECTION_INFO_ACTIVITY =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveConnectionInfoAndReturnStatusActivity";
+ private static final String WIFI_LOCATION_TEST_APP_RETRIEVE_CONNECTION_INFO_SERVICE =
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME + ".RetrieveConnectionInfoAndReturnStatusService";
+
+ private static final int DURATION_MS = 10_000;
+
+ @Rule
+ public final ActivityTestRule<WaitForResultActivity> mActivityRule =
+ new ActivityTestRule<>(WaitForResultActivity.class);
+
+ private Context mContext;
+ private WifiManager mWifiManager;
+ private boolean mWasVerboseLoggingEnabled;
+ private boolean mWasScanThrottleEnabled;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ // skip the test if WiFi is not supported
+ assumeTrue(WifiFeature.isWifiSupported(mContext));
+
+ mWifiManager = mContext.getSystemService(WifiManager.class);
+ assertThat(mWifiManager).isNotNull();
+
+ installApp(WIFI_LOCATION_TEST_APP_APK_PATH);
+
+ // turn on verbose logging for tests
+ mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.isVerboseLoggingEnabled());
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.setVerboseLoggingEnabled(true));
+ // Disable scan throttling for tests.
+ mWasScanThrottleEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.isScanThrottleEnabled());
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.setScanThrottleEnabled(false));
+
+ if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+ PollingCheck.check("Wifi not enabled", DURATION_MS, () -> mWifiManager.isWifiEnabled());
+ List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.getConfiguredNetworks());
+ assertWithMessage("Need at least one saved network").that(savedNetworks).isNotEmpty();
+ // Wait for wifi is to be connected
+ PollingCheck.check(
+ "Wifi not connected",
+ DURATION_MS,
+ () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (!WifiFeature.isWifiSupported(mContext)) return;
+
+ uninstallApp(WIFI_LOCATION_TEST_APP_PACKAGE_NAME);
+
+ if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.setScanThrottleEnabled(mWasScanThrottleEnabled));
+ ShellIdentityUtils.invokeWithShellPermissions(
+ () -> mWifiManager.setVerboseLoggingEnabled(mWasVerboseLoggingEnabled));
+ }
+
+ private void setWifiEnabled(boolean enable) throws Exception {
+ // now trigger the change using shell commands.
+ SystemUtil.runShellCommand("svc wifi " + (enable ? "enable" : "disable"));
+ }
+
+ private void turnScreenOn() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "input keyevent KEYCODE_WAKEUP");
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(""
+ + "wm dismiss-keyguard");
+ // Since the screen on/off intent is ordered, they will not be sent right now.
+ Thread.sleep(2_000);
+ }
+
+ private void turnScreenOff() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
+ "input keyevent KEYCODE_SLEEP");
+ // Since the screen on/off intent is ordered, they will not be sent right now.
+ Thread.sleep(2_000);
+ }
+
+ private void installApp(String apk) throws InterruptedException {
+ String installResult = SystemUtil.runShellCommand("pm install -r -d " + apk);
+ Thread.sleep(10_000);
+ assertThat(installResult.trim()).isEqualTo("Success");
+ }
+
+ private void uninstallApp(String pkg) throws InterruptedException {
+ String uninstallResult = SystemUtil.runShellCommand(
+ "pm uninstall " + pkg);
+ Thread.sleep(10_000);
+ assertThat(uninstallResult.trim()).isEqualTo("Success");
+ }
+
+ private void startFgActivityAndAssertStatusIs(
+ ComponentName componentName, boolean status) throws Exception {
+ turnScreenOn();
+
+ WaitForResultActivity activity = mActivityRule.getActivity();
+ activity.startActivityToWaitForResult(componentName);
+ assertThat(activity.waitForActivityResult(DURATION_MS)).isEqualTo(status);
+ }
+
+ private void startBgServiceAndAssertStatusIs(
+ ComponentName componentName, boolean status) throws Exception {
+ turnScreenOff();
+
+ WaitForResultActivity activity = mActivityRule.getActivity();
+ activity.startServiceToWaitForResult(componentName);
+ assertThat(activity.waitForServiceResult(DURATION_MS)).isEqualTo(status);
+ }
+
+ private void triggerScanFgActivityAndAssertStatusIs(boolean status) throws Exception {
+ startFgActivityAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_TRIGGER_SCAN_ACTIVITY), status);
+ }
+
+ private void triggerScanBgServiceAndAssertStatusIs(boolean status) throws Exception {
+ startBgServiceAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_TRIGGER_SCAN_SERVICE), status);
+ }
+
+ private void retrieveScanResultsFgActivityAndAssertStatusIs(boolean status) throws Exception {
+ startFgActivityAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_RETRIEVE_SCAN_RESULTS_ACTIVITY), status);
+ }
+
+ private void retrieveScanResultsBgServiceAndAssertStatusIs(boolean status) throws Exception {
+ startBgServiceAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_RETRIEVE_SCAN_RESULTS_SERVICE), status);
+ }
+
+ private void retrieveConnectionInfoFgActivityAndAssertStatusIs(boolean status)
+ throws Exception {
+ startFgActivityAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_RETRIEVE_CONNECTION_INFO_ACTIVITY), status);
+ }
+
+ private void retrieveConnectionInfoBgServiceAndAssertStatusIs(boolean status) throws Exception {
+ startBgServiceAndAssertStatusIs(new ComponentName(WIFI_LOCATION_TEST_APP_PACKAGE_NAME,
+ WIFI_LOCATION_TEST_APP_RETRIEVE_CONNECTION_INFO_SERVICE), status);
+ }
+
+ @Test
+ public void testScanTriggerNotAllowedForForegroundActivityWithNoLocationPermission()
+ throws Exception {
+ triggerScanFgActivityAndAssertStatusIs(false);
+ }
+
+ @Test
+ public void testScanTriggerAllowedForForegroundActivityWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ triggerScanFgActivityAndAssertStatusIs(true);
+ }
+
+ @Test
+ public void testScanTriggerAllowedForBackgroundServiceWithBackgroundLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_BACKGROUND_LOCATION);
+ triggerScanBgServiceAndAssertStatusIs(true);
+ }
+
+ @Test
+ public void testScanTriggerNotAllowedForBackgroundServiceWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ triggerScanBgServiceAndAssertStatusIs(false);
+ }
+
+ @Test
+ public void testScanResultsRetrievalNotAllowedForForegroundActivityWithNoLocationPermission()
+ throws Exception {
+ retrieveScanResultsFgActivityAndAssertStatusIs(false);
+ }
+
+ @Test
+ public void testScanResultsRetrievalAllowedForForegroundActivityWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ retrieveScanResultsFgActivityAndAssertStatusIs(true);
+ }
+
+ @Test
+ public void testScanResultsRetrievalAllowedForBackgroundServiceWithBackgroundLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_BACKGROUND_LOCATION);
+ retrieveScanResultsBgServiceAndAssertStatusIs(true);
+ }
+
+ @Test
+ public void testScanResultsRetrievalNotAllowedForBackgroundServiceWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ retrieveScanResultsBgServiceAndAssertStatusIs(false);
+ }
+
+ @Test
+ public void testConnectionInfoRetrievalNotAllowedForForegroundActivityWithNoLocationPermission()
+ throws Exception {
+ retrieveConnectionInfoFgActivityAndAssertStatusIs(false);
+ }
+
+ @Test
+ public void testConnectionInfoRetrievalAllowedForForegroundActivityWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ retrieveConnectionInfoFgActivityAndAssertStatusIs(true);
+ }
+
+ @Test
+ public void
+ testConnectionInfoRetrievalAllowedForBackgroundServiceWithBackgroundLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_BACKGROUND_LOCATION);
+ retrieveConnectionInfoBgServiceAndAssertStatusIs(true);
+ }
+
+ @Test
+ public void
+ testConnectionInfoRetrievalNotAllowedForBackgroundServiceWithFineLocationPermission()
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
+ WIFI_LOCATION_TEST_APP_PACKAGE_NAME, ACCESS_FINE_LOCATION);
+ retrieveConnectionInfoBgServiceAndAssertStatusIs(false);
+ }
+}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
index 71831de..58be4d8 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -140,10 +140,9 @@
// A full single scan duration is about 6-7 seconds if country code is set
// to US. If country code is set to world mode (00), we would expect a scan
// duration of roughly 8 seconds. So we set scan timeout as 9 seconds here.
- private static final int SCAN_TIMEOUT_MSEC = 9000;
- private static final int TIMEOUT_MSEC = 6000;
- private static final int WAIT_MSEC = 60;
+ private static final int SCAN_TEST_WAIT_DURATION_MS = 9000;
private static final int TEST_WAIT_DURATION_MS = 10_000;
+ private static final int WAIT_MSEC = 60;
private static final int DURATION_SCREEN_TOGGLE = 2000;
private static final int DURATION_SETTINGS_TOGGLE = 1_000;
private static final int WIFI_SCAN_TEST_INTERVAL_MILLIS = 60 * 1000;
@@ -351,7 +350,7 @@
private void waitForExpectedWifiState(boolean enabled) throws InterruptedException {
synchronized (mMySync) {
- long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+ long timeout = System.currentTimeMillis() + TEST_WAIT_DURATION_MS;
int expected = (enabled ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
while (System.currentTimeMillis() < timeout
&& mMySync.expectedState != expected) {
@@ -377,7 +376,7 @@
mMySync.expectedState = STATE_SCANNING;
mScanResults = null;
assertTrue(mWifiManager.startScan());
- long timeout = System.currentTimeMillis() + SCAN_TIMEOUT_MSEC;
+ long timeout = System.currentTimeMillis() + SCAN_TEST_WAIT_DURATION_MS;
while (System.currentTimeMillis() < timeout && mMySync.expectedState == STATE_SCANNING)
mMySync.wait(WAIT_MSEC);
}
@@ -386,7 +385,7 @@
private void waitForNetworkInfoState(NetworkInfo.State state) throws Exception {
synchronized (mMySync) {
if (mNetworkInfo.getState() == state) return;
- long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+ long timeout = System.currentTimeMillis() + TEST_WAIT_DURATION_MS;
while (System.currentTimeMillis() < timeout
&& mNetworkInfo.getState() != state)
mMySync.wait(WAIT_MSEC);
@@ -404,7 +403,7 @@
private void ensureNotNetworkInfoState(NetworkInfo.State state) throws Exception {
synchronized (mMySync) {
- long timeout = System.currentTimeMillis() + TIMEOUT_MSEC + WAIT_MSEC;
+ long timeout = System.currentTimeMillis() + TEST_WAIT_DURATION_MS + WAIT_MSEC;
while (System.currentTimeMillis() < timeout) {
assertNotEquals(mNetworkInfo.getState(), state);
mMySync.wait(WAIT_MSEC);
@@ -2077,8 +2076,8 @@
URL url = new URL("http://www.google.com/");
connection = (HttpURLConnection) url.openConnection();
connection.setInstanceFollowRedirects(false);
- connection.setConnectTimeout(TIMEOUT_MSEC);
- connection.setReadTimeout(TIMEOUT_MSEC);
+ connection.setConnectTimeout(TEST_WAIT_DURATION_MS);
+ connection.setReadTimeout(TEST_WAIT_DURATION_MS);
connection.setUseCaches(false);
InputStream stream = connection.getInputStream();
byte[] bytes = new byte[100];
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
index a7c4cc7..e17d022 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
@@ -70,13 +70,15 @@
private final Object mLock = new Object();
private final Object mUiLock = new Object();
private WifiConfiguration mTestNetwork;
+ private TestNetworkCallback mNetworkCallback;
private boolean mWasVerboseLoggingEnabled;
private boolean mWasScanThrottleEnabled;
private static final int DURATION = 10_000;
- private static final int DURATION_UI_INTERACTION = 15_000;
- private static final int DURATION_NETWORK_CONNECTION = 30_000;
+ private static final int DURATION_UI_INTERACTION = 25_000;
+ private static final int DURATION_NETWORK_CONNECTION = 60_000;
private static final int DURATION_SCREEN_TOGGLE = 2000;
+ private static final int SCAN_RETRY_CNT_TO_FIND_MATCHING_BSSID = 3;
@Override
protected void setUp() throws Exception {
@@ -130,6 +132,10 @@
}
if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
turnScreenOff();
+ // If there is failure, ensure we unregister the previous request.
+ if (mNetworkCallback != null) {
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ }
ShellIdentityUtils.invokeWithShellPermissions(
() -> mWifiManager.enableNetwork(mTestNetwork.networkId, false));
ShellIdentityUtils.invokeWithShellPermissions(
@@ -260,7 +266,6 @@
try {
mWifiManager.registerNetworkRequestMatchCallback(
Executors.newSingleThreadExecutor(), networkRequestMatchCallback);
- // now wait for the registration callback first.
mUiLock.wait(DURATION_UI_INTERACTION);
} catch (InterruptedException e) {
}
@@ -271,7 +276,6 @@
// 2. Wait for matching scan results
synchronized (mUiLock) {
try {
- // now wait for the registration callback first.
mUiLock.wait(DURATION_UI_INTERACTION);
} catch (InterruptedException e) {
}
@@ -290,7 +294,6 @@
// 4. Wait for connection success or abort.
synchronized (mUiLock) {
try {
- // now wait for the registration callback first.
mUiLock.wait(DURATION_UI_INTERACTION);
} catch (InterruptedException e) {
}
@@ -318,7 +321,7 @@
Thread uiThread = new Thread(() -> handleUiInteractions(shouldUserReject));
// File the network request & wait for the callback.
- TestNetworkCallback networkCallbackListener = new TestNetworkCallback(mLock);
+ mNetworkCallback = new TestNetworkCallback(mLock);
synchronized (mLock) {
try {
// File a request for wifi network.
@@ -327,7 +330,7 @@
.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(specifier)
.build(),
- networkCallbackListener);
+ mNetworkCallback);
// Wait for the request to reach the wifi stack before kick-starting the UI
// interactions.
Thread.sleep(100);
@@ -339,9 +342,9 @@
}
}
if (shouldUserReject) {
- assertTrue(networkCallbackListener.onUnavailableCalled);
+ assertTrue(mNetworkCallback.onUnavailableCalled);
} else {
- assertTrue(networkCallbackListener.onAvailableCalled);
+ assertTrue(mNetworkCallback.onAvailableCalled);
}
try {
@@ -352,7 +355,8 @@
}
// Release the request after the test.
- mConnectivityManager.unregisterNetworkCallback(networkCallbackListener);
+ mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
+ mNetworkCallback = null;
}
private void testSuccessfulConnectionWithSpecifier(WifiNetworkSpecifier specifier) {
@@ -447,25 +451,27 @@
* this matching may pick the wrong one.
*/
private ScanResult findScanResultMatchingSavedNetwork() {
- // Trigger a scan to get fresh scan results.
- TestScanResultsCallback scanResultsCallback = new TestScanResultsCallback(mLock);
- synchronized (mLock) {
- try {
- mWifiManager.registerScanResultsCallback(
- Executors.newSingleThreadExecutor(), scanResultsCallback);
- mWifiManager.startScan(new WorkSource(myUid()));
- // now wait for callback
- mLock.wait(DURATION_NETWORK_CONNECTION);
- } catch (InterruptedException e) {
- } finally {
- mWifiManager.unregisterScanResultsCallback(scanResultsCallback);
+ for (int i = 0; i < SCAN_RETRY_CNT_TO_FIND_MATCHING_BSSID; i++) {
+ // Trigger a scan to get fresh scan results.
+ TestScanResultsCallback scanResultsCallback = new TestScanResultsCallback(mLock);
+ synchronized (mLock) {
+ try {
+ mWifiManager.registerScanResultsCallback(
+ Executors.newSingleThreadExecutor(), scanResultsCallback);
+ mWifiManager.startScan(new WorkSource(myUid()));
+ // now wait for callback
+ mLock.wait(DURATION_NETWORK_CONNECTION);
+ } catch (InterruptedException e) {
+ } finally {
+ mWifiManager.unregisterScanResultsCallback(scanResultsCallback);
+ }
}
- }
- List<ScanResult> scanResults = mWifiManager.getScanResults();
- if (scanResults == null || scanResults.isEmpty()) fail("No scan results available");
- for (ScanResult scanResult : scanResults) {
- if (TextUtils.equals(scanResult.SSID, removeDoubleQuotes(mTestNetwork.SSID))) {
- return scanResult;
+ List<ScanResult> scanResults = mWifiManager.getScanResults();
+ if (scanResults == null || scanResults.isEmpty()) fail("No scan results available");
+ for (ScanResult scanResult : scanResults) {
+ if (TextUtils.equals(scanResult.SSID, removeDoubleQuotes(mTestNetwork.SSID))) {
+ return scanResult;
+ }
}
}
fail("No matching scan results found");
diff --git a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
index 554512c..4264d3f 100644
--- a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
+++ b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
@@ -145,9 +145,9 @@
// Performance numbers only make sense on real devices, so skip on non-real devices
public static boolean frankenDevice() throws IOException {
- String systemBrand = getProperty("ro.product.system.brand");
- String systemModel = getProperty("ro.product.system.model");
- String systemProduct = getProperty("ro.product.system.name");
+ String systemBrand = getProperty("ro.product.system_ext.brand");
+ String systemModel = getProperty("ro.product.system_ext.model");
+ String systemProduct = getProperty("ro.product.system_ext.name");
if (("Android".equals(systemBrand) || "generic".equals(systemBrand)) &&
(systemModel.startsWith("AOSP on ") || systemProduct.startsWith("aosp_"))) {
return true;