Merge "Add more role manager tests." into rvc-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index a5bde79..0a0226f 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -3577,8 +3577,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>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 663b54e..51a4810 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -4562,6 +4562,9 @@
<!-- HDR Capabilities test -->
<string name="tv_hdr_capabilities_test">HDR Capabilities Test</string>
+ <string name="tv_hdr_capabilities_test_step_hdr_display">HDR Display</string>
+ <string name="tv_hdr_capabilities_test_step_no_display">No Display</string>
+ <string name="tv_hdr_capabilities_test_step_non_hdr_display">Non HDR Display</string>
<string name="tv_hdr_capabilities_test_info">This test checks if
Display.getHdrCapabilities correctly reports the HDR capabilities of the display.
</string>
@@ -4595,7 +4598,12 @@
<string name="tv_display_modes_connect_1080p_display">
Connect a 1080p display and press the "%s" button, below.
</string>
-
+ <string name="tv_panel_display_modes_reported_are_supported">
+ The supported display modes are:\n%s\n\nAre all of the above display modes supported by the hardware?
+ </string>
+ <string name="tv_panel_display_modes_supported_are_reported">
+ Are there other modes which are supported by the hardware, but are not listed above?
+ </string>
<string name="overlay_view_text">Overlay View Dummy Text</string>
<string name="custom_rating">Example of input app specific custom rating.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index 4c6a270..1c9fc0f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -110,7 +110,7 @@
* Call this to create a test step where the test automatically evaluates whether
* an expected condition is satisfied.
*/
- protected View createAutoItem(int stringId) {
+ public View createAutoItem(int stringId) {
View item = mInflater.inflate(R.layout.tv_item, mItemList, false);
TextView instructions = (TextView) item.findViewById(R.id.instructions);
instructions.setText(stringId);
@@ -119,9 +119,21 @@
}
/**
+ * Call this to create a test step where the test automatically evaluates whether
+ * an expected condition is satisfied.
+ */
+ public View createAutoItem(CharSequence instructionCharSequence) {
+ View item = mInflater.inflate(R.layout.tv_item, mItemList, false);
+ TextView instructions = (TextView) item.findViewById(R.id.instructions);
+ instructions.setText(instructionCharSequence);
+ mItemList.addView(item);
+ return item;
+ }
+
+ /**
* Call this to create alternative choice for the previous test step.
*/
- protected View createButtonItem(int buttonTextId, View.OnClickListener l) {
+ public View createButtonItem(int buttonTextId, View.OnClickListener l) {
View item = mInflater.inflate(R.layout.tv_item, mItemList, false);
Button button = (Button) item.findViewById(R.id.user_action_button);
button.setVisibility(View.VISIBLE);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvUtil.java
new file mode 100644
index 0000000..6327d68
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvUtil.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 com.android.cts.verifier.tv;
+
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TvUtil {
+ public static final String LOG_TAG = "TvUtil";
+
+ public static boolean isHdmiSourceDevice() {
+ final int DEVICE_TYPE_HDMI_SOURCE = 4;
+ try {
+ return getHdmiDeviceType().contains(DEVICE_TYPE_HDMI_SOURCE);
+ } catch (Exception exception) {
+ Log.e(LOG_TAG, "Exception while looking up HDMI device type.", exception);
+ }
+ return false;
+ }
+
+ public static List<Integer> getHdmiDeviceType()
+ throws InvocationTargetException, IllegalAccessException, ClassNotFoundException,
+ NoSuchMethodException {
+ Method getStringMethod =
+ ClassLoader.getSystemClassLoader()
+ .loadClass("android.os.SystemProperties")
+ .getMethod("get", String.class);
+ String deviceTypesStr = (String) getStringMethod.invoke(null, "ro.hdmi.device_type");
+ if (deviceTypesStr.equals("")) {
+ return new ArrayList<>();
+ }
+ return Arrays.stream(deviceTypesStr.split(","))
+ .map(Integer::parseInt)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java
index 37c33a3..03c1930 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/AsyncTestStep.java
@@ -18,28 +18,29 @@
import android.view.View;
+import androidx.annotation.StringRes;
+
import com.android.cts.verifier.R;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
/**
- * Encapsulates the logic of an asynchronous test step, which displays a human instructions and a
- * button to start the test. For synchronous steps see {@link SyncTestStep}.
+ * Encapsulates the logic of an asynchronous test step, which displays human instructions and a
+ * button to start the test. For synchronous steps see {@link SyncTestStep}.
*/
-public abstract class AsyncTestStep extends TestStepBase {
+public abstract class AsyncTestStep extends OneButtonTestStep {
- public AsyncTestStep(TvAppVerifierActivity context) {
- super(context);
+ public AsyncTestStep(TvAppVerifierActivity context, @StringRes int stepNameStringId,
+ String instructionText, @StringRes int buttonStringId) {
+ super(context, stepNameStringId, instructionText, buttonStringId);
}
- /**
- * Runs the test logic, when finished calls {@link AsyncTestStep#done()}.
- */
+ /** Runs the test logic, when finished calls {@link AsyncTestStep#done()}. */
public abstract void runTestAsync();
@Override
protected void onButtonClickRunTest() {
// Disable the button, so the user can't run it twice.
- disableButton();
+ disableInteractivity();
showLoadingSpinner();
runTestAsync();
}
@@ -51,12 +52,12 @@
}
private void showLoadingSpinner() {
- View spinner = mViewItem.findViewById(R.id.loadingSpinner);
+ View spinner = mButtonView.findViewById(R.id.loadingSpinner);
spinner.setVisibility(View.VISIBLE);
}
private void hideLoadingSpinner() {
- View spinner = mViewItem.findViewById(R.id.loadingSpinner);
+ View spinner = mButtonView.findViewById(R.id.loadingSpinner);
spinner.setVisibility(View.INVISIBLE);
}
}
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 7123be7..b86c58b 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
@@ -16,6 +16,8 @@
package com.android.cts.verifier.tv.display;
+import android.content.Context;
+import android.hardware.display.DisplayManager;
import android.view.Display;
import androidx.annotation.StringRes;
@@ -34,12 +36,12 @@
/**
* Test to verify the HDR Capabilities API is correctly implemented.
*
- * This test checks if
- * <a href="https://developer.android.com/reference/android/view/Display.html#isHdr()">Display.isHdr()</a>
- * and
- * <a href="https://developer.android.com/reference/android/view/Display.html#getHdrCapabilities()">Display.getHdrCapabilities()</a>
- * return correct results when 1. HDR Display is connected, 2. non-HDR
- * Display is connected and 3. no display is connected.
+ * <p>This test checks if <a
+ * href="https://developer.android.com/reference/android/view/Display.html#isHdr()">Display.isHdr()</a>
+ * and <a
+ * href="https://developer.android.com/reference/android/view/Display.html#getHdrCapabilities()">Display.getHdrCapabilities()</a>
+ * return correct results when 1. HDR Display is connected, 2. non-HDR Display is connected and 3.
+ * no display is connected.
*/
public class DisplayHdrCapabilitiesTestActivity extends TvAppVerifierActivity {
private static final float MAX_EXPECTED_LUMINANCE = 10_000f;
@@ -49,8 +51,8 @@
@Override
protected void setInfoResources() {
- setInfoResources(R.string.tv_hdr_capabilities_test,
- R.string.tv_hdr_capabilities_test_info, -1);
+ setInfoResources(
+ R.string.tv_hdr_capabilities_test, R.string.tv_hdr_capabilities_test_info, -1);
}
@Override
@@ -72,32 +74,28 @@
private static class NonHdrDisplayTestStep extends SyncTestStep {
public NonHdrDisplayTestStep(TvAppVerifierActivity context) {
- super(context);
+ super(
+ context,
+ R.string.tv_hdr_capabilities_test_step_non_hdr_display,
+ getInstructionText(context),
+ getButtonStringId());
}
- @Override
- protected String getInstructionText() {
- return mContext.getString(R.string.tv_hdr_connect_no_hdr_display,
- mContext.getString(getButtonStringId()));
+ private static String getInstructionText(Context context) {
+ return context.getString(
+ R.string.tv_hdr_connect_no_hdr_display, context.getString(getButtonStringId()));
}
- @Override
- protected String getStepName() {
- return "Non HDR Display";
- }
-
- @Override
- protected @StringRes int getButtonStringId() {
+ private static @StringRes int getButtonStringId() {
return R.string.tv_start_test;
}
@Override
public void runTest() {
- Display display = mContext.getWindowManager().getDefaultDisplay();
- getAsserter()
- .withMessage("Display.isHdr()")
- .that(display.isHdr())
- .isFalse();
+ DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isFalse();
getAsserter()
.withMessage("Display.getHdrCapabilities()")
.that(display.getHdrCapabilities().getSupportedHdrTypes())
@@ -108,33 +106,29 @@
private static class HdrDisplayTestStep extends SyncTestStep {
public HdrDisplayTestStep(TvAppVerifierActivity context) {
- super(context);
+ super(
+ context,
+ R.string.tv_hdr_capabilities_test_step_hdr_display,
+ getInstructionText(context),
+ getButtonStringId());
}
- @Override
- protected String getInstructionText() {
- return mContext.getString(R.string.tv_hdr_connect_hdr_display,
- mContext.getString(getButtonStringId()));
+ private static String getInstructionText(Context context) {
+ return context.getString(
+ R.string.tv_hdr_connect_hdr_display, context.getString(getButtonStringId()));
}
- @Override
- protected String getStepName() {
- return "HDR Display";
- }
-
- @Override
- protected @StringRes int getButtonStringId() {
+ private static @StringRes int getButtonStringId() {
return R.string.tv_start_test;
}
@Override
public void runTest() {
- Display display = mContext.getWindowManager().getDefaultDisplay();
+ DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
- getAsserter()
- .withMessage("Display.isHdr()")
- .that(display.isHdr())
- .isTrue();
+ getAsserter().withMessage("Display.isHdr()").that(display.isHdr()).isTrue();
Display.HdrCapabilities hdrCapabilities = display.getHdrCapabilities();
@@ -144,12 +138,13 @@
getAsserter()
.withMessage("Display.getHdrCapabilities().getSupportedTypes()")
.that(supportedHdrTypes)
- .isEqualTo(new int[]{
- Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION,
- Display.HdrCapabilities.HDR_TYPE_HDR10,
- Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS,
- Display.HdrCapabilities.HDR_TYPE_HLG
- });
+ .isEqualTo(
+ new int[] {
+ Display.HdrCapabilities.HDR_TYPE_DOLBY_VISION,
+ Display.HdrCapabilities.HDR_TYPE_HDR10,
+ Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS,
+ Display.HdrCapabilities.HDR_TYPE_HLG
+ });
float maxLuminance = hdrCapabilities.getDesiredMaxLuminance();
getAsserter()
@@ -172,41 +167,44 @@
private static class NoDisplayTestStep extends AsyncTestStep {
public NoDisplayTestStep(TvAppVerifierActivity context) {
- super(context);
+ super(
+ context,
+ R.string.tv_hdr_capabilities_test_step_no_display,
+ getInstructionText(context),
+ getButtonStringId());
}
- @Override
- protected String getInstructionText() {
- return mContext.getString(R.string.tv_hdr_disconnect_display,
- mContext.getString(getButtonStringId()),
+ private static String getInstructionText(Context context) {
+ return context.getString(
+ R.string.tv_hdr_disconnect_display,
+ context.getString(getButtonStringId()),
DISPLAY_DISCONNECT_WAIT_TIME_SECONDS,
- DISPLAY_DISCONNECT_WAIT_TIME_SECONDS+1);
+ DISPLAY_DISCONNECT_WAIT_TIME_SECONDS + 1);
}
- @Override
- protected String getStepName() {
- return "No Display";
- }
-
- @Override
- protected @StringRes int getButtonStringId() {
+ private static @StringRes int getButtonStringId() {
return R.string.tv_start_test;
}
@Override
public void runTestAsync() {
// Wait for the user to disconnect the display.
- mContext.getPostTarget().postDelayed(() -> {
- try {
- // Verify the display APIs do not crash when the display is disconnected
- Display display = mContext.getWindowManager().getDefaultDisplay();
- display.isHdr();
- display.getHdrCapabilities();
- } catch (Exception e) {
- getAsserter().fail(Throwables.getStackTraceAsString(e));
- }
- done();
- }, Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis());
+ final long delay = Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis();
+ mContext.getPostTarget().postDelayed(this::runTest, delay);
+ }
+
+ private void runTest() {
+ try {
+ // Verify the display APIs do not crash when the display is disconnected
+ DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ display.isHdr();
+ display.getHdrCapabilities();
+ } catch (Exception e) {
+ getAsserter().fail(Throwables.getStackTraceAsString(e));
+ }
+ done();
}
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
index c9b4de1..71cea14 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/DisplayModesTestActivity.java
@@ -25,6 +25,7 @@
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.truth.Correspondence;
@@ -35,13 +36,15 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
* Test for verifying that the platform correctly reports display resolution and refresh rate. More
- * specifically Display.getMode() and Display.getSupportedModes() APIs are tested against reference
- * displays.
+ * specifically Display.getMode() and Display.getSupportedModes() APIs are tested. In the case for
+ * set-top boxes and TV dongles they are tested against reference displays. For TV panels they are
+ * tested against the hardware capabilities of the device.
*/
public class DisplayModesTestActivity extends TvAppVerifierActivity {
private static final int DISPLAY_DISCONNECT_WAIT_TIME_SECONDS = 5;
@@ -64,7 +67,6 @@
};
private TestSequence mTestSequence;
- private DisplayManager mDisplayManager;
@Override
protected void setInfoResources() {
@@ -74,15 +76,21 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
}
@Override
protected void createTestItems() {
List<TestStepBase> testSteps = new ArrayList<>();
- testSteps.add(new NoDisplayTestStep(this));
- testSteps.add(new Display2160pTestStep(this));
- testSteps.add(new Display1080pTestStep(this));
+ if (TvUtil.isHdmiSourceDevice()) {
+ // The device is a set-top box or a TV dongle
+ testSteps.add(new NoDisplayTestStep(this));
+ testSteps.add(new Display2160pTestStep(this));
+ testSteps.add(new Display1080pTestStep(this));
+ } else {
+ // The device is a TV Panel
+ testSteps.add(new TvPanelReportedModesAreSupportedTestStep(this));
+ testSteps.add(new TvPanelSupportedModesAreReportedTestStep(this));
+ }
mTestSequence = new TestSequence(this, testSteps);
mTestSequence.init();
}
@@ -92,101 +100,101 @@
return mTestSequence.getFailureDetails();
}
- private class NoDisplayTestStep extends AsyncTestStep {
+ private static class NoDisplayTestStep extends AsyncTestStep {
public NoDisplayTestStep(TvAppVerifierActivity context) {
- super(context);
+ super(
+ context,
+ R.string.tv_display_modes_test_step_no_display,
+ getInstructionText(context),
+ getButtonStringId());
}
- @Override
- protected String getStepName() {
- return mContext.getString(R.string.tv_display_modes_test_step_no_display);
- }
-
- @Override
- protected String getInstructionText() {
- return mContext.getString(
+ private static String getInstructionText(Context context) {
+ return context.getString(
R.string.tv_display_modes_disconnect_display,
- mContext.getString(getButtonStringId()),
+ context.getString(getButtonStringId()),
DISPLAY_DISCONNECT_WAIT_TIME_SECONDS,
DISPLAY_DISCONNECT_WAIT_TIME_SECONDS + 1);
}
- @Override
- protected @StringRes int getButtonStringId() {
+ private static @StringRes int getButtonStringId() {
return R.string.tv_start_test;
}
@Override
public void runTestAsync() {
- mContext.getPostTarget()
- .postDelayed(
- () -> {
- try {
- // Verify the display APIs do not crash when the display is
- // disconnected
- Display display =
- mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- display.getMode();
- display.getSupportedModes();
- } catch (Exception e) {
- getAsserter().fail(Throwables.getStackTraceAsString(e));
- }
- done();
- },
- Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis());
+ final long delay = Duration.ofSeconds(DISPLAY_DISCONNECT_WAIT_TIME_SECONDS).toMillis();
+ mContext.getPostTarget().postDelayed(this::runTest, delay);
+ }
+
+ private void runTest() {
+ try {
+ // Verify the display APIs do not crash when the display is disconnected
+ DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ display.getMode();
+ display.getSupportedModes();
+ } catch (Exception e) {
+ getAsserter().fail(Throwables.getStackTraceAsString(e));
+ }
+ done();
}
}
- private class Display2160pTestStep extends SyncTestStep {
+ private static class Display2160pTestStep extends SyncTestStep {
public Display2160pTestStep(TvAppVerifierActivity context) {
- super(context);
+ super(
+ context,
+ R.string.tv_display_modes_test_step_2160p,
+ getInstructionText(context),
+ getButtonStringId());
}
- @Override
- protected String getStepName() {
- return mContext.getString(R.string.tv_display_modes_test_step_2160p);
- }
-
- @Override
- protected String getInstructionText() {
- return mContext.getString(
+ private static String getInstructionText(Context context) {
+ return context.getString(
R.string.tv_display_modes_connect_2160p_display,
- mContext.getString(getButtonStringId()));
+ context.getString(getButtonStringId()));
}
- @Override
- protected @StringRes int getButtonStringId() {
+ private static @StringRes int getButtonStringId() {
return R.string.tv_start_test;
}
@Override
public void runTest() {
- Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
getAsserter()
.withMessage("Display.getMode()")
.about(MODE_SUBJECT_FACTORY)
.that(display.getMode())
- .isEquivalentTo(new Mode(3840, 2160, 60f), REFRESH_RATE_PRECISION);
+ .isEquivalentToAnyOf(
+ REFRESH_RATE_PRECISION,
+ new Mode(3840, 2160, 60f),
+ new Mode(3840, 2160, 50f));
- Mode[] expected2160pSupportedModes = new Mode[]{
- new Mode(720, 480, 60f),
- new Mode(720, 576, 50f),
- // 720p modes
- new Mode(1280, 720, 50f),
- new Mode(1280, 720, 60f),
- // 1080p modes
- new Mode(1920, 1080, 24f),
- new Mode(1920, 1080, 25f),
- new Mode(1920, 1080, 30f),
- new Mode(1920, 1080, 50f),
- new Mode(1920, 1080, 60f),
- // 2160p modes
- new Mode(3840, 2160, 24f),
- new Mode(3840, 2160, 25f),
- new Mode(3840, 2160, 30f),
- new Mode(3840, 2160, 50f),
- new Mode(3840, 2160, 60f)
- };
+ Mode[] expected2160pSupportedModes =
+ new Mode[] {
+ new Mode(720, 480, 60f),
+ new Mode(720, 576, 50f),
+ // 720p modes
+ new Mode(1280, 720, 50f),
+ new Mode(1280, 720, 60f),
+ // 1080p modes
+ new Mode(1920, 1080, 24f),
+ new Mode(1920, 1080, 25f),
+ new Mode(1920, 1080, 30f),
+ new Mode(1920, 1080, 50f),
+ new Mode(1920, 1080, 60f),
+ // 2160p modes
+ new Mode(3840, 2160, 24f),
+ new Mode(3840, 2160, 25f),
+ new Mode(3840, 2160, 30f),
+ new Mode(3840, 2160, 50f),
+ new Mode(3840, 2160, 60f)
+ };
getAsserter()
.withMessage("Display.getSupportedModes()")
.that(Arrays.asList(display.getSupportedModes()))
@@ -195,51 +203,54 @@
}
}
- private class Display1080pTestStep extends SyncTestStep {
+ private static class Display1080pTestStep extends SyncTestStep {
public Display1080pTestStep(TvAppVerifierActivity context) {
- super(context);
+ super(
+ context,
+ R.string.tv_display_modes_test_step_1080p,
+ getInstructionText(context),
+ getButtonStringId());
}
- @Override
- protected String getStepName() {
- return mContext.getString(R.string.tv_display_modes_test_step_1080p);
- }
-
- @Override
- protected String getInstructionText() {
- return mContext.getString(
+ private static String getInstructionText(Context context) {
+ return context.getString(
R.string.tv_display_modes_connect_1080p_display,
- mContext.getString(getButtonStringId()));
+ context.getString(getButtonStringId()));
}
- @Override
- protected @StringRes int getButtonStringId() {
+ private static @StringRes int getButtonStringId() {
return R.string.tv_start_test;
}
@Override
public void runTest() {
- Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ DisplayManager displayManager =
+ (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
getAsserter()
.withMessage("Display.getMode()")
.about(MODE_SUBJECT_FACTORY)
.that(display.getMode())
- .isEquivalentTo(new Mode(1920, 1080, 60f), REFRESH_RATE_PRECISION);
+ .isEquivalentToAnyOf(
+ REFRESH_RATE_PRECISION,
+ new Mode(1920, 1080, 60f),
+ new Mode(1920, 1080, 50f));
- final Mode[] expected1080pSupportedModes = new Mode[]{
- new Mode(720, 480, 60f),
- new Mode(720, 576, 50f),
- // 720p modes
- new Mode(1280, 720, 50f),
- new Mode(1280, 720, 60f),
- // 1080p modes
- new Mode(1920, 1080, 24f),
- new Mode(1920, 1080, 25f),
- new Mode(1920, 1080, 30f),
- new Mode(1920, 1080, 50f),
- new Mode(1920, 1080, 60f),
- };
+ final Mode[] expected1080pSupportedModes =
+ new Mode[] {
+ new Mode(720, 480, 60f),
+ new Mode(720, 576, 50f),
+ // 720p modes
+ new Mode(1280, 720, 50f),
+ new Mode(1280, 720, 60f),
+ // 1080p modes
+ new Mode(1920, 1080, 24f),
+ new Mode(1920, 1080, 25f),
+ new Mode(1920, 1080, 30f),
+ new Mode(1920, 1080, 50f),
+ new Mode(1920, 1080, 60f),
+ };
getAsserter()
.withMessage("Display.getSupportedModes()")
.that(Arrays.asList(display.getSupportedModes()))
@@ -248,6 +259,35 @@
}
}
+ private static class TvPanelReportedModesAreSupportedTestStep extends YesNoTestStep {
+ public TvPanelReportedModesAreSupportedTestStep(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);
+ String supportedModes =
+ Arrays.stream(display.getSupportedModes())
+ .map(DisplayModesTestActivity::formatDisplayMode)
+ .collect(Collectors.joining("\n"));
+
+ return context.getString(
+ R.string.tv_panel_display_modes_reported_are_supported, supportedModes);
+ }
+ }
+
+ private static class TvPanelSupportedModesAreReportedTestStep extends YesNoTestStep {
+ public TvPanelSupportedModesAreReportedTestStep(TvAppVerifierActivity context) {
+ super(context, getInstructionText(context));
+ }
+
+ private static String getInstructionText(Context context) {
+ return context.getString(R.string.tv_panel_display_modes_supported_are_reported);
+ }
+ }
+
// We use a custom Mode class since the constructors of Display.Mode are hidden. Additionally,
// we want to use fuzzy comparision for frame rates which is not used in Display.Mode.equals().
private static class Mode {
@@ -269,7 +309,7 @@
@Override
public String toString() {
- return String.format("%dx%d %.2f Hz", mWidth, mHeight, mRefreshRate);
+ return formatDisplayMode(mWidth, mHeight, mRefreshRate);
}
}
@@ -278,10 +318,22 @@
super(failureMetadata, subject);
}
- public void isEquivalentTo(Mode mode, float refreshRatePrecision) {
- if (!mode.isEquivalent(actual(), refreshRatePrecision)) {
- failWithActual("expected", mode);
+ public void isEquivalentToAnyOf(final float refreshRatePrecision, Mode... modes) {
+ boolean found =
+ Arrays.stream(modes)
+ .anyMatch(mode -> mode.isEquivalent(actual(), refreshRatePrecision));
+ if (!found) {
+ failWithActual("expected any of", Arrays.toString(modes));
}
}
}
+
+ private static String formatDisplayMode(Display.Mode mode) {
+ return formatDisplayMode(
+ mode.getPhysicalWidth(), mode.getPhysicalHeight(), mode.getRefreshRate());
+ }
+
+ private static String formatDisplayMode(int width, int height, float refreshRate) {
+ return String.format("%dx%d %.2f Hz", width, height, refreshRate);
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java
new file mode 100644
index 0000000..c73fd53
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OneButtonTestStep.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv.display;
+
+import android.view.View;
+
+import androidx.annotation.StringRes;
+
+import com.android.cts.verifier.tv.TvAppVerifierActivity;
+
+/** Test step containing instruction to the user and a button. */
+public abstract class OneButtonTestStep extends TestStepBase {
+
+ protected View mButtonView;
+
+ @StringRes
+ private int mButtonStringId;
+
+ @StringRes
+ private int mStepNameStringId;
+
+ /**
+ * Constructs a test step containing instruction to the user and a button.
+ *
+ * @param context The test activity which this test step is part of.
+ * @param instructionText The text of the test instruction visible to the user.
+ * @param stepNameStringId Id of a string resource containing human readable name of this step
+ * to be used in logs.
+ * @param buttonStringId Id of a string resource containing the text of the button.
+ */
+ public OneButtonTestStep(TvAppVerifierActivity context, @StringRes int stepNameStringId,
+ String instructionText, @StringRes int buttonStringId) {
+ super(context, instructionText);
+ mStepNameStringId = stepNameStringId;
+ mButtonStringId = buttonStringId;
+ }
+
+ @Override
+ public void createUiElements() {
+ super.createUiElements();
+ mButtonView =
+ mContext.createButtonItem(
+ mButtonStringId,
+ (View view) -> {
+ String stepName = mContext.getString(mStepNameStringId);
+ appendInfoDetails("Running test step %s...", stepName);
+ onButtonClickRunTest();
+ });
+ }
+
+ @Override
+ public void enableInteractivity() {
+ TvAppVerifierActivity.setButtonEnabled(mButtonView, true);
+ }
+
+ @Override
+ public void disableInteractivity() {
+ TvAppVerifierActivity.setButtonEnabled(mButtonView, false);
+ }
+
+ /** Test logic to be executed when the button is pressed. */
+ protected abstract void onButtonClickRunTest();
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java
index 0c4181a..a1736fb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/SyncTestStep.java
@@ -16,15 +16,18 @@
package com.android.cts.verifier.tv.display;
+import androidx.annotation.StringRes;
+
import com.android.cts.verifier.tv.TvAppVerifierActivity;
/**
- * Encapsulates the logic of a synchronously running test step, which displays a human instructions
+ * Encapsulates the logic of a synchronously running test step, which displays human instructions
* and a button to start the test. For asynchronous steps see {@link AsyncTestStep}.
*/
-public abstract class SyncTestStep extends TestStepBase {
- public SyncTestStep(TvAppVerifierActivity context) {
- super(context);
+public abstract class SyncTestStep extends OneButtonTestStep {
+ public SyncTestStep(TvAppVerifierActivity context, @StringRes int stepNameStringId,
+ String instructionText, @StringRes int buttonStringId) {
+ super(context, stepNameStringId, instructionText, buttonStringId);
}
public abstract void runTest();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
index e80828a..7815dcf 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestSequence.java
@@ -40,7 +40,7 @@
// After a step is completed we enable the button of the next step.
for (int i = 0; i < steps.size() - 1; i++) {
final int next = i + 1;
- steps.get(i).setOnDoneListener(() -> steps.get(next).enableButton());
+ steps.get(i).setOnDoneListener(() -> steps.get(next).enableInteractivity());
}
// When the last step is done, mark the sequence as done.
@@ -48,7 +48,7 @@
.setOnDoneListener(() -> onAllStepsDone());
// Enable the button of the first test step so the user can start it.
- steps.get(0).enableButton();
+ steps.get(0).enableInteractivity();
}
public String getFailureDetails() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
index ae2a65b..050c549 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/TestStepBase.java
@@ -19,74 +19,56 @@
import android.view.View;
import android.widget.TextView;
-import androidx.annotation.StringRes;
-
import com.android.cts.verifier.R;
import com.android.cts.verifier.tv.TvAppVerifierActivity;
import com.google.common.truth.FailureStrategy;
import com.google.common.truth.StandardSubjectBuilder;
-import java.util.Arrays;
-
-/**
- * Encapsulates the logic of a test step, which displays a human instructions and a button to start
- * the test.
- */
+/** Encapsulates the logic of a test step, which displays human instructions. */
public abstract class TestStepBase {
- final protected TvAppVerifierActivity mContext;
- protected View mViewItem;
+ protected final TvAppVerifierActivity mContext;
+
private boolean mHasPassed;
private Runnable mOnDoneListener;
private String mFailureDetails;
private StandardSubjectBuilder mAsserter;
+ private View mInstructionView;
+ private String mInstructionText;
/**
* Constructs a test step containing instruction to the user and a button.
*
* @param context The test activity which this test step is part of.
+ * @param instructionText The text of the test instruction visible to the user.
*/
- public TestStepBase(TvAppVerifierActivity context) {
+ public TestStepBase(TvAppVerifierActivity context, String instructionText) {
this.mContext = context;
- FailureStrategy failureStrategy = assertionError -> {
- appendFailureDetails(assertionError.getMessage());
- mHasPassed = false;
- };
+ FailureStrategy failureStrategy =
+ assertionError -> {
+ appendFailureDetails(assertionError.getMessage());
+ mHasPassed = false;
+ };
mAsserter = StandardSubjectBuilder.forCustomFailureStrategy(failureStrategy);
mHasPassed = true;
+ mInstructionText = instructionText;
}
public boolean hasPassed() {
return mHasPassed;
}
- /**
- * Creates the View for this test step in the context {@link TvAppVerifierActivity}.
- */
+ /** Creates the View for this test step in the context {@link TvAppVerifierActivity}. */
public void createUiElements() {
- mViewItem = mContext.createUserItem(
- getInstructionText(),
- getButtonStringId(),
- (View view) -> {
- appendInfoDetails("Running test step %s...", getStepName());
- onButtonClickRunTest();
- });
+ mInstructionView = mContext.createAutoItem(mInstructionText);
}
- /**
- * Enables the button of this test step.
- */
- public void enableButton() {
- TvAppVerifierActivity.setButtonEnabled(mViewItem, true);
- }
+ /** Enables interactivity for this test step - for example, it enables buttons. */
+ public abstract void enableInteractivity();
- /**
- * Disables the button of this test step.
- */
- public void disableButton() {
- TvAppVerifierActivity.setButtonEnabled(mViewItem, false);
- }
+ /** Disables interactivity for this test step - for example, it disables buttons. */
+ public abstract void disableInteractivity();
public void setOnDoneListener(Runnable listener) {
mOnDoneListener = listener;
@@ -96,25 +78,8 @@
return mFailureDetails;
}
- /**
- * Human readable name of this test step to be output to logs.
- */
- protected abstract String getStepName();
-
- protected abstract void onButtonClickRunTest();
-
- /**
- * Returns the text of the test instruction visible to the user.
- */
- protected abstract String getInstructionText();
-
- /**
- * Returns id of string resource containing the text of the button.
- */
- protected abstract @StringRes int getButtonStringId();
-
protected void done() {
- TvAppVerifierActivity.setPassState(mViewItem, mHasPassed);
+ TvAppVerifierActivity.setPassState(mInstructionView, mHasPassed);
if (mOnDoneListener != null) {
mOnDoneListener.run();
}
@@ -133,7 +98,8 @@
protected void appendFailureDetails(String failure) {
String details = String.format("Failure: %s", failure);
appendDetails(details);
- appendMessageToView(mViewItem, details);
+
+ appendMessageToView(mInstructionView, details);
}
protected void appendDetails(String details) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java
new file mode 100644
index 0000000..0281252
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/YesNoTestStep.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv.display;
+
+import android.view.View;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.tv.TvAppVerifierActivity;
+
+/**
+ * Encapsulates the logic of a test step, which displays human instructions for a manual test and
+ * two buttons - Yes and No, which respectively set the test in passing and failing state.
+ */
+public abstract class YesNoTestStep extends TestStepBase {
+ private View yesButton;
+ private View noButton;
+
+ /**
+ * Constructs a test step containing human instructions for a manual test and two buttons -
+ * Yes and No.
+ *
+ * @param context The test activity which this test step is part of.
+ * @param instructionText The text of the test instruction visible to the user.
+ */
+ public YesNoTestStep(TvAppVerifierActivity context, String instructionText) {
+ super(context, instructionText);
+ }
+
+ @Override
+ public void createUiElements() {
+ super.createUiElements();
+ yesButton =
+ mContext.createButtonItem(
+ R.string.tv_yes,
+ (View view) -> {
+ disableInteractivity();
+ // do nothing so the test will pass
+ done();
+ });
+ noButton =
+ mContext.createButtonItem(
+ R.string.tv_no,
+ (View view) -> {
+ disableInteractivity();
+ getAsserter().fail();
+ done();
+ });
+ }
+
+ @Override
+ public void enableInteractivity() {
+ TvAppVerifierActivity.setButtonEnabled(yesButton, true);
+ TvAppVerifierActivity.setButtonEnabled(noButton, true);
+ }
+
+ @Override
+ public void disableInteractivity() {
+ TvAppVerifierActivity.setButtonEnabled(yesButton, false);
+ TvAppVerifierActivity.setButtonEnabled(noButton, false);
+ }
+}
diff --git a/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-default-caps b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-default-caps
new file mode 100644
index 0000000..509ea3b
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-default-caps
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.pk8 b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.pk8
new file mode 100644
index 0000000..5e73f27
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.pk8
Binary files differ
diff --git a/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.x509.pem b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.x509.pem
new file mode 100644
index 0000000..f8e5e65
--- /dev/null
+++ b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256_2.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbTCCAROgAwIBAgIJAIhVvR3SsrIlMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM
+B2VjLXAyNTYwHhcNMTgwNzEzMTc0MTUxWhcNMjgwNzEwMTc0MTUxWjAUMRIwEAYD
+VQQDDAllYy1wMjU2XzIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQdTMoEcq2X
+7jzs7w2pPWK0UMZ4gzOzbnVTzen3SrXfALu6a6lQ5oRh1wu8JxtiFR2tLeK/YgPN
+IHaAHHqdRCLho1AwTjAdBgNVHQ4EFgQUeZHZKwII/ESL9QbU78n/9CjLXl8wHwYD
+VR0jBBgwFoAU1BM1aLlbMBWLMiBx6oxD/1sFzMgwDAYDVR0TBAUwAwEB/zAKBggq
+hkjOPQQDAgNIADBFAiAnaauxtJ/C9TR5xK6SpmMdq/1SLJrLC7orQ+vrmcYwEQIh
+ANJg+x0fF2z5t/pgCYv9JDGfSQWj5f2hAKb+Giqxn/Ce
+-----END CERTIFICATE-----
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v1-ec-p256-two-signers-targetSdk-30.apk b/hostsidetests/appsecurity/res/pkgsigverify/v1-ec-p256-two-signers-targetSdk-30.apk
new file mode 100644
index 0000000..789a022
--- /dev/null
+++ b/hostsidetests/appsecurity/res/pkgsigverify/v1-ec-p256-two-signers-targetSdk-30.apk
Binary files differ
diff --git a/hostsidetests/appsecurity/res/pkgsigverify/v1-only-target-r.apk b/hostsidetests/appsecurity/res/pkgsigverify/v1-only-target-r.apk
deleted file mode 100644
index f06a60d..0000000
--- a/hostsidetests/appsecurity/res/pkgsigverify/v1-only-target-r.apk
+++ /dev/null
Binary files differ
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
index 23fcb2a..05a76b4 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
@@ -714,6 +714,27 @@
"testGetSigningCertificatesShowsAll");
}
+ public void testInstallV3KeyRotationGetApkContentsSigners() throws Exception {
+ // The GET_SIGNING_CERTIFICATES flag results in a PackageInfo object returned with a
+ // SigningInfo instance that can be used to query all certificates in the lineage or only
+ // the current signer(s) via getApkContentsSigners. This test verifies when a V3 signed
+ // package with a rotated key is queried getApkContentsSigners only returns the current
+ // signer.
+ installApkFromBuild("v3-ec-p256-with-por_1_2-default-caps.apk");
+ Utils.runDeviceTests(
+ getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
+ "testGetApkContentsSignersShowsCurrent");
+ }
+
+ public void testInstallV2MultipleSignersGetApkContentsSigners() throws Exception {
+ // Similar to the above test, but verifies when an APK is signed with two V2 signers
+ // getApkContentsSigners returns both of the V2 signers.
+ installApkFromBuild("v1v2-ec-p256-two-signers-targetSdk-30.apk");
+ Utils.runDeviceTests(
+ getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS,
+ "testGetApkContentsSignersShowsMultipleSigners");
+ }
+
public void testInstallV3KeyRotationHasSigningCertificate() throws Exception {
// tests that hasSigningCertificate() recognizes past and current signing certs
assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-2-with-por_1_2-full-caps.apk");
@@ -764,16 +785,17 @@
assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1_P_and_2_Qplus.apk");
}
- public void testInstallTargetRWithV1Signer() throws Exception {
- // An app targeting R must have at least a V2 signature; this test verifies that an app
- // targeting R with only a V1 signature fails to install.
- assertInstallFails("v1-only-target-r.apk");
+ public void testInstallTargetSdk30WithV1Signers() throws Exception {
+ // An app targeting SDK version >= 30 must have at least a V2 signature; this test verifies
+ // an app targeting SDK version 30 with only a V1 signature fails to install.
+ assertInstallFails("v1-ec-p256-two-signers-targetSdk-30.apk");
}
- public void testInstallTargetRWithV2V2Signers() throws Exception {
- // An app targeting R must have at least a V2 signature; this test verifies that an app
- // targeting R with both a V1 and V2 signature installs successfully.
- assertInstallSucceeds("v1v2-target-r.apk");
+ public void testInstallTargetSdk30WithV1V2Signers() throws Exception {
+ // An app targeting SDK version >= 30 must have at least a V2 signature; this test verifies
+ // that an app targeting SDK version 30 with both a V1 and V2 signature installs
+ // successfully.
+ installApkFromBuild("v1v2-ec-p256-two-signers-targetSdk-30.apk");
}
public void testInstallV4WithV2Signer() throws Exception {
@@ -894,10 +916,14 @@
}
private void installDeviceTestPkg() throws Exception {
+ installApkFromBuild(DEVICE_TESTS_APK);
+ }
+
+ private void installApkFromBuild(String apkName) throws Exception {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- File apk = buildHelper.getTestFile(DEVICE_TESTS_APK);
- String result = getDevice().installPackage(apk, true);
- assertNull("failed to install " + DEVICE_TESTS_APK + ", Reason: " + result, result);
+ File apk = buildHelper.getTestFile(apkName);
+ String result = getDevice().installPackage(apk, true, INSTALL_ARG_FORCE_QUERYABLE);
+ assertNull("failed to install " + apkName + ", Reason: " + result, result);
}
private String installPackageFromResource(String apkFilenameInResources, boolean ephemeral)
diff --git a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java
index f787cc9..1a46d6a 100644
--- a/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java
+++ b/hostsidetests/appsecurity/test-apps/AppDataIsolationTestApp/common/src/com/android/cts/appdataisolation/common/FileUtils.java
@@ -19,9 +19,14 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import static org.testng.Assert.expectThrows;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -60,6 +65,15 @@
});
assertThat(exception.getMessage()).contains(JAVA_FILE_NOT_FOUND_MSG);
assertThat(exception.getMessage()).doesNotContain(JAVA_FILE_PERMISSION_DENIED_MSG);
+
+ // Try to create a directory here, and it should return permission denied not directory
+ // exists.
+ try {
+ Os.mkdir(path, 0700);
+ fail("Should not able to mkdir() on " + path);
+ } catch (ErrnoException e) {
+ assertEquals(e.errno, OsConstants.EACCES);
+ }
}
public static void assertDirIsAccessible(String path) {
diff --git a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
index 0b45764..ae419b6 100644
--- a/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/CorruptApkTests/compressed_arsc/Android.mk
@@ -20,6 +20,7 @@
LOCAL_MODULE := CtsCorruptApkTests_Unaligned_Q
LOCAL_SRC_FILES := unaligned_Q.apk
LOCAL_MODULE_CLASS := APPS
+LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_SUFFIX := .apk
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/unaligned_Q.apk
@@ -30,8 +31,9 @@
LOCAL_MODULE := CtsCorruptApkTests_Unaligned_R
LOCAL_SRC_FILES := unaligned_R.apk
LOCAL_MODULE_CLASS := APPS
+LOCAL_MODULE_TAGS := tests
LOCAL_MODULE_SUFFIX := .apk
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/unaligned_R.apk
LOCAL_COMPATIBILITY_SUITE := cts vts10 general-tests
-include $(BUILD_PREBUILT)
\ No newline at end of file
+include $(BUILD_PREBUILT)
diff --git a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
index 1e57012..7d94aed 100644
--- a/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageStatsApp/src/com/android/cts/storagestatsapp/StorageStatsTest.java
@@ -216,6 +216,20 @@
final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
final UserHandle user = android.os.Process.myUserHandle();
+ // Since scoped storage, apps can't access the package-specific Android/
+ // directories anymore. So we compute the current size, and assume the
+ // delta is in Android/*, which unfortunately may have data from
+ // test APKs that aren't cleaned up properly.
+ //
+ // Then, when we compute the new size and compare it with the stats,
+ // we expect the same delta
+ final long manualSizeBefore = getSizeManual(
+ Environment.getExternalStorageDirectory(), true);
+ final long statsSizeBefore = stats.queryExternalStatsForUser(
+ UUID_DEFAULT, user).getTotalBytes();
+
+ final long deltaBefore = statsSizeBefore - manualSizeBefore;
+
useSpace(getContext());
final File top = Environment.getExternalStorageDirectory();
@@ -240,7 +254,8 @@
manualSize += getSizeManual(getContext().getExternalCacheDir(), true);
final long statsSize = stats.queryExternalStatsForUser(UUID_DEFAULT, user).getTotalBytes();
- assertMostlyEquals(manualSize, statsSize);
+ final long deltaAfter = statsSize - manualSize;
+ assertMostlyEquals(deltaBefore, deltaAfter);
}
public void testVerifyCategory() throws Exception {
diff --git a/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java b/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java
index 4fe98ec..2f06395 100644
--- a/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java
+++ b/hostsidetests/appsecurity/test-apps/V3SigningSchemeRotation/src/android/appsecurity/cts/v3rotationtests/V3RotationTest.java
@@ -19,12 +19,13 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
-import android.util.Log;
import android.test.AndroidTestCase;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.lang.Override;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
/**
* On-device tests for APK Signature Scheme v3 based signing certificate rotation
@@ -83,6 +84,38 @@
+ "a477acbe6eecc8d575511b2b77a3551f040ccf21792f74cd95c84e3ff87ad03851db81d164c836830e31"
+ "8208a52dcdc77e376f73b96d";
+ // These are the hex encodings of the der form of the pkgsigverify/ec-p256[_2] certs used to
+ // sign APKs that are part of this test.
+ private static final String EC_P256_FIRST_CERT_HEX =
+ "3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a86"
+ + "48ce3d04030230123110300e06035504030c0765632d70323536301e170d"
+ + "3136303333313134353830365a170d3433303831373134353830365a3012"
+ + "3110300e06035504030c0765632d703235363059301306072a8648ce3d02"
+ + "0106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2b"
+ + "a0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991e"
+ + "f0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e"
+ + "04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d"
+ + "23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c06"
+ + "03551d13040530030101ff300a06082a8648ce3d04030203490030460221"
+ + "00f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16"
+ + "db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0f"
+ + "dbb8042cb655aadd";
+
+ private static final String EC_P256_SECOND_CERT_HEX =
+ "3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a86"
+ + "48ce3d04030230123110300e06035504030c0765632d70323536301e170d"
+ + "3138303731333137343135315a170d3238303731303137343135315a3014"
+ + "3112301006035504030c0965632d703235365f323059301306072a8648ce"
+ + "3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da9"
+ + "3d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc"
+ + "271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d060355"
+ + "1d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603"
+ + "551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc830"
+ + "0c0603551d13040530030101ff300a06082a8648ce3d0403020348003045"
+ + "02202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb"
+ + "99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda1"
+ + "00a6fe1a2ab19ff09e";
+
public void testHasPerm() throws Exception {
PackageManager pm = getContext().getPackageManager();
assertTrue(PERMISSION_NAME + " not granted to " + COMPANION_PKG,
@@ -141,6 +174,31 @@
+ SECOND_CERT_HEX, matchedSecond);
}
+ public void testGetApkContentsSignersShowsCurrent() throws Exception {
+ // The SigningInfo instance returned from GET_SIGNING_CERTIFICATES provides an option to
+ // obtain only the current signer through getApkContentsSigners.
+ PackageManager pm = getContext().getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(PKG, PackageManager.GET_SIGNING_CERTIFICATES);
+ assertNotNull("Failed to get signatures in Package Info of " + PKG, pi.signingInfo);
+ assertFalse("Multiple signing certificates found in signing certificate history for " + PKG,
+ pi.signingInfo.hasMultipleSigners());
+ assertExpectedSignatures(pi.signingInfo.getApkContentsSigners(), EC_P256_SECOND_CERT_HEX);
+ }
+
+ public void testGetApkContentsSignersShowsMultipleSigners() throws Exception {
+ // Similar to the test above when GET_SIGNING_CERTIFICATES is used to obtain the signers
+ // getApkContentSigners should return all of the current signatures when there are multiple
+ // V1 / V2 signers.
+ PackageManager pm = getContext().getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(PKG, PackageManager.GET_SIGNING_CERTIFICATES);
+ assertNotNull("Failed to get signatures in PackageInfo of " + PKG,
+ pi.signingInfo);
+ assertTrue("Multiple signing certificates should have been reported for " + PKG,
+ pi.signingInfo.hasMultipleSigners());
+ assertExpectedSignatures(pi.signingInfo.getApkContentsSigners(), EC_P256_FIRST_CERT_HEX,
+ EC_P256_SECOND_CERT_HEX);
+ }
+
public void testHasSigningCertificate() throws Exception {
// make sure that hasSigningCertificate() reports that both certificates in the signing
// history are present
@@ -226,4 +284,20 @@
messageDigest.update(data);
return messageDigest.digest();
}
+
+ private static void assertExpectedSignatures(Signature[] signatures,
+ String... expectedSignatures) throws Exception {
+ int numSigners = signatures.length;
+ assertEquals("An unexpected number of signatures was returned, expected "
+ + expectedSignatures.length + ", but received " + signatures.length,
+ expectedSignatures.length, numSigners);
+ Set<String> expectedSignatureSet = new HashSet<>(Arrays.asList(expectedSignatures));
+ for (int i = 0; i < numSigners; i++) {
+ String reportedCert = signatures[i].toCharsString();
+ // Remove the reported certificate from the set to ensure duplicates are not matched.
+ if (!expectedSignatureSet.remove(reportedCert)) {
+ fail("Received an unexpected signature during the test: " + reportedCert);
+ }
+ }
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
new file mode 100644
index 0000000..e037d67
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2020 Google Inc.
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+cert_dir := cts/hostsidetests/appsecurity/certs/pkgsigverify
+
+# This is the default test package signed with the default key.
+include $(LOCAL_PATH)/base.mk
+LOCAL_PACKAGE_NAME := CtsPkgInstallTinyApp
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+# This is the test package signed using the V1/V2 signature schemes with
+# two signers targeting SDK version 30 with sandbox version 1. From this
+# package the v1-ec-p256-two-signers-targetSdk-30.apk is created with the
+# following command:
+# apksigner sign --in v1v2-ec-p256-two-signers-targetSdk-30.apk --out
+# v1-ec-p256-two-signers-targetSdk-30.apk --cert ec-p256.x509.pem --key
+# ec-p256.pk8 --next-signer --cert ec-p256_2.x509.pem --key ec-p256_2.pk8
+# --v2-signing-enabled false --v3-signing-enabled false --v4-signing-enabled false
+include $(LOCAL_PATH)/base.mk
+LOCAL_SDK_VERSION := 30
+LOCAL_MANIFEST_FILE := AndroidManifest-sandbox-v1.xml
+LOCAL_PACKAGE_NAME := v1v2-ec-p256-two-signers-targetSdk-30
+LOCAL_CERTIFICATE := $(cert_dir)/ec-p256
+LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256_2
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+# This is the test package signed using the V3 signature scheme with
+# a rotated key and one signer in the lineage with default capabilities.
+include $(LOCAL_PATH)/base.mk
+LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-default-caps
+LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2
+LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256
+LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-default-caps
+include $(BUILD_CTS_SUPPORT_PACKAGE)
+
+cert_dir :=
+
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-sandbox-v1.xml b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-sandbox-v1.xml
new file mode 100644
index 0000000..8ca35572
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-sandbox-v1.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.appsecurity.cts.tinyapp"
+ android:versionCode="10"
+ android:versionName="1.0"
+ android:targetSandboxVersion="1">
+ <application android:label="@string/app_name">
+ <activity
+ android:name=".MainActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/base.mk b/hostsidetests/appsecurity/test-apps/tinyapp/base.mk
new file mode 100644
index 0000000..3c4581f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/tinyapp/base.mk
@@ -0,0 +1,23 @@
+#
+# Copyright (C) 2020 Google Inc.
+#
+# 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.
+#
+
+# Base setup that can be included by all builds of this package.
+include $(CLEAR_VARS)
+LOCAL_COMPATIBILITY_SUITE := cts vts10 general-tests
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
+LOCAL_SDK_VERSION := current
+
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java
index 1753d75..3b493f4 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/MeteredDataRestrictionTest.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
+import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
@@ -50,6 +51,7 @@
private static final String METERED_DATA_APP_MAIN_ACTIVITY
= METERED_DATA_APP_PKG + ".MainActivity";
+ private static final long WAIT_FOR_NETWORK_RECONNECTION_TIMEOUT_SEC = 10;
private static final long WAIT_FOR_NETWORK_INFO_TIMEOUT_SEC = 8;
private static final int NUM_TRIES_METERED_STATUS_CHECK = 20;
@@ -151,34 +153,40 @@
}
private void setMeteredNetwork() throws Exception {
+ final int oldNetId = getActiveNetworkNetId();
+ final boolean oldMeteredState = mCm.isActiveNetworkMetered();
final NetworkInfo networkInfo = mCm.getActiveNetworkInfo();
if (networkInfo == null) {
fail("Active network is not available");
- } else if (mCm.isActiveNetworkMetered()) {
- Log.i(TAG, "Active network already metered: " + networkInfo);
- return;
} else if (networkInfo.getType() != ConnectivityManager.TYPE_WIFI) {
fail("Active network doesn't support setting metered status: " + networkInfo);
}
- final String netId = setWifiMeteredStatus(true);
+ final String ssid = setWifiMeteredStatus(true);
// Set flag so status is reverted on resetMeteredNetwork();
- mMeteredWifi = netId;
+ mMeteredWifi = ssid;
+
+ // When transitioning from unmetered to metered, the network stack will discconect
+ // the current WiFi connection and reconnect it. In this case we need to wait for
+ // the new network to come up.
+ if (!oldMeteredState) {
+ waitForReconnection(oldNetId);
+ }
// Sanity check.
- assertWifiMeteredStatus(netId, true);
+ assertWifiMeteredStatus(ssid, true);
assertActiveNetworkMetered(true);
}
private void resetMeteredNetwork() throws Exception {
if (mMeteredWifi != null) {
Log.i(TAG, "Resetting metered status for netId=" + mMeteredWifi);
- setWifiMeteredStatus(mMeteredWifi, false);
- assertWifiMeteredStatus(mMeteredWifi, false);
+ setWifiMeteredStatus(mMeteredWifi, /* default meteredness */ null);
+ assertWifiMeteredStatus(mMeteredWifi, /* default meteredness */ null);
assertActiveNetworkMetered(false);
}
}
- private String setWifiMeteredStatus(boolean metered) throws Exception {
+ private String setWifiMeteredStatus(Boolean metered) throws Exception {
final String ssid = mWm.getConnectionInfo().getSSID();
assertNotNull("null SSID", ssid);
final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
@@ -187,14 +195,15 @@
return netId;
}
- private void setWifiMeteredStatus(String netId, boolean metered) throws Exception {
- Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
- executeCmd("cmd netpolicy set metered-network " + netId + " " + metered);
+ private void setWifiMeteredStatus(String ssid, Boolean metered) throws Exception {
+ Log.i(TAG, "Setting wi-fi network " + ssid + " metered status to " + metered);
+ executeCmd("cmd netpolicy set metered-network " + ssid + " " +
+ (metered != null ? metered.toString() : "undefined"));
}
- private void assertWifiMeteredStatus(String netId, boolean metered) throws Exception {
+ private void assertWifiMeteredStatus(String ssid, Boolean metered) throws Exception {
final String cmd = "cmd netpolicy list wifi-networks";
- final String expectedResult = netId + ";" + metered;
+ final String expectedResult = ssid + ";" + (metered != null ? metered.toString() : "none");
String cmdResult = null;
for (int i = 0; i < NUM_TRIES_METERED_STATUS_CHECK; ++i) {
cmdResult = executeCmd(cmd);
@@ -226,4 +235,27 @@
Log.i(TAG, "Cmd '" + cmd + "' result: " + result);
return result;
}
+
+ private int getActiveNetworkNetId() {
+ Network network = mCm.getActiveNetwork();
+ if (network == null) {
+ return 0;
+ }
+ return network.getNetId();
+ }
+
+ private void waitForReconnection(int oldNetId) throws InterruptedException {
+ long pollingDeadline = System.currentTimeMillis()
+ + WAIT_FOR_NETWORK_RECONNECTION_TIMEOUT_SEC * 1000;
+ int latestNetId;
+ do {
+ Thread.sleep(1000);
+ if (System.currentTimeMillis() >= pollingDeadline) {
+ fail("Timeout waiting for network reconnection");
+ }
+ latestNetId = getActiveNetworkNetId();
+ // NetId will be 0 while old network is disconnected but new network
+ // has not come up yet.
+ } while (latestNetId == 0 || latestNetId == oldNetId);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
index a61a6e2..6ca7bd3 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/OrgOwnedProfileOwnerParentTest.java
@@ -20,6 +20,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.hardware.camera2.CameraManager;
@@ -94,7 +96,6 @@
private static final Set<String> PROFILE_OWNER_ORGANIZATION_OWNED_GLOBAL_RESTRICTIONS =
Sets.newSet(
UserManager.DISALLOW_CONFIG_DATE_TIME,
- UserManager.DISALLOW_ADD_USER,
UserManager.DISALLOW_BLUETOOTH,
UserManager.DISALLOW_BLUETOOTH_SHARING,
UserManager.DISALLOW_CONFIG_BLUETOOTH,
@@ -138,6 +139,25 @@
assertThat(restrictions.get(restriction)).isNull();
}
+ public void testUnableToAddAndClearBaseUserRestrictions_onParent() {
+ testUnableToAddBaseUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
+ testUnableToClearBaseUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE);
+ testUnableToAddBaseUserRestriction(UserManager.DISALLOW_ADD_USER);
+ testUnableToClearBaseUserRestriction(UserManager.DISALLOW_ADD_USER);
+ }
+
+ private void testUnableToAddBaseUserRestriction(String restriction) {
+ assertThrows(UnsupportedOperationException.class,
+ () -> mParentDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ restriction));
+ }
+
+ private void testUnableToClearBaseUserRestriction(String restriction) {
+ assertThrows(UnsupportedOperationException.class,
+ () -> mParentDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ restriction));
+ }
+
private void checkCanOpenCamera(boolean canOpen) throws Exception {
// If the device does not support a camera it will return an empty camera ID list.
if (mCameraManager.getCameraIdList() == null
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
index caa52d6..04eebca 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/UserRestrictionsParentTest.java
@@ -61,25 +61,4 @@
assertThat(
mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_DATE_TIME)).isFalse();
}
-
- public void testAddUserRestrictionDisallowAddUser_onParent() {
- DevicePolicyManager parentDevicePolicyManager =
- mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
- assertNotNull(parentDevicePolicyManager);
-
- parentDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
- UserManager.DISALLOW_ADD_USER);
- }
-
- public void testHasUserRestrictionDisallowAddUser() {
- assertThat(mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)).isTrue();
- }
-
- public void testClearUserRestrictionDisallowAddUser() {
- DevicePolicyManager parentDevicePolicyManager =
- mDevicePolicyManager.getParentProfileInstance(ADMIN_RECEIVER_COMPONENT);
-
- parentDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
- UserManager.DISALLOW_ADD_USER);
- }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
index 2c28f1d..7f6398c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java
@@ -112,6 +112,15 @@
}
@Test
+ public void testCannotAddSecondaryUser() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ failToCreateUser();
+ }
+
+ @Test
public void testCanRelinquishControlOverDevice() throws Exception {
if (!mHasFeature) {
return;
@@ -181,18 +190,16 @@
@Test
public void testUserRestrictionsSetOnParentAreNotPersisted() throws Exception {
- if (!mHasFeature || !canCreateAdditionalUsers(1)) {
+ if (!mHasFeature) {
return;
}
- int secondaryUserId = createUser();
- setPoAsUser(secondaryUserId);
-
+ installAppAsUser(DEVICE_ADMIN_APK, mPrimaryUserId);
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
"testAddUserRestrictionDisallowConfigDateTime_onParent", mUserId);
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
"testHasUserRestrictionDisallowConfigDateTime", mUserId);
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
- "testHasUserRestrictionDisallowConfigDateTime", secondaryUserId);
+ "testHasUserRestrictionDisallowConfigDateTime", mPrimaryUserId);
removeOrgOwnedProfile();
assertHasNoUser(mUserId);
@@ -201,29 +208,7 @@
// User restrictions are not persist after organization-owned profile owner is removed
runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
- "testUserRestrictionDisallowConfigDateTimeIsNotPersisted", secondaryUserId);
- }
-
- @Test
- public void testUserRestrictionsSetOnParentAreEnforced() throws Exception {
- if (!mHasFeature || !canCreateAdditionalUsers(1)) {
- return;
- }
- int userId = createUser();
- removeUser(userId);
-
- runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
- "testAddUserRestrictionDisallowAddUser_onParent", mUserId);
- runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
- "testHasUserRestrictionDisallowAddUser", mUserId);
-
- // Make sure the user restriction is enforced
- failToCreateUser();
-
- runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
- "testHasUserRestrictionDisallowAddUser", mUserId);
- runDeviceTestsAsUser(DEVICE_ADMIN_PKG, ".UserRestrictionsParentTest",
- "testClearUserRestrictionDisallowAddUser", mUserId);
+ "testUserRestrictionDisallowConfigDateTimeIsNotPersisted", mPrimaryUserId);
}
@Test
diff --git a/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java b/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java
index b7771bc..4e52a6e 100644
--- a/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java
+++ b/hostsidetests/dumpsys/apps/ProcStatsTestApp/src/com/android/server/cts/procstats/ProcStatsTest.java
@@ -36,10 +36,6 @@
import java.util.List;
import java.util.regex.Pattern;
-/**
- * Used by NetstatsIncidentTest. Makes some network requests so "dumpsys netstats" will have
- * something to show.
- */
@RunWith(AndroidJUnit4.class)
public class ProcStatsTest {
private static final String TAG = "ProcStatsTest";
diff --git a/hostsidetests/incident/apps/netstatsapp/Android.bp b/hostsidetests/incident/apps/netstatsapp/Android.bp
deleted file mode 100644
index ef69c0b..0000000
--- a/hostsidetests/incident/apps/netstatsapp/Android.bp
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2017 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: "CtsNetStatsApp",
- defaults: ["cts_defaults"],
- srcs: ["src/**/*.java"],
- libs: [
- "android.test.runner.stubs",
- "junit",
- ],
- static_libs: [
- "ctstestrunner-axt",
- "compatibility-device-util-axt",
- "androidx.legacy_legacy-support-v4",
- ],
- sdk_version: "test_current",
- // tag this module as a cts test artifact
- test_suites: [
- "cts",
- "vts10",
- "general-tests",
- ],
-}
diff --git a/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml
deleted file mode 100644
index df045fd..0000000
--- a/hostsidetests/incident/apps/netstatsapp/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.cts.netstats" >
-
- <uses-permission android:name="android.permission.INTERNET" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.server.cts.netstats" />
-</manifest>
diff --git a/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java b/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java
deleted file mode 100644
index e9ec71e..0000000
--- a/hostsidetests/incident/apps/netstatsapp/src/com/android/server/cts/netstats/NetstatsDeviceTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.cts.netstats;
-
-import android.net.TrafficStats;
-import android.util.Log;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.net.URL;
-
-import javax.net.ssl.HttpsURLConnection;
-
-/**
- * Used by NetstatsIncidentTest. Makes some network requests so "dumpsys netstats" will have
- * something to show.
- */
-@RunWith(AndroidJUnit4.class)
-public class NetstatsDeviceTest {
- private static final String TAG = "NetstatsDeviceTest";
-
- private static final int NET_TAG = 123123123;
-
- @Test
- public void testDoNetworkWithoutTagging() throws Exception {
- Log.i(TAG, "testDoNetworkWithoutTagging");
-
- makeNetworkRequest();
- }
-
- @Test
- public void testDoNetworkWithTagging() throws Exception {
- Log.i(TAG, "testDoNetworkWithTagging");
-
- TrafficStats.getAndSetThreadStatsTag(NET_TAG);
- makeNetworkRequest();
- }
-
- private void makeNetworkRequest() throws Exception {
- final URL url = new URL("https://www.android.com/");
- final HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
- HttpsURLConnection.setFollowRedirects(true);
- try {
- final int status = urlConnection.getResponseCode();
-
- Log.i(TAG, "Response code from " + url + ": " + status);
-
- // Doesn't matter what response code we got. We touched the network, which is enough.
- } finally {
- urlConnection.disconnect();
- }
- }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
index 58784e9..460181d0 100644
--- a/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/IncidentdTest.java
@@ -40,8 +40,6 @@
FingerprintIncidentTest.verifyFingerprintServiceDumpProto(dump.getFingerprint(), filterLevel);
}
- NetstatsIncidentTest.verifyNetworkStatsServiceDumpProto(dump.getNetstats(), filterLevel);
-
SettingsIncidentTest.verifySettingsServiceDumpProto(dump.getSettings(), filterLevel);
NotificationIncidentTest.verifyNotificationServiceDumpProto(dump.getNotification(), filterLevel);
diff --git a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
deleted file mode 100644
index 13c5803..0000000
--- a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright (C) 2017 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.server.cts;
-
-import android.service.NetworkIdentityProto;
-import android.service.NetworkInterfaceProto;
-import android.service.NetworkStatsCollectionKeyProto;
-import android.service.NetworkStatsCollectionStatsProto;
-import android.service.NetworkStatsHistoryBucketProto;
-import android.service.NetworkStatsHistoryProto;
-import android.service.NetworkStatsRecorderProto;
-import android.service.NetworkStatsServiceDumpProto;
-
-import com.android.tradefed.log.LogUtil.CLog;
-
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * Test for "dumpsys netstats --proto"
- *
- * Note most of the logic here is just heuristics.
- *
- * Usage:
-
- cts-tradefed run cts --skip-device-info --skip-preconditions \
- --skip-system-status-check \
- com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker \
- -a armeabi-v7a -m CtsIncidentHostTestCases -t com.android.server.cts.NetstatsIncidentTest
-
- */
-public class NetstatsIncidentTest extends ProtoDumpTestCase {
- private static final String DEVICE_SIDE_TEST_APK = "CtsNetStatsApp.apk";
- private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.netstats";
- private static final String FEATURE_WIFI = "android.hardware.wifi";
-
- @Override
- protected void tearDown() throws Exception {
- getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
-
- super.tearDown();
- }
-
-
- private void assertPositive(String name, long value) {
- if (value > 0) return;
- fail(name + " expected to be positive, but was: " + value);
- }
-
- private void assertNotNegative(String name, long value) {
- if (value >= 0) return;
- fail(name + " expected to be zero or positive, but was: " + value);
- }
-
- private void assertGreaterOrEqual(long greater, long lesser) {
- assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
- greater >= lesser);
- }
-
- /**
- * Parse the output of "dumpsys netstats --proto" and make sure all the values are probable.
- */
- public void testSanityCheck() throws Exception {
-
- final long st = System.currentTimeMillis();
-
- installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
-
- // Find the package UID.
- final int uid = Integer.parseInt(execCommandAndGetFirstGroup(
- "dumpsys package " + DEVICE_SIDE_TEST_PACKAGE, "userId=(\\d+)"));
-
- CLog.i("Start time: " + st);
- CLog.i("App UID: " + uid);
-
- // Run the device side test which makes some network requests.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, null, null);
-
- // Make some more activity.
- getDevice().executeShellCommand("ping -s 100 -c 10 -i 0 www.android.com");
-
- // Force refresh the output.
- getDevice().executeShellCommand("dumpsys netstats --poll");
-
- NetworkStatsServiceDumpProto dump = getDump(NetworkStatsServiceDumpProto.parser(),
- "dumpsys netstats --proto");
-
- CLog.d("First dump:\n" + dump.toString());
-
- // Basic sanity check.
- checkInterfaces(dump.getActiveInterfacesList());
- checkInterfaces(dump.getActiveUidInterfacesList());
-
- checkStats(dump.getDevStats(), /*withUid=*/ false, /*withTag=*/ false);
- checkStats(dump.getXtStats(), /*withUid=*/ false, /*withTag=*/ false);
- checkStats(dump.getUidStats(), /*withUid=*/ true, /*withTag=*/ false);
- checkStats(dump.getUidTagStats(), /*withUid=*/ true, /*withTag=*/ true);
-
- // Remember the original values.
- final Predicate<NetworkStatsCollectionKeyProto> uidFilt = key -> key.getUid() == uid;
- final Predicate<NetworkStatsCollectionKeyProto> tagFilt =
- key -> (key.getTag() == 123123123) && (key.getUid() == uid);
-
- final long devRxPackets = sum(dump.getDevStats(), st, b -> b.getRxPackets());
- final long devRxBytes = sum(dump.getDevStats(), st, b -> b.getRxBytes());
- final long devTxPackets = sum(dump.getDevStats(), st, b -> b.getTxPackets());
- final long devTxBytes = sum(dump.getDevStats(), st, b -> b.getTxBytes());
-
- final long xtRxPackets = sum(dump.getXtStats(), st, b -> b.getRxPackets());
- final long xtRxBytes = sum(dump.getXtStats(), st, b -> b.getRxBytes());
- final long xtTxPackets = sum(dump.getXtStats(), st, b -> b.getTxPackets());
- final long xtTxBytes = sum(dump.getXtStats(), st, b -> b.getTxBytes());
-
- final long uidRxPackets = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
- final long uidRxBytes = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
- final long uidTxPackets = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
- final long uidTxBytes = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
-
- final long tagRxPackets = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
- final long tagRxBytes = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
- final long tagTxPackets = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
- final long tagTxBytes = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
-
- // Run again to make some more activity.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
- "com.android.server.cts.netstats.NetstatsDeviceTest",
- "testDoNetworkWithoutTagging");
-
- getDevice().executeShellCommand("dumpsys netstats --poll");
- dump = getDump(NetworkStatsServiceDumpProto.parser(), "dumpsys netstats --proto");
-
- CLog.d("Second dump:\n" + dump.toString());
-
- final long devRxPackets2 = sum(dump.getDevStats(), st, b -> b.getRxPackets());
- final long devRxBytes2 = sum(dump.getDevStats(), st, b -> b.getRxBytes());
- final long devTxPackets2 = sum(dump.getDevStats(), st, b -> b.getTxPackets());
- final long devTxBytes2 = sum(dump.getDevStats(), st, b -> b.getTxBytes());
-
- final long xtRxPackets2 = sum(dump.getXtStats(), st, b -> b.getRxPackets());
- final long xtRxBytes2 = sum(dump.getXtStats(), st, b -> b.getRxBytes());
- final long xtTxPackets2 = sum(dump.getXtStats(), st, b -> b.getTxPackets());
- final long xtTxBytes2 = sum(dump.getXtStats(), st, b -> b.getTxBytes());
-
- final long uidRxPackets2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
- final long uidRxBytes2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
- final long uidTxPackets2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
- final long uidTxBytes2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
-
- final long tagRxPackets2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
- final long tagRxBytes2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
- final long tagTxPackets2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
- final long tagTxBytes2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
-
- // At least 1 packet, 100 bytes sent.
- assertGreaterOrEqual(uidTxPackets2, uidTxPackets + 1);
- assertGreaterOrEqual(uidTxBytes2, uidTxBytes + 100);
-
-// assertGreaterOrEqual(tagTxPackets2, tagTxPackets + 1);
-// assertGreaterOrEqual(tagTxBytes2, tagTxBytes + 100);
-
- // At least 2 packets, 100 bytes sent.
- assertGreaterOrEqual(uidRxPackets2, uidRxPackets + 2);
- assertGreaterOrEqual(uidRxBytes2, uidRxBytes + 100);
-
-// assertGreaterOrEqual(tagRxPackets2, tagRxPackets + 2);
-// assertGreaterOrEqual(tagRxBytes2, tagRxBytes + 100);
-
- // Run again to make some more activity.
- runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
- "com.android.server.cts.netstats.NetstatsDeviceTest",
- "testDoNetworkWithTagging");
-
- getDevice().executeShellCommand("dumpsys netstats --poll");
- dump = getDump(NetworkStatsServiceDumpProto.parser(), "dumpsys netstats --proto");
-
- CLog.d("Second dump:\n" + dump.toString());
-
- final long devRxPackets3 = sum(dump.getDevStats(), st, b -> b.getRxPackets());
- final long devRxBytes3 = sum(dump.getDevStats(), st, b -> b.getRxBytes());
- final long devTxPackets3 = sum(dump.getDevStats(), st, b -> b.getTxPackets());
- final long devTxBytes3 = sum(dump.getDevStats(), st, b -> b.getTxBytes());
-
- final long xtRxPackets3 = sum(dump.getXtStats(), st, b -> b.getRxPackets());
- final long xtRxBytes3 = sum(dump.getXtStats(), st, b -> b.getRxBytes());
- final long xtTxPackets3 = sum(dump.getXtStats(), st, b -> b.getTxPackets());
- final long xtTxBytes3 = sum(dump.getXtStats(), st, b -> b.getTxBytes());
-
- final long uidRxPackets3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
- final long uidRxBytes3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
- final long uidTxPackets3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
- final long uidTxBytes3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
-
- final long tagRxPackets3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
- final long tagRxBytes3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
- final long tagTxPackets3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
- final long tagTxBytes3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
-
- // At least 1 packet, 100 bytes sent.
- assertGreaterOrEqual(uidTxPackets3, uidTxPackets2 + 1);
- assertGreaterOrEqual(uidTxBytes3, uidTxBytes2 + 100);
-
- assertGreaterOrEqual(tagTxPackets3, tagTxPackets2 + 1);
- assertGreaterOrEqual(tagTxBytes3, tagTxBytes2 + 100);
-
- // At least 2 packets, 100 bytes sent.
- assertGreaterOrEqual(uidRxPackets3, uidRxPackets2 + 2);
- assertGreaterOrEqual(uidRxBytes3, uidRxBytes2 + 100);
-
- assertGreaterOrEqual(tagRxPackets3, tagRxPackets2 + 2);
- assertGreaterOrEqual(tagRxBytes3, tagRxBytes2 + 100);
- }
-
- private long sum(NetworkStatsRecorderProto recorder,
- long startTime,
- Function<NetworkStatsHistoryBucketProto, Long> func) {
- return sum(recorder, startTime, key -> true, func);
- }
-
- private long sum(NetworkStatsRecorderProto recorder,
- long startTime,
- Predicate<NetworkStatsCollectionKeyProto> filter,
- Function<NetworkStatsHistoryBucketProto, Long> func) {
-
- long total = 0;
- for (NetworkStatsCollectionStatsProto stats
- : recorder.getCompleteHistory().getStatsList()) {
- if (!filter.test(stats.getKey())) {
- continue;
- }
- for (NetworkStatsHistoryBucketProto bucket : stats.getHistory().getBucketsList()) {
- if (startTime < bucket.getBucketStartMs()) {
- continue;
- }
- total += func.apply(bucket);
- }
- }
- return total;
- }
-
- private void checkInterfaces(List<NetworkInterfaceProto> interfaces) throws Exception{
- /* Example:
- active_interfaces=[
- NetworkInterfaceProto {
- interface=wlan0
- identities=NetworkIdentitySetProto {
- identities=[
- NetworkIdentityProto {
- type=1
- subscriber_id=
- network_id="wifiap"
- roaming=false
- metered=false
- }
- ]
- }
- }
- ]
- */
- assertTrue("There must be at least one network device",
- interfaces.size() > 0);
-
- boolean allRoaming = true;
- boolean allMetered = true;
-
- for (NetworkInterfaceProto iface : interfaces) {
- assertTrue("Missing interface name", !iface.getInterface().isEmpty());
-
- assertPositive("# identities", iface.getIdentities().getIdentitiesList().size());
-
- for (NetworkIdentityProto iden : iface.getIdentities().getIdentitiesList()) {
- allRoaming &= iden.getRoaming();
- allMetered &= iden.getMetered();
-
- // TODO Can we check the other fields too? type, subscriber_id, and network_id.
- }
- }
- assertFalse("There must be at least one non-roaming interface during CTS", allRoaming);
- if (hasWiFiFeature()) {
- assertFalse("There must be at least one non-metered interface during CTS", allMetered);
- }
- }
-
- private void checkStats(NetworkStatsRecorderProto recorder, boolean withUid, boolean withTag) {
- /*
- * Example:
- dev_stats=NetworkStatsRecorderProto {
- pending_total_bytes=136
- complete_history=NetworkStatsCollectionProto {
- stats=[
- NetworkStatsCollectionStatsProto {
- key=NetworkStatsCollectionKeyProto {
- identity=NetworkIdentitySetProto {
- identities=[
- NetworkIdentityProto {
- type=1
- subscriber_id=
- network_id="wifiap"
- roaming=false
- metered=false
- }
- ]
- }
- uid=-1
- set=-1
- tag=0
- }
- history=NetworkStatsHistoryProto {
- bucket_duration_ms=3600000
- buckets=[
- NetworkStatsHistoryBucketProto {
- bucket_start_ms=2273694336
- rx_bytes=2142
- rx_packets=10
- tx_bytes=1568
- tx_packets=12
- operations=0
- }
- NetworkStatsHistoryBucketProto {
- bucket_start_ms=3196682880
- rx_bytes=2092039
- rx_packets=1987
- tx_bytes=236735
- tx_packets=1750
- operations=0
- }
- */
-
- assertNotNegative("Pending bytes", recorder.getPendingTotalBytes());
-
- for (NetworkStatsCollectionStatsProto stats : recorder.getCompleteHistory().getStatsList()) {
-
- final NetworkStatsCollectionKeyProto key = stats.getKey();
-
- // TODO Check the key.
-
- final NetworkStatsHistoryProto hist = stats.getHistory();
-
- assertPositive("duration", hist.getBucketDurationMs());
-
- // Subtract one hour from duration to compensate for possible DTS.
- final long minInterval = hist.getBucketDurationMs() - (60 * 60 * 1000);
-
- NetworkStatsHistoryBucketProto prev = null;
- for (NetworkStatsHistoryBucketProto bucket : hist.getBucketsList()) {
-
- // Make sure the start time is increasing by at least the "duration",
- // except we subtract duration from one our to compensate possible DTS.
-
- if (prev != null) {
- assertTrue(
- String.format("Last start=%d, current start=%d, diff=%d, duration=%d",
- prev.getBucketStartMs(), bucket.getBucketStartMs(),
- (bucket.getBucketStartMs() - prev.getBucketStartMs()),
- minInterval),
- (bucket.getBucketStartMs() - prev.getBucketStartMs()) >=
- minInterval);
- }
- assertNotNegative("RX bytes", bucket.getRxBytes());
- assertNotNegative("RX packets", bucket.getRxPackets());
- assertNotNegative("TX bytes", bucket.getTxBytes());
- assertNotNegative("TX packets", bucket.getTxPackets());
-
- assertTrue(
- String.format("# of bytes %d too small for # of packets %d",
- bucket.getRxBytes(), bucket.getRxPackets()),
- bucket.getRxBytes() >= bucket.getRxPackets());
- assertTrue(
- String.format("# of bytes %d too small for # of packets %d",
- bucket.getTxBytes(), bucket.getTxPackets()),
- bucket.getTxBytes() >= bucket.getTxPackets());
- }
- }
-
- // TODO Make sure test app's UID actually shows up.
- }
-
- private boolean hasWiFiFeature() throws Exception {
- final String commandOutput = getDevice().executeShellCommand("pm list features");
- return commandOutput.contains(FEATURE_WIFI);
- }
-
- // Currently only verifies that privacy filtering is done properly.
- static void verifyNetworkStatsServiceDumpProto(NetworkStatsServiceDumpProto dump, final int filterLevel) throws Exception {
- if (filterLevel == PRIVACY_AUTO) {
- for (NetworkInterfaceProto nip : dump.getActiveInterfacesList()) {
- verifyNetworkInterfaceProto(nip, filterLevel);
- }
- for (NetworkInterfaceProto nip : dump.getActiveUidInterfacesList()) {
- verifyNetworkInterfaceProto(nip, filterLevel);
- }
- }
- }
-
- private static void verifyNetworkInterfaceProto(NetworkInterfaceProto nip, final int filterLevel) throws Exception {
- for (NetworkIdentityProto ni : nip.getIdentities().getIdentitiesList()) {
- if (filterLevel == PRIVACY_AUTO) {
- assertTrue(ni.getSubscriberId().isEmpty());
- assertTrue(ni.getNetworkId().isEmpty());
- }
- }
- }
-}
diff --git a/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java
index 9c9d11e..6ea48fe 100644
--- a/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/PackageIncidentTest.java
@@ -23,9 +23,9 @@
/** Test for "dumpsys package --proto" */
public class PackageIncidentTest extends ProtoDumpTestCase {
- // Use the test apk from the NetstatsIncidentTest
- private static final String DEVICE_SIDE_TEST_APK = "CtsNetStatsApp.apk";
- private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.netstats";
+ // Use the test apk from the BatteryStatsIncidentTest
+ private static final String DEVICE_SIDE_TEST_APK = "CtsBatteryStatsApp.apk";
+ private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.device.batterystats";
@Override
protected void tearDown() throws Exception {
diff --git a/hostsidetests/incrementalinstall/Android.bp b/hostsidetests/incrementalinstall/Android.bp
index fbc9dba..64c15aa 100644
--- a/hostsidetests/incrementalinstall/Android.bp
+++ b/hostsidetests/incrementalinstall/Android.bp
@@ -27,5 +27,11 @@
test_suites: [
"cts",
],
- data: [":IncrementalTestAppRule"],
+ data: [
+ ":IncrementalTestAppDynamicAsset",
+ ":IncrementalTestAppDynamicCode",
+ ":IncrementalTestAppCompressedNativeLib",
+ ":IncrementalTestAppUncompressedNativeLib",
+ ],
+
}
diff --git a/hostsidetests/incrementalinstall/app/Android.bp b/hostsidetests/incrementalinstall/app/Android.bp
index 132ea73..483011e 100644
--- a/hostsidetests/incrementalinstall/app/Android.bp
+++ b/hostsidetests/incrementalinstall/app/Android.bp
@@ -80,29 +80,3 @@
aapt_include_all_resources: true,
manifest: "AndroidManifestV2.xml",
}
-
-genrule {
- // copy apks and id sig to gendir.
- name: "IncrementalTestAppRule",
- srcs: [":IncrementalTestApp",
- ":IncrementalTestApp2_v1",
- ":IncrementalTestApp2_v2",
- ":IncrementalTestAppDynamicAsset",
- ":IncrementalTestAppDynamicCode",
- ":IncrementalTestAppCompressedNativeLib",
- ":IncrementalTestAppUncompressedNativeLib",],
- out: ["IncrementalTestApp.apk.idsig",
- "IncrementalTestApp2_v1.apk.idsig",
- "IncrementalTestApp2_v2.apk.idsig",
- "IncrementalTestAppDynamicAsset.apk", "IncrementalTestAppDynamicAsset.apk.idsig",
- "IncrementalTestAppDynamicCode.apk", "IncrementalTestAppDynamicCode.apk.idsig",
- "IncrementalTestAppCompressedNativeLib.apk", "IncrementalTestAppCompressedNativeLib.apk.idsig",
- "IncrementalTestAppUncompressedNativeLib.apk", "IncrementalTestAppUncompressedNativeLib.apk.idsig",],
- cmd: "cp $(locations :IncrementalTestApp) $(genDir)" +
- " && cp $(locations :IncrementalTestApp2_v1) $(genDir)" +
- " && cp $(locations :IncrementalTestApp2_v2) $(genDir)" +
- " && cp $(locations :IncrementalTestAppDynamicAsset) $(genDir)" +
- " && cp $(locations :IncrementalTestAppDynamicCode) $(genDir)" +
- " && cp $(locations :IncrementalTestAppCompressedNativeLib) $(genDir)" +
- " && cp $(locations :IncrementalTestAppUncompressedNativeLib) $(genDir)",
- }
\ No newline at end of file
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
index a820ae5..5aafdf0 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -25,4 +25,5 @@
String getRestrictBackgroundStatus();
void sendNotification(int notificationId, String notificationType);
void registerNetworkCallback(in INetworkCallback cb);
+ void unregisterNetworkCallback();
}
diff --git a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
index 740ec26..2048bab 100644
--- a/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
+++ b/hostsidetests/net/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
@@ -17,9 +17,11 @@
package com.android.cts.net.hostside;
import android.net.Network;
+import android.net.NetworkCapabilities;
interface INetworkCallback {
void onBlockedStatusChanged(in Network network, boolean blocked);
void onAvailable(in Network network);
void onLost(in Network network);
+ void onCapabilitiesChanged(in Network network, in NetworkCapabilities cap);
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 51bdf8e..5fe4573 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -50,7 +50,7 @@
public final void tearDown() throws Exception {
super.tearDown();
- turnBatteryOff();
+ executeSilentShellCommand("cmd battery reset");
setAppIdle(false);
}
@@ -131,11 +131,11 @@
@RequiredProperties({BATTERY_SAVER_MODE})
@Test
public void testAppIdleNetworkAccess_whenCharging() throws Exception {
- // Check that idle app doesn't get network when charging
+ // Check that app is paroled when charging
setAppIdle(true);
assertBackgroundNetworkAccess(false);
turnBatteryOff();
- assertBackgroundNetworkAccess(false);
+ assertBackgroundNetworkAccess(true);
turnBatteryOn();
assertBackgroundNetworkAccess(false);
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 1db0417..2072db3 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -700,6 +700,10 @@
mServiceClient.registerNetworkCallback(cb);
}
+ protected void unregisterNetworkCallback() throws Exception {
+ mServiceClient.unregisterNetworkCallback();
+ }
+
/**
* Registers a {@link NotificationListenerService} implementation that will execute the
* notification actions right after the notification is sent.
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
index 3ee7b99..6546e26 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -100,4 +100,8 @@
public void registerNetworkCallback(INetworkCallback cb) throws RemoteException {
mService.registerNetworkCallback(cb);
}
+
+ public void unregisterNetworkCallback() throws RemoteException {
+ mService.unregisterNetworkCallback();
+ }
}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index ed397b9..ec884d0 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,18 +16,23 @@
package com.android.cts.net.hostside;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
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.net.Network;
+import android.net.NetworkCapabilities;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import java.util.Objects;
@@ -35,15 +40,18 @@
import java.util.concurrent.TimeUnit;
public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
-
private Network mNetwork;
private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
+ @Rule
+ public final MeterednessConfigurationRule mMeterednessConfiguration
+ = new MeterednessConfigurationRule();
enum CallbackState {
NONE,
AVAILABLE,
LOST,
- BLOCKED_STATUS
+ BLOCKED_STATUS,
+ CAPABILITIES
}
private static class CallbackInfo {
@@ -75,7 +83,7 @@
}
private class TestNetworkCallback extends INetworkCallback.Stub {
- private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
+ private static final int TEST_CALLBACK_TIMEOUT_MS = 5_000;
private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
@@ -117,12 +125,21 @@
setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
}
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+ setLastCallback(CallbackState.CAPABILITIES, network, cap);
+ }
+
public void expectLostCallback(Network expectedNetwork) {
expectCallback(CallbackState.LOST, expectedNetwork, null);
}
- public void expectAvailableCallback(Network expectedNetwork) {
- expectCallback(CallbackState.AVAILABLE, expectedNetwork, null);
+ public Network expectAvailableCallbackAndGetNetwork() {
+ final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+ if (cb.state != CallbackState.AVAILABLE) {
+ fail("Network is not available");
+ }
+ return cb.network;
}
public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
@@ -130,15 +147,28 @@
expectBlocked);
}
- void assertNoCallback() {
- CallbackInfo cb = null;
- try {
- cb = mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- // Expected.
- }
- if (cb != null) {
- assertNull("Unexpected callback: " + cb, cb);
+ public void waitBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
+ final long deadline = System.currentTimeMillis() + TEST_CALLBACK_TIMEOUT_MS;
+ do {
+ final CallbackInfo cb = nextCallback((int) (deadline - System.currentTimeMillis()));
+ if (cb.state == CallbackState.BLOCKED_STATUS) {
+ assertEquals(expectBlocked, (Boolean) cb.arg);
+ return;
+ }
+ } while (System.currentTimeMillis() <= deadline);
+ fail("Didn't receive onBlockedStatusChanged()");
+ }
+
+ public void expectCapabilitiesCallback(Network expectedNetwork, boolean hasCapability,
+ int capability) {
+ final CallbackInfo cb = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+ final NetworkCapabilities cap = (NetworkCapabilities) cb.arg;
+ assertEquals(expectedNetwork, cb.network);
+ assertEquals(CallbackState.CAPABILITIES, cb.state);
+ if (hasCapability) {
+ assertTrue(cap.hasCapability(capability));
+ } else {
+ assertFalse(cap.hasCapability(capability));
}
}
}
@@ -147,13 +177,28 @@
public void setUp() throws Exception {
super.setUp();
- mNetwork = mCm.getActiveNetwork();
-
registerBroadcastReceiver();
removeRestrictBackgroundWhitelist(mUid);
removeRestrictBackgroundBlacklist(mUid);
assertRestrictBackgroundChangedReceived(0);
+
+ // Initial state
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+
+ // Make wifi a metered network.
+ mMeterednessConfiguration.configureNetworkMeteredness(true);
+
+ // Register callback
+ registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+ // Once the wifi is marked as metered, the wifi will reconnect. Wait for onAvailable()
+ // callback to ensure wifi is connected before the test and store the default network.
+ mNetwork = mTestNetworkCallback.expectAvailableCallbackAndGetNetwork();
+ // Check that the network is metered.
+ mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, false /* hasCapability */,
+ NET_CAPABILITY_NOT_METERED);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
}
@After
@@ -162,102 +207,80 @@
setRestrictBackground(false);
setBatterySaverMode(false);
+ unregisterNetworkCallback();
}
@RequiredProperties({DATA_SAVER_MODE})
@Test
public void testOnBlockedStatusChanged_dataSaver() throws Exception {
- // Initial state
- setBatterySaverMode(false);
- setRestrictBackground(false);
-
- final MeterednessConfigurationRule meterednessConfiguration
- = new MeterednessConfigurationRule();
- meterednessConfiguration.configureNetworkMeteredness(true);
try {
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
// Enable restrict background
setRestrictBackground(true);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
// Add to whitelist
addRestrictBackgroundWhitelist(mUid);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
// Remove from whitelist
removeRestrictBackgroundWhitelist(mUid);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
// Set to non-metered network
- meterednessConfiguration.configureNetworkMeteredness(false);
+ mMeterednessConfiguration.configureNetworkMeteredness(false);
+ mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+ NET_CAPABILITY_NOT_METERED);
try {
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
// Disable restrict background, should not trigger callback
setRestrictBackground(false);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.assertNoCallback();
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
}
@RequiredProperties({BATTERY_SAVER_MODE})
@Test
public void testOnBlockedStatusChanged_powerSaver() throws Exception {
- // Set initial state.
- setBatterySaverMode(false);
- setRestrictBackground(false);
-
- final MeterednessConfigurationRule meterednessConfiguration
- = new MeterednessConfigurationRule();
- meterednessConfiguration.configureNetworkMeteredness(true);
try {
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
// Enable Power Saver
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
// Disable Power Saver
setBatterySaverMode(false);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
// Set to non-metered network
- meterednessConfiguration.configureNetworkMeteredness(false);
+ mMeterednessConfiguration.configureNetworkMeteredness(false);
+ mTestNetworkCallback.expectCapabilitiesCallback(mNetwork, true /* hasCapability */,
+ NET_CAPABILITY_NOT_METERED);
try {
- mTestNetworkCallback.assertNoCallback();
-
// Enable Power Saver
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, true);
// Disable Power Saver
setBatterySaverMode(false);
assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ mTestNetworkCallback.waitBlockedStatusCallback(mNetwork, false);
} finally {
- meterednessConfiguration.resetNetworkMeteredness();
+ mMeterednessConfiguration.resetNetworkMeteredness();
}
}
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
index ec536af..590e17e 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -127,6 +127,16 @@
unregisterNetworkCallback();
}
}
+
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities cap) {
+ try {
+ cb.onCapabilitiesChanged(network, cap);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Cannot send onCapabilitiesChanged: " + e);
+ unregisterNetworkCallback();
+ }
+ }
};
mCm.registerNetworkCallback(makeWifiNetworkRequest(), mNetworkCallback);
try {
@@ -135,17 +145,21 @@
unregisterNetworkCallback();
}
}
- };
- private void unregisterNetworkCallback() {
- Log.d(TAG, "unregistering network callback");
- mCm.unregisterNetworkCallback(mNetworkCallback);
- mNetworkCallback = null;
- }
+ @Override
+ public void unregisterNetworkCallback() {
+ Log.d(TAG, "unregistering network callback");
+ if (mNetworkCallback != null) {
+ mCm.unregisterNetworkCallback(mNetworkCallback);
+ mNetworkCallback = null;
+ }
+ }
+ };
private NetworkRequest makeWifiNetworkRequest() {
return new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build();
}
diff --git a/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java b/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
index dfce7da..b0facec 100644
--- a/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
+++ b/hostsidetests/net/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
@@ -24,6 +24,9 @@
public class NetworkPolicyTestsPreparer implements ITargetPreparer {
private ITestDevice mDevice;
private String mOriginalAppStandbyEnabled;
+ private String mOriginalBatteryStatsConstants;
+ private final static String KEY_STABLE_CHARGING_DELAY_MS = "battery_charged_delay_ms";
+ private final static int DESIRED_STABLE_CHARGING_DELAY_MS = 0;
@Override
public void setUp(TestInformation testInformation) throws DeviceNotAvailableException {
@@ -31,12 +34,18 @@
mOriginalAppStandbyEnabled = getAppStandbyEnabled();
setAppStandbyEnabled("1");
LogUtil.CLog.d("Original app_standby_enabled: " + mOriginalAppStandbyEnabled);
+
+ mOriginalBatteryStatsConstants = getBatteryStatsConstants();
+ setBatteryStatsConstants(
+ KEY_STABLE_CHARGING_DELAY_MS + "=" + DESIRED_STABLE_CHARGING_DELAY_MS);
+ LogUtil.CLog.d("Original battery_saver_constants: " + mOriginalBatteryStatsConstants);
}
@Override
public void tearDown(TestInformation testInformation, Throwable e)
throws DeviceNotAvailableException {
setAppStandbyEnabled(mOriginalAppStandbyEnabled);
+ setBatteryStatsConstants(mOriginalBatteryStatsConstants);
}
private void setAppStandbyEnabled(String appStandbyEnabled) throws DeviceNotAvailableException {
@@ -51,6 +60,15 @@
return executeCmd("settings get global app_standby_enabled").trim();
}
+ private void setBatteryStatsConstants(String batteryStatsConstants)
+ throws DeviceNotAvailableException {
+ executeCmd("settings put global battery_stats_constants \"" + batteryStatsConstants + "\"");
+ }
+
+ private String getBatteryStatsConstants() throws DeviceNotAvailableException {
+ return executeCmd("settings get global battery_stats_constants");
+ }
+
private String executeCmd(String cmd) throws DeviceNotAvailableException {
final String output = mDevice.executeShellCommand(cmd).trim();
LogUtil.CLog.d("Output for '%s': %s", cmd, output);
diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
index 9f8ec2f..9e68c8c 100644
--- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java
@@ -306,20 +306,44 @@
}
@Test
+ public void testAbandonStagedApkBeforeReady_CommitAndAbandon() throws Exception {
+ int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ abandonSession(sessionId);
+ assertThat(getStagedSessionInfo(sessionId)).isNull();
+ }
+
+ @Test
+ public void testAbandonStagedApkBeforeReady_VerifyPostReboot() throws Exception {
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ }
+
+ @Test
+ public void testStageAnotherSessionImmediatelyAfterAbandon() throws Exception {
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ int sessionId = stageSingleApk(TestApp.Apex2).assertSuccessful().getSessionId();
+ abandonSession(sessionId);
+ stageSingleApk(TestApp.Apex2).assertSuccessful();
+ }
+
+ @Test
+ public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception {
+ assertThat(getInstalledVersion(TestApp.A)).isEqualTo(-1);
+ assertThat(getInstalledVersion(TestApp.Apex)).isEqualTo(1);
+ // Using an apex in hopes that pre-reboot verification will take longer to complete
+ // and we will manage to abandon it before session becomes ready.
+ int sessionId = stageMultipleApks(TestApp.A1, TestApp.Apex2).assertSuccessful()
+ .getSessionId();
+ abandonSession(sessionId);
+ assertThat(getStagedSessionInfo(sessionId)).isNull();
+ assertNoSessionUpdatedBroadcastSent();
+ }
+
+ @Test
public void testGetActiveStagedSessions() throws Exception {
PackageInstaller packageInstaller = getPackageInstaller();
int firstSessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(firstSessionId);
int secondSessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId();
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(secondSessionId);
List<Integer> stagedSessionIds = packageInstaller.getActiveStagedSessions()
.stream().map(s -> s.getSessionId()).collect(Collectors.toList());
assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId);
@@ -341,11 +365,6 @@
public void testIsStagedSessionActive() throws Exception {
PackageInstaller packageInstaller = getPackageInstaller();
int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(sessionId);
List<PackageInstaller.SessionInfo> allSessions = packageInstaller.getAllSessions();
boolean activeStagedSessionFound = false;
@@ -377,17 +396,8 @@
public void testGetActiveStagedSessions_MultiApkSession() throws Exception {
int firstSessionId = stageMultipleApks(TestApp.A1, TestApp.B1)
.assertSuccessful().getSessionId();
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(firstSessionId);
int secondSessionId = stageMultipleApks(TestApp.C1)
.assertSuccessful().getSessionId();
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- waitForIsReadyBroadcast(secondSessionId);
List<Integer> stagedSessionIds = getPackageInstaller().getActiveStagedSessions()
.stream().map(s -> s.getSessionId()).collect(Collectors.toList());
assertThat(stagedSessionIds).containsExactly(firstSessionId, secondSessionId);
@@ -873,59 +883,33 @@
// Should fail to stage multiple sessions when check-point is not available
@Test
public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception {
- int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ stageSingleApk(TestApp.A1).assertSuccessful();
StageSessionResult failedSessionResult = stageSingleApk(TestApp.B1);
assertThat(failedSessionResult.getErrorMessage()).contains(
"Cannot stage multiple sessions without checkpoint support");
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(sessionId);
}
@Test
public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception {
- int sessionId = stageSingleApk(TestApp.A1).assertSuccessful().getSessionId();
+ stageSingleApk(TestApp.A1).assertSuccessful();
StageSessionResult failedSessionResult = stageSingleApk(TestApp.A1);
assertThat(failedSessionResult.getErrorMessage()).contains(
"has been staged already by session");
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(sessionId);
}
@Test
public void testAllowNonOverlappingMultipleStagedInstall_MultiPackageSinglePackage_Apk()
throws Exception {
- int firstSessionId = stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful()
- .getSessionId();
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(firstSessionId);
- int secondSessionId = stageSingleApk(TestApp.C1).assertSuccessful().getSessionId();
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(secondSessionId);
+ stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful();
+ stageSingleApk(TestApp.C1).assertSuccessful();
}
@Test
public void testFailOverlappingMultipleStagedInstall_BothMultiPackage_Apk() throws Exception {
- int sessionId = stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful().getSessionId();
+ stageMultipleApks(TestApp.A1, TestApp.B1).assertSuccessful();
StageSessionResult failedSessionResult = stageMultipleApks(TestApp.A2, TestApp.C1);
assertThat(failedSessionResult.getErrorMessage()).contains(
"has been staged already by session");
- // Currently abandoning a session before pre-reboot verification finishes might result in
- // a system_server crash. Before that issue is resolved we need to manually wait for
- // pre-reboot verification to finish before abandoning sessions.
- // TODO(b/145925842): remove following two lines after fixing the bug.
- waitForIsReadyBroadcast(sessionId);
}
// Should succeed in installing multiple staged sessions together
@@ -1173,7 +1157,8 @@
// Commit the session (this will start the installation workflow).
Log.i(TAG, "Committing session for apk: " + testApp);
commitSession(sessionId);
- return new StageSessionResult(sessionId, LocalIntentSender.getIntentSenderResult());
+ return new StageSessionResult(sessionId,
+ LocalIntentSender.getIntentSenderResult(sessionId));
}
private static StageSessionResult stageMultipleApks(TestApp... testApps) throws Exception {
@@ -1368,6 +1353,13 @@
assertThat(info).isNull();
}
+ private void assertNoSessionUpdatedBroadcastSent() throws Exception {
+ PackageInstaller.SessionInfo info =
+ SessionUpdateBroadcastReceiver.sessionBroadcasts.poll(10,
+ TimeUnit.SECONDS);
+ assertThat(info).isNull();
+ }
+
@Test
public void isCheckpointSupported() {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
index d5b1a8e..2369a2d 100644
--- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
+++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java
@@ -125,6 +125,25 @@
@Test
@LargeTest
+ public void testAbandonStagedApkBeforeReady() throws Exception {
+ runPhase("testAbandonStagedApkBeforeReady_CommitAndAbandon");
+ getDevice().reboot();
+ runPhase("testAbandonStagedApkBeforeReady_VerifyPostReboot");
+ }
+
+ @Test
+ public void testStageAnotherSessionImmediatelyAfterAbandon() throws Exception {
+ runPhase("testStageAnotherSessionImmediatelyAfterAbandon");
+ }
+
+ @Test
+ public void testNoSessionUpdatedBroadcastSentForStagedSessionAbandon() throws Exception {
+ assumeTrue(isUpdatingApexSupported());
+ runPhase("testNoSessionUpdatedBroadcastSentForStagedSessionAbandon");
+ }
+
+ @Test
+ @LargeTest
public void testInstallMultipleStagedApks() throws Exception {
assumeSystemUser();
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 9742ee3..25727f1 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
@@ -62,6 +62,7 @@
import android.os.HandlerThread;
import android.os.Looper;
import android.os.PowerManager;
+import android.os.Process;
import android.os.SystemClock;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -85,6 +86,7 @@
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.BiConsumer;
public class AtomTests {
private static final String TAG = AtomTests.class.getSimpleName();
@@ -226,7 +228,68 @@
performBleScan(scanSettings, Arrays.asList(scanFilter.build()), true);
}
- private static void performBleScan(ScanSettings scanSettings, List<ScanFilter> scanFilters, boolean waitForResult) {
+ @Test
+ public void testBleScanInterrupted() throws Exception {
+ performBleAction((bluetoothAdapter, bleScanner) -> {
+ ScanSettings scanSettings = new ScanSettings.Builder()
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
+ ScanCallback scanCallback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ Log.v(TAG, "called onScanResult");
+ }
+ @Override
+ public void onScanFailed(int errorCode) {
+ Log.v(TAG, "called onScanFailed");
+ }
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ Log.v(TAG, "called onBatchScanResults");
+ }
+ };
+
+ int uid = Process.myUid();
+ int whatAtomId = 9_999;
+
+ // Change state to State.ON.
+ bleScanner.startScan(null, scanSettings, scanCallback);
+ sleep(500);
+ writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+ writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+ bluetoothAdapter.disable();
+ sleep(500);
+
+ // Trigger State.RESET so that new state is State.OFF.
+ if (!bluetoothAdapter.enable()) {
+ Log.e(TAG, "Could not enable bluetooth to trigger state reset");
+ return;
+ }
+ sleep(2_000); // Wait for Bluetooth to fully turn on.
+ writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+ writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+ writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+ });
+ }
+
+ private static void writeSliceByBleScanStateChangedAtom(int atomId, int firstUid,
+ boolean field2, boolean field3,
+ boolean field4) {
+ final StatsEvent.Builder builder = StatsEvent.newBuilder()
+ .setAtomId(atomId)
+ .writeAttributionChain(new int[] {firstUid}, new String[] {"tag1"})
+ .writeBoolean(field2)
+ .writeBoolean(field3)
+ .writeBoolean(field4)
+ .usePooledBuffer();
+
+ StatsLog.write(builder.build());
+ }
+
+ /**
+ * Set up BluetoothLeScanner and perform the action in the callback.
+ * Restore Bluetooth to original state afterwards.
+ **/
+ private static void performBleAction(BiConsumer<BluetoothAdapter, BluetoothLeScanner> actions) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Log.e(TAG, "Device does not support Bluetooth");
@@ -238,7 +301,7 @@
Log.e(TAG, "Bluetooth is not enabled");
return;
}
- sleep(8_000);
+ sleep(2_000); // Wait for Bluetooth to fully turn on.
bluetoothEnabledByTest = true;
}
BluetoothLeScanner bleScanner = bluetoothAdapter.getBluetoothLeScanner();
@@ -247,38 +310,45 @@
return;
}
- CountDownLatch resultsLatch = new CountDownLatch(1);
- ScanCallback scanCallback = new ScanCallback() {
- @Override
- public void onScanResult(int callbackType, ScanResult result) {
- Log.v(TAG, "called onScanResult");
- resultsLatch.countDown();
- }
- @Override
- public void onScanFailed(int errorCode) {
- Log.v(TAG, "called onScanFailed");
- }
- @Override
- public void onBatchScanResults(List<ScanResult> results) {
- Log.v(TAG, "called onBatchScanResults");
- resultsLatch.countDown();
- }
- };
+ actions.accept(bluetoothAdapter, bleScanner);
- bleScanner.startScan(scanFilters, scanSettings, scanCallback);
- if (waitForResult) {
- waitForReceiver(InstrumentationRegistry.getContext(), 59_000, resultsLatch, null);
- } else {
- sleep(2_000);
- }
- bleScanner.stopScan(scanCallback);
-
- // Restore adapter state at end of test
+ // Restore adapter state
if (bluetoothEnabledByTest) {
bluetoothAdapter.disable();
}
}
+
+ private static void performBleScan(ScanSettings scanSettings, List<ScanFilter> scanFilters, boolean waitForResult) {
+ performBleAction((bluetoothAdapter, bleScanner) -> {
+ CountDownLatch resultsLatch = new CountDownLatch(1);
+ ScanCallback scanCallback = new ScanCallback() {
+ @Override
+ public void onScanResult(int callbackType, ScanResult result) {
+ Log.v(TAG, "called onScanResult");
+ resultsLatch.countDown();
+ }
+ @Override
+ public void onScanFailed(int errorCode) {
+ Log.v(TAG, "called onScanFailed");
+ }
+ @Override
+ public void onBatchScanResults(List<ScanResult> results) {
+ Log.v(TAG, "called onBatchScanResults");
+ resultsLatch.countDown();
+ }
+ };
+
+ bleScanner.startScan(scanFilters, scanSettings, scanCallback);
+ if (waitForResult) {
+ waitForReceiver(InstrumentationRegistry.getContext(), 59_000, resultsLatch, null);
+ } else {
+ sleep(2_000);
+ }
+ bleScanner.stopScan(scanCallback);
+ });
+ }
+
@Test
public void testCameraState() throws Exception {
Context context = InstrumentationRegistry.getContext();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
index b455c36..fb08d2d 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/atom/UidAtomTests.java
@@ -131,7 +131,7 @@
Thread.sleep(WAIT_TIME_SHORT);
executeBackgroundService(ACTION_LMK);
- Thread.sleep(5_000);
+ Thread.sleep(15_000);
// Sorted list of events in order in which they occurred.
List<EventMetricData> data = getEventMetricDataList();
diff --git a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
index 4eeb74c..aaf94af 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/metric/CountMetricsTests.java
@@ -21,8 +21,12 @@
import android.cts.statsd.atom.DeviceAtomTestCase;
import com.android.internal.os.StatsdConfigProto;
+import com.android.internal.os.StatsdConfigProto.FieldMatcher;
+import com.android.internal.os.StatsdConfigProto.Position;
import com.android.os.AtomsProto.Atom;
import com.android.os.AtomsProto.AppBreadcrumbReported;
+import com.android.os.AtomsProto.AttributionNode;
+import com.android.os.AtomsProto.BleScanStateChanged;
import com.android.os.StatsLog;
import com.android.os.StatsLog.ConfigMetricsReport;
import com.android.os.StatsLog.ConfigMetricsReportList;
@@ -296,7 +300,7 @@
assertThat(bucketInfo.getCount()).isEqualTo(1);
assertWithMessage("First report's bucket should be less than 1 day")
.that(bucketInfo.getEndBucketElapsedNanos())
- .isLessThan(bucketInfo.getStartBucketElapsedNanos() +
+ .isLessThan(bucketInfo.getStartBucketElapsedNanos() +
1_000_000_000L * 60L * 60L * 24L);
//Second report should have a count of 2.
@@ -307,4 +311,118 @@
}
assertThat(totalCount).isEqualTo(2);
}
+
+ public void testSlicedStateCountMetric() throws Exception {
+ if (statsdDisabled()) {
+ return;
+ }
+ if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
+
+ int whatMatcherId = 3;
+ int stateId = 4;
+
+ // Atom 9999 {
+ // repeated AttributionNode attribution_node = 1;
+ // optional bool is_filtered = 2;
+ // optional bool is_first_match = 3;
+ // optional bool is_opportunistic = 4;
+ // }
+ int whatAtomId = 9_999;
+
+ StatsdConfigProto.AtomMatcher whatMatcher =
+ MetricsUtils.getAtomMatcher(whatAtomId)
+ .setId(whatMatcherId)
+ .build();
+
+ StatsdConfigProto.State state = StatsdConfigProto.State.newBuilder()
+ .setId(stateId)
+ .setAtomId(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER)
+ .build();
+
+ StatsdConfigProto.MetricStateLink stateLink = StatsdConfigProto.MetricStateLink.newBuilder()
+ .setStateAtomId(Atom.BLE_SCAN_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)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(4)
+ )
+ )
+ .setFieldsInState(FieldMatcher.newBuilder()
+ .setField(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(BleScanStateChanged.ATTRIBUTION_NODE_FIELD_NUMBER)
+ .setPosition(Position.FIRST)
+ .addChild(FieldMatcher.newBuilder()
+ .setField(AttributionNode.UID_FIELD_NUMBER)
+ )
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(BleScanStateChanged.IS_FILTERED_FIELD_NUMBER)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(BleScanStateChanged.IS_FIRST_MATCH_FIELD_NUMBER)
+ )
+ .addChild(FieldMatcher.newBuilder()
+ .setField(BleScanStateChanged.IS_OPPORTUNISTIC_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", "testBleScanInterrupted");
+
+ 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);
+
+ CountMetricData data = dataWrapper.getData(0);
+ assertThat(data.getSliceByStateCount()).isEqualTo(1);
+ assertThat(data.getSliceByState(0).getAtomId())
+ .isEqualTo(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER);
+ assertThat(data.getSliceByState(0).getValue())
+ .isEqualTo(BleScanStateChanged.State.OFF.ordinal());
+ long totalCount = data.getBucketInfoList().stream()
+ .mapToLong(CountBucketInfo::getCount)
+ .sum();
+ assertThat(totalCount).isEqualTo(3);
+
+ data = dataWrapper.getData(1);
+ assertThat(data.getSliceByStateCount()).isEqualTo(1);
+ assertThat(data.getSliceByState(0).getAtomId())
+ .isEqualTo(Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER);
+ assertThat(data.getSliceByState(0).getValue())
+ .isEqualTo(BleScanStateChanged.State.ON.ordinal());
+ totalCount = data.getBucketInfoList().stream()
+ .mapToLong(CountBucketInfo::getCount)
+ .sum();
+ assertThat(totalCount).isEqualTo(2);
+ }
}
diff --git a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
index 31401ca..ba980fb 100644
--- a/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
+++ b/hostsidetests/statsd/src/android/cts/statsd/subscriber/ShellSubscriberTest.java
@@ -159,6 +159,12 @@
while (output.length > startIndex) {
assertThat(output.length).isAtLeast(startIndex + sizetBytes);
int dataLength = readSizetFromByteArray(output, startIndex);
+ if (dataLength == 0) {
+ // We have received a heartbeat from statsd. This heartbeat isn't accompanied by any
+ // atoms so return to top of while loop.
+ startIndex += sizetBytes;
+ continue;
+ }
assertThat(output.length).isAtLeast(startIndex + sizetBytes + dataLength);
ShellDataProto.ShellData data = null;
diff --git a/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
index cf2abe8..4560ab1 100644
--- a/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
+++ b/libs/install/src/com/android/cts/install/lib/LocalIntentSender.java
@@ -63,6 +63,21 @@
return intent;
}
+ /**
+ * Returns an Intent that targets the given {@code sessionId}, while discarding others.
+ */
+ public static Intent getIntentSenderResult(int sessionId) throws InterruptedException {
+ while (true) {
+ Intent intent = sIntentSenderResults.take();
+ if (intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1) == sessionId) {
+ Log.i(TAG, "Taking intent " + prettyPrint(intent));
+ return intent;
+ } else {
+ Log.i(TAG, "Discarding intent " + prettyPrint(intent));
+ }
+ }
+ }
+
private static String prettyPrint(Intent intent) {
int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index eaea69b..faeb8f9 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -497,6 +497,9 @@
if (enable) {
SystemUtil.runShellCommand("svc wifi enable");
+ //noinspection deprecation
+ SystemUtil.runWithShellPermissionIdentity(wm::reconnect,
+ android.Manifest.permission.NETWORK_SETTINGS);
} else {
SystemUtil.runShellCommand("svc wifi disable");
}
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
index c3220ad..cfb04b4 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/utils/ActivityLaunchUtils.java
@@ -17,7 +17,6 @@
import static android.accessibilityservice.cts.utils.AsyncUtils.DEFAULT_TIMEOUT_MS;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.accessibilityservice.AccessibilityService;
@@ -31,7 +30,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
-import android.os.Bundle;
import android.os.PowerManager;
import android.os.SystemClock;
import android.text.TextUtils;
@@ -93,7 +91,12 @@
ActivityLauncher activityLauncher = new ActivityLauncher() {
@Override
Activity launchActivity() {
- return instrumentation.startActivitySync(intent, options.toBundle());
+ uiAutomation.adoptShellPermissionIdentity();
+ try {
+ return instrumentation.startActivitySync(intent, options.toBundle());
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
}
};
return launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(instrumentation,
diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
index 6b11ba6..c49e89a 100644
--- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
+++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java
@@ -1145,6 +1145,7 @@
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";
@@ -1194,6 +1195,13 @@
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);
}
}
}
diff --git a/tests/app/src/android/app/cts/UiModeManagerTest.java b/tests/app/src/android/app/cts/UiModeManagerTest.java
index 25ec9a3..1d443f6 100644
--- a/tests/app/src/android/app/cts/UiModeManagerTest.java
+++ b/tests/app/src/android/app/cts/UiModeManagerTest.java
@@ -34,6 +34,7 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.time.LocalTime;
public class UiModeManagerTest extends AndroidTestCase {
private static final String TAG = "UiModeManagerTest";
@@ -84,6 +85,28 @@
}
}
+ public void testSetAndGetCustomTimeStart() {
+ LocalTime time = mUiModeManager.getCustomNightModeStart();
+ // decrease time
+ LocalTime timeNew = LocalTime.of(
+ (time.getHour() + 1) % 12,
+ (time.getMinute() + 30) % 60);
+ setStartTime(timeNew);
+ assertNotSame(time, timeNew);
+ assertEquals(timeNew, mUiModeManager.getCustomNightModeStart());
+ }
+
+ public void testSetAndGetCustomTimeEnd() {
+ LocalTime time = mUiModeManager.getCustomNightModeEnd();
+ // decrease time
+ LocalTime timeNew = LocalTime.of(
+ (time.getHour() + 1) % 12,
+ (time.getMinute() + 30) % 60);
+ setEndTime(timeNew);
+ assertNotSame(time, timeNew);
+ assertEquals(timeNew, mUiModeManager.getCustomNightModeEnd());
+ }
+
public void testNightModeYesPersisted() throws InterruptedException {
// Reset the mode to no if it is set to another value
setNightMode(UiModeManager.MODE_NIGHT_NO);
@@ -327,7 +350,6 @@
}
private void setNightMode(int mode) {
- final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
String modeString = "unknown";
switch (mode) {
case UiModeManager.MODE_NIGHT_AUTO:
@@ -341,6 +363,21 @@
break;
}
final String command = " cmd uimode night " + modeString;
+ applyCommand(command);
+ }
+
+ private void setStartTime(LocalTime t) {
+ final String command = " cmd uimode time start " + t.toString();
+ applyCommand(command);
+ }
+
+ private void setEndTime(LocalTime t) {
+ final String command = " cmd uimode time end " + t.toString();
+ applyCommand(command);
+ }
+
+ private void applyCommand(String command) {
+ final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
Assert.assertNotNull("Failed to execute shell command: " + command, fd);
// Wait for the command to finish by reading until EOF
@@ -356,4 +393,5 @@
uiAutomation.destroy();
}
}
+
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 65df6fb..5b1a86e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -210,7 +210,6 @@
}
};
- @ClassRule
public static final MockImeSessionRule sMockImeSessionRule = new MockImeSessionRule(
InstrumentationRegistry.getTargetContext(),
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
@@ -248,6 +247,9 @@
// test being ran and finishes dangling activities at the end
.around(mTestWatcher)
//
+ // sMockImeSessionRule make sure MockImeSession.create() is used to launch mock IME
+ .around(sMockImeSessionRule)
+ //
// mLoggingRule wraps the test but doesn't interfere with it
.around(mLoggingRule)
//
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 19223b0..5fe5d2f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -56,8 +56,10 @@
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
@@ -79,6 +81,7 @@
private Handler mHandler;
private BlockingStateCallback mCameraListener;
private CameraErrorCollector mCollector;
+ private Set<Set<String>> mConcurrentCameraIdCombinations;
/** Load validation jni on initialization. */
static {
@@ -106,6 +109,8 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mCollector = new CameraErrorCollector();
+ mConcurrentCameraIdCombinations =
+ CameraTestUtils.getConcurrentCameraIds(mCameraManager, mAdoptShellPerm);
}
@Override
@@ -160,10 +165,12 @@
* must be matched system features.
*/
boolean externalCameraConnected = false;
+ Map<String, Integer> lensFacingMap = new HashMap<String, Integer>();
for (int i = 0; i < ids.length; i++) {
CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
assertNotNull("Can't get camera characteristics for camera " + ids[i], props);
Integer lensFacing = props.get(CameraCharacteristics.LENS_FACING);
+ lensFacingMap.put(ids[i], lensFacing);
assertNotNull("Can't get lens facing info", lensFacing);
if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
assertTrue("System doesn't have front camera feature",
@@ -198,6 +205,38 @@
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)
|| mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
+
+ boolean frontBackAdvertised =
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_CONCURRENT);
+
+ boolean frontBackCombinationFound = false;
+ // Go through all combinations and see that at least one combination has a front + back
+ // camera.
+ for (Set<String> cameraIdCombination : mConcurrentCameraIdCombinations) {
+ boolean frontFacingFound = false, backFacingFound = false;
+ for (String cameraId : cameraIdCombination) {
+ Integer lensFacing = lensFacingMap.get(cameraId);
+ if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
+ frontFacingFound = true;
+ } else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
+ backFacingFound = true;
+ }
+ if (frontFacingFound && backFacingFound) {
+ frontBackCombinationFound = true;
+ break;
+ }
+ }
+ if (frontBackCombinationFound) {
+ break;
+ }
+ }
+
+ if(mCameraIdsUnderTest.length > 0) {
+ assertTrue("System camera feature FEATURE_CAMERA_CONCURRENT = " + frontBackAdvertised +
+ " and device actually having a front back combination which can operate " +
+ "concurrently = " + frontBackCombinationFound + " do not match",
+ frontBackAdvertised == frontBackCombinationFound);
+ }
}
// Test: that properties can be queried from each device, without exceptions.
diff --git a/tests/framework/base/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
index b940aae..9425c2d 100644
--- a/tests/framework/base/windowmanager/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -48,6 +48,13 @@
<option name="test-file-name" value="CtsDragAndDropTargetAppSdk23.apk"/>
<option name="test-file-name" value="CtsDeviceAlertWindowTestAppSdk25.apk"/>
</target_preparer>
+ <!-- Necessary for Automotive devices - no-op for other Android verticals -->
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command"
+ value="settings put secure android.car.ENABLE_INITIAL_NOTICE_SCREEN_TO_USER 0" />
+ <option name="teardown-command"
+ value="settings put secure android.car.ENABLE_INITIAL_NOTICE_SCREEN_TO_USER 1" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="android.server.wm.cts"/>
<option name="runtime-hint" value="1h"/>
diff --git a/tests/framework/base/windowmanager/backgroundactivity/OWNERS b/tests/framework/base/windowmanager/backgroundactivity/OWNERS
new file mode 100644
index 0000000..fcaa235
--- /dev/null
+++ b/tests/framework/base/windowmanager/backgroundactivity/OWNERS
@@ -0,0 +1,2 @@
+alanstokes@google.com
+brufino@google.com
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 09c8fc2..19ba5c0 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
@@ -454,10 +454,12 @@
// 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();
}
private void assertTaskStack(ComponentName[] expectedComponents,
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
index 1b6aa86..9f71d26 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/DreamManagerServiceTests.java
@@ -22,10 +22,14 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static org.junit.Assume.assumeTrue;
import android.app.DreamManager;
import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
+import android.view.Surface;
import androidx.test.filters.FlakyTest;
@@ -79,7 +83,7 @@
});
}
- private void assertDreamActivityIsGone() {
+ private void assertDreamActivityGone() {
mWmState.computeState();
assertTrue(!mWmState.containsWindow(getWindowName(mDreamActivityName))
&& !mWmState.containsActivity(mDreamActivityName));
@@ -90,16 +94,18 @@
setActiveDream(TEST_DREAM_SERVICE);
startDream(TEST_DREAM_SERVICE);
- mWmState.waitForValidState(mDreamActivityName);
- mWmState.assertVisibility(mDreamActivityName, true);
- mWmState.assertHomeActivityVisible(false);
+ waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+ "Dream activity should be the top resumed activity");
+ mWmState.waitForValidState(mWmState.getHomeActivityName());
+ mWmState.assertVisibility(mWmState.getHomeActivityName(), false);
assertTrue(getIsDreaming());
stopDream();
mWmState.waitAndAssertActivityRemoved(mDreamActivityName);
- mWmState.assertHomeActivityVisible(true);
+ waitAndAssertTopResumedActivity(mWmState.getHomeActivityName(), DEFAULT_DISPLAY,
+ "Home activity should show when dream is stopped");
}
@Test
@@ -107,14 +113,17 @@
setActiveDream(TEST_DREAM_SERVICE);
startDream(TEST_DREAM_SERVICE);
- mWmState.waitForValidState(mDreamActivityName);
+ waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+ "Dream activity should be the top resumed activity");
+ mWmState.waitForValidState(mWmState.getHomeActivityName());
+ mWmState.assertVisibility(mWmState.getHomeActivityName(), false);
assertTrue(getIsDreaming());
stopDream();
Thread.sleep(ACTIVITY_STOP_TIMEOUT);
- assertDreamActivityIsGone();
+ assertDreamActivityGone();
assertFalse(getIsDreaming());
}
@@ -123,15 +132,32 @@
setActiveDream(TEST_STUBBORN_DREAM_SERVICE);
startDream(TEST_STUBBORN_DREAM_SERVICE);
- mWmState.waitForValidState(mDreamActivityName);
- mWmState.assertVisibility(mDreamActivityName, true);
- mWmState.assertHomeActivityVisible(false);
+ waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+ "Dream activity should be the top resumed activity");
+ mWmState.waitForValidState(mWmState.getHomeActivityName());
+ mWmState.assertVisibility(mWmState.getHomeActivityName(), false);
stopDream();
Thread.sleep(ACTIVITY_FORCE_STOP_TIMEOUT);
- assertDreamActivityIsGone();
+ assertDreamActivityGone();
assertFalse(getIsDreaming());
+ waitAndAssertTopResumedActivity(mWmState.getHomeActivityName(), DEFAULT_DISPLAY,
+ "Home activity should show when dream is stopped");
+ }
+
+ @Test
+ public void testDreamNotFinishAfterRotation() {
+ assumeTrue("Skipping test: no rotation support", supportsRotation());
+
+ final RotationSession rotationSession = createManagedRotationSession();
+ rotationSession.set(Surface.ROTATION_0);
+ setActiveDream(TEST_DREAM_SERVICE);
+ startDream(TEST_DREAM_SERVICE);
+ rotationSession.set(Surface.ROTATION_90);
+
+ waitAndAssertTopResumedActivity(mDreamActivityName, DEFAULT_DISPLAY,
+ "Dream activity should be the top resumed activity");
}
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
index c699460..115310f 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java
@@ -33,9 +33,12 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
+
import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static androidx.test.InstrumentationRegistry.getInstrumentation;
@@ -52,13 +55,15 @@
import com.android.compatibility.common.util.CtsTouchUtils;
import com.android.compatibility.common.util.PollingCheck;
+
import com.android.cts.mockime.ImeEventStream;
import com.android.cts.mockime.MockImeSession;
-import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
+import java.util.concurrent.TimeUnit;
+
/**
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:KeyguardLockedTests
@@ -104,8 +109,14 @@
keyguardLock.disableKeyguard();
lockScreenSession.setLockCredential();
+ lockScreenSession.gotoKeyguard();
+
mWmState.waitForKeyguardShowingAndNotOccluded();
mWmState.assertKeyguardShowingAndNotOccluded();
+ assertTrue(mKeyguardManager.isKeyguardLocked());
+ assertTrue(mKeyguardManager.isDeviceLocked());
+ assertTrue(mKeyguardManager.isDeviceSecure());
+ assertTrue(mKeyguardManager.isKeyguardSecure());
} finally {
keyguardLock.reenableKeyguard();
}
@@ -183,6 +194,9 @@
@Test
public void testDismissKeyguardActivity_method_cancelled() {
+ // Pressing the back button does not cancel Keyguard in AAOS.
+ assumeFalse(isCar());
+
final LockScreenSession lockScreenSession = createManagedLockScreenSession();
lockScreenSession.setLockCredential();
separateTestJournal();
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 538ae84..474d9d2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java
@@ -190,6 +190,12 @@
addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
mInstrumentation.waitForIdleSync();
+ // If we don't support hardware acceleration on the main activity the embedded
+ // view also won't be.
+ if (!mSurfaceView.isHardwareAccelerated()) {
+ return;
+ }
+
assertTrue(mEmbeddedView.isHardwareAccelerated());
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
index a9ecab2..51645f1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityLifecycleSplitScreenTests.java
@@ -46,7 +46,6 @@
import android.content.Intent;
import android.platform.test.annotations.Presubmit;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import org.junit.Before;
@@ -73,7 +72,6 @@
mUseTaskOrganizer = false;
}
- @FlakyTest(bugId = 137329632)
@Test
public void testResumedWhenRecreatedFromInNonFocusedStack() throws Exception {
// Launch first activity
diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java
index 279f74d..8a6ba7c 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
@@ -987,6 +987,10 @@
return hasDeviceFeature(FEATURE_WATCH);
}
+ protected boolean isCar() {
+ return hasDeviceFeature(FEATURE_AUTOMOTIVE);
+ }
+
protected boolean isTablet() {
// Larger than approx 7" tablets
return mContext.getResources().getConfiguration().smallestScreenWidthDp >= 600;
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
index 2d3a344..23e4dd8 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/FocusHandlingTest.java
@@ -562,9 +562,7 @@
CtsTouchUtils.emulateTapOnViewCenter(instrumentation, null, editText);
TestUtils.waitOnMainUntil(() -> editTextHasWindowFocus.get()
&& !popupTextHasWindowFocus.get(), TIMEOUT);
- // Expect there is no "onStartInput" when window focus back to activity's EditText.
- // Since the EditText still has view focus and served by InputMethodManager.
- notExpectEvent(stream, editorMatcher("onStartInput", marker), NOT_EXPECT_TIMEOUT);
+ expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
expectEvent(stream, event -> "showSoftInput".equals(event.getEventName()), TIMEOUT);
}
}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
index 3a57360..b817a2c 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/WindowFocusHandleService.java
@@ -21,7 +21,6 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.app.Service;
@@ -131,12 +130,6 @@
150, 150, pos.x, pos.y,
TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE,
PixelFormat.OPAQUE);
- // Currently SOFT_INPUT_STATE_UNSPECIFIED isn't appropriate for CTS test because there is no
- // clear spec about how it behaves. In order to make our tests deterministic, currently we
- // must use SOFT_INPUT_STATE_HIDDEN to make sure soft-keyboard will hide after navigating
- // forward to next window.
- // TODO(Bug 77152727): Remove the following code once we define how
- params.softInputMode = SOFT_INPUT_STATE_HIDDEN;
wm.addView(editText, params);
return editText;
}
diff --git a/tests/media/AndroidManifest.xml b/tests/media/AndroidManifest.xml
index 7cd34c5..3ccd92e 100644
--- a/tests/media/AndroidManifest.xml
+++ b/tests/media/AndroidManifest.xml
@@ -28,6 +28,7 @@
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
+ <activity android:name="android.mediav2.cts.CodecTestActivity" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.mediav2.cts"
diff --git a/tests/media/jni/Android.bp b/tests/media/jni/Android.bp
index 0db7dad..1e13378 100644
--- a/tests/media/jni/Android.bp
+++ b/tests/media/jni/Android.bp
@@ -15,7 +15,7 @@
cc_test_library {
name: "libctsmediav2muxer_jni",
srcs: [
- "NativeMediaConstants.cpp",
+ "NativeMediaCommon.cpp",
"NativeMuxerTest.cpp",
"NativeMuxerUnitTest.cpp",
],
@@ -37,7 +37,7 @@
cc_test_library {
name: "libctsmediav2extractor_jni",
srcs: [
- "NativeMediaConstants.cpp",
+ "NativeMediaCommon.cpp",
"NativeExtractorTest.cpp",
"NativeExtractorUnitTest.cpp",
],
@@ -59,15 +59,17 @@
cc_test_library {
name: "libctsmediav2codec_jni",
srcs: [
- "NativeMediaConstants.cpp",
+ "NativeMediaCommon.cpp",
"NativeCodecDecoderTest.cpp",
"NativeCodecEncoderTest.cpp",
"NativeCodecTestBase.cpp",
"NativeCodecUnitTest.cpp",
+ "NativeCodecEncoderSurfaceTest.cpp",
],
shared_libs: [
"libmediandk",
"liblog",
+ "libandroid",
],
include_dirs: [
"frameworks/av/media/ndk/include/media",
diff --git a/tests/media/jni/NativeCodecDecoderTest.cpp b/tests/media/jni/NativeCodecDecoderTest.cpp
index fb9c66d..d5eb85b 100644
--- a/tests/media/jni/NativeCodecDecoderTest.cpp
+++ b/tests/media/jni/NativeCodecDecoderTest.cpp
@@ -17,7 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "NativeCodecDecoderTest"
#include <log/log.h>
-
+#include <android/native_window_jni.h>
#include <NdkMediaExtractor.h>
#include <jni.h>
#include <sys/stat.h>
@@ -26,7 +26,7 @@
#include <fstream>
#include "NativeCodecTestBase.h"
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
class CodecDecoderTest final : public CodecTestBase {
private:
@@ -37,6 +37,7 @@
AMediaFormat* mInpDecDupFormat;
std::vector<std::pair<void*, size_t>> mCsdBuffers;
int mCurrCsdIdx;
+ ANativeWindow* mWindow;
void setUpAudioReference(const char* refFile);
void deleteReference();
@@ -52,7 +53,7 @@
OutputManager* ref, int64_t pts, SeekMode mode);
public:
- explicit CodecDecoderTest(const char* mime);
+ explicit CodecDecoderTest(const char* mime, ANativeWindow* window);
~CodecDecoderTest();
bool testSimpleDecode(const char* decoder, const char* testFile, const char* refFile,
@@ -62,14 +63,15 @@
bool testSimpleDecodeQueueCSD(const char* decoder, const char* testFile);
};
-CodecDecoderTest::CodecDecoderTest(const char* mime)
+CodecDecoderTest::CodecDecoderTest(const char* mime, ANativeWindow* window)
: CodecTestBase(mime),
mRefData(nullptr),
mRefLength(0),
mExtractor(nullptr),
mInpDecFormat(nullptr),
mInpDecDupFormat(nullptr),
- mCurrCsdIdx(0) {}
+ mCurrCsdIdx(0),
+ mWindow{window} {}
CodecDecoderTest::~CodecDecoderTest() {
deleteReference();
@@ -151,7 +153,7 @@
resetContext(isAsync, signalEOSWithLastFrame);
CHECK_STATUS(mAsyncHandle.setCallBack(mCodec, isAsync),
"AMediaCodec_setAsyncNotifyCallback failed");
- CHECK_STATUS(AMediaCodec_configure(mCodec, format, nullptr, nullptr,
+ CHECK_STATUS(AMediaCodec_configure(mCodec, format, mWindow, nullptr,
isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0),
"AMediaCodec_configure failed");
return true;
@@ -229,7 +231,7 @@
}
ALOGV("output: id: %zu size: %d pts: %d flags: %d", bufferIndex, info->size,
(int)info->presentationTimeUs, info->flags);
- CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, false),
+ CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, mWindow != nullptr),
"AMediaCodec_releaseOutputBuffer failed");
return !hasSeenError();
}
@@ -261,7 +263,7 @@
bool CodecDecoderTest::decodeToMemory(const char* decoder, AMediaFormat* format, int frameLimit,
OutputManager* ref, int64_t pts, SeekMode mode) {
- mSaveToMem = true;
+ mSaveToMem = (mWindow == nullptr);
mOutputBuff = ref;
AMediaExtractor_seekTo(mExtractor, pts, mode);
mCodec = AMediaCodec_createCodecByName(decoder);
@@ -285,7 +287,7 @@
const char* refFile, float rmsError) {
bool isPass = true;
if (!setUpExtractor(testFile)) return false;
- mSaveToMem = true;
+ mSaveToMem = (mWindow == nullptr);
auto ref = &mRefBuff;
auto test = &mTestBuff;
const bool boolStates[]{true, false};
@@ -451,7 +453,7 @@
if (mIsCodecInAsyncMode) {
CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
}
- mSaveToMem = true;
+ mSaveToMem = (mWindow == nullptr);
test->reset();
AMediaExtractor_seekTo(mExtractor, pts, mode);
if (!doWork(INT32_MAX)) return false;
@@ -504,7 +506,7 @@
bool CodecDecoderTest::testOnlyEos(const char* decoder, const char* testFile) {
bool isPass = true;
if (!setUpExtractor(testFile)) return false;
- mSaveToMem = true;
+ mSaveToMem = (mWindow == nullptr);
auto ref = &mRefBuff;
auto test = &mTestBuff;
const bool boolStates[]{true, false};
@@ -638,16 +640,22 @@
return isPass;
}
-static jboolean nativeTestSimpleDecode(JNIEnv* env, jobject, jstring jDecoder, jstring jMime,
- jstring jtestFile, jstring jrefFile, jfloat jrmsError) {
+static jboolean nativeTestSimpleDecode(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
+ jstring jMime, jstring jtestFile, jstring jrefFile,
+ jfloat jrmsError) {
const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
const char* cMime = env->GetStringUTFChars(jMime, nullptr);
const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
const char* cRefFile = env->GetStringUTFChars(jrefFile, nullptr);
float cRmsError = jrmsError;
- auto codecDecoderTest = new CodecDecoderTest(cMime);
+ ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
+ auto* codecDecoderTest = new CodecDecoderTest(cMime, window);
bool isPass = codecDecoderTest->testSimpleDecode(cDecoder, cTestFile, cRefFile, cRmsError);
delete codecDecoderTest;
+ if (window) {
+ ANativeWindow_release(window);
+ window = nullptr;
+ }
env->ReleaseStringUTFChars(jDecoder, cDecoder);
env->ReleaseStringUTFChars(jMime, cMime);
env->ReleaseStringUTFChars(jtestFile, cTestFile);
@@ -660,7 +668,7 @@
const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
const char* cMime = env->GetStringUTFChars(jMime, nullptr);
const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
- auto codecDecoderTest = new CodecDecoderTest(cMime);
+ auto* codecDecoderTest = new CodecDecoderTest(cMime, nullptr);
bool isPass = codecDecoderTest->testOnlyEos(cDecoder, cTestFile);
delete codecDecoderTest;
env->ReleaseStringUTFChars(jDecoder, cDecoder);
@@ -669,14 +677,19 @@
return static_cast<jboolean>(isPass);
}
-static jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jDecoder, jstring jMime,
- jstring jtestFile) {
+static jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
+ jstring jMime, jstring jtestFile) {
const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
const char* cMime = env->GetStringUTFChars(jMime, nullptr);
const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
- auto codecDecoderTest = new CodecDecoderTest(cMime);
+ ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
+ auto* codecDecoderTest = new CodecDecoderTest(cMime, window);
bool isPass = codecDecoderTest->testFlush(cDecoder, cTestFile);
delete codecDecoderTest;
+ if (window) {
+ ANativeWindow_release(window);
+ window = nullptr;
+ }
env->ReleaseStringUTFChars(jDecoder, cDecoder);
env->ReleaseStringUTFChars(jMime, cMime);
env->ReleaseStringUTFChars(jtestFile, cTestFile);
@@ -688,7 +701,7 @@
const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
const char* cMime = env->GetStringUTFChars(jMime, nullptr);
const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
- auto codecDecoderTest = new CodecDecoderTest(cMime);
+ auto codecDecoderTest = new CodecDecoderTest(cMime, nullptr);
bool isPass = codecDecoderTest->testSimpleDecodeQueueCSD(cDecoder, cTestFile);
delete codecDecoderTest;
env->ReleaseStringUTFChars(jDecoder, cDecoder);
@@ -700,11 +713,13 @@
int registerAndroidMediaV2CtsDecoderTest(JNIEnv* env) {
const JNINativeMethod methodTable[] = {
{"nativeTestSimpleDecode",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;F)Z",
+ "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;Ljava/"
+ "lang/String;F)Z",
(void*)nativeTestSimpleDecode},
{"nativeTestOnlyEos", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
(void*)nativeTestOnlyEos},
- {"nativeTestFlush", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+ {"nativeTestFlush",
+ "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;)Z",
(void*)nativeTestFlush},
{"nativeTestSimpleDecodeQueueCSD",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
@@ -713,3 +728,17 @@
jclass c = env->FindClass("android/mediav2/cts/CodecDecoderTest");
return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
}
+
+int registerAndroidMediaV2CtsDecoderSurfaceTest(JNIEnv* env) {
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestSimpleDecode",
+ "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;Ljava/"
+ "lang/String;F)Z",
+ (void*)nativeTestSimpleDecode},
+ {"nativeTestFlush",
+ "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;)Z",
+ (void*)nativeTestFlush},
+ };
+ jclass c = env->FindClass("android/mediav2/cts/CodecDecoderSurfaceTest");
+ return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
diff --git a/tests/media/jni/NativeCodecEncoderSurfaceTest.cpp b/tests/media/jni/NativeCodecEncoderSurfaceTest.cpp
new file mode 100644
index 0000000..2c76eb1
--- /dev/null
+++ b/tests/media/jni/NativeCodecEncoderSurfaceTest.cpp
@@ -0,0 +1,586 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeCodecEncoderSurfaceTest"
+#include <log/log.h>
+#include <android/native_window_jni.h>
+#include <NdkMediaExtractor.h>
+#include <NdkMediaMuxer.h>
+#include <jni.h>
+#include <sys/stat.h>
+
+#include "NativeCodecTestBase.h"
+#include "NativeMediaCommon.h"
+
+class CodecEncoderSurfaceTest {
+ private:
+ const long kQDeQTimeOutUs = 5000;
+ const char* mMime;
+ ANativeWindow* mWindow;
+ AMediaExtractor* mExtractor;
+ AMediaFormat* mDecFormat;
+ AMediaFormat* mEncFormat;
+ AMediaMuxer* mMuxer;
+ AMediaCodec* mDecoder;
+ AMediaCodec* mEncoder;
+ CodecAsyncHandler mAsyncHandleDecoder;
+ CodecAsyncHandler mAsyncHandleEncoder;
+ bool mIsCodecInAsyncMode;
+ bool mSawDecInputEOS;
+ bool mSawDecOutputEOS;
+ bool mSawEncOutputEOS;
+ bool mSignalEOSWithLastFrame;
+ int mDecInputCount;
+ int mDecOutputCount;
+ int mEncOutputCount;
+ int mEncBitrate;
+ int mEncFramerate;
+ int mMaxBFrames;
+ int mMuxTrackID;
+
+ OutputManager* mOutputBuff;
+ OutputManager mRefBuff;
+ OutputManager mTestBuff;
+ bool mSaveToMem;
+
+ bool setUpExtractor(const char* srcPath);
+ void deleteExtractor();
+ bool configureCodec(bool isAsync, bool signalEOSWithLastFrame);
+ void resetContext(bool isAsync, bool signalEOSWithLastFrame);
+ void setUpEncoderFormat();
+ bool enqueueDecoderInput(size_t bufferIndex);
+ bool dequeueDecoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo);
+ bool dequeueEncoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* info);
+ bool tryEncoderOutput(long timeOutUs);
+ bool waitForAllEncoderOutputs();
+ bool queueEOS();
+ bool enqueueDecoderEOS(size_t bufferIndex);
+ bool doWork(int frameLimit);
+ bool hasSeenError() { return mAsyncHandleDecoder.getError() || mAsyncHandleEncoder.getError(); }
+
+ public:
+ CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate);
+ ~CodecEncoderSurfaceTest();
+
+ bool testSimpleEncode(const char* encoder, const char* decoder, const char* srcPath,
+ const char* muxOutPath);
+};
+
+CodecEncoderSurfaceTest::CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate)
+ : mMime{mime}, mEncBitrate{bitrate}, mEncFramerate{framerate} {
+ mWindow = nullptr;
+ mExtractor = nullptr;
+ mDecFormat = nullptr;
+ mEncFormat = nullptr;
+ mMuxer = nullptr;
+ mDecoder = nullptr;
+ mEncoder = nullptr;
+ resetContext(false, false);
+ mMaxBFrames = 0;
+ mMuxTrackID = -1;
+}
+
+CodecEncoderSurfaceTest::~CodecEncoderSurfaceTest() {
+ deleteExtractor();
+ if (mWindow) {
+ ANativeWindow_release(mWindow);
+ mWindow = nullptr;
+ }
+ if (mEncFormat) {
+ AMediaFormat_delete(mEncFormat);
+ mEncFormat = nullptr;
+ }
+ if (mMuxer) {
+ AMediaMuxer_delete(mMuxer);
+ mMuxer = nullptr;
+ }
+ if (mDecoder) {
+ AMediaCodec_delete(mDecoder);
+ mDecoder = nullptr;
+ }
+ if (mEncoder) {
+ AMediaCodec_delete(mEncoder);
+ mEncoder = nullptr;
+ }
+}
+
+bool CodecEncoderSurfaceTest::setUpExtractor(const char* srcFile) {
+ FILE* fp = fopen(srcFile, "rbe");
+ struct stat buf {};
+ if (fp && !fstat(fileno(fp), &buf)) {
+ deleteExtractor();
+ mExtractor = AMediaExtractor_new();
+ media_status_t res =
+ AMediaExtractor_setDataSourceFd(mExtractor, fileno(fp), 0, buf.st_size);
+ if (res != AMEDIA_OK) {
+ deleteExtractor();
+ } else {
+ for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(mExtractor);
+ trackID++) {
+ AMediaFormat* currFormat = AMediaExtractor_getTrackFormat(mExtractor, trackID);
+ const char* mime = nullptr;
+ AMediaFormat_getString(currFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ if (mime && strncmp(mime, "video/", strlen("video/")) == 0) {
+ AMediaExtractor_selectTrack(mExtractor, trackID);
+ AMediaFormat_setInt32(currFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT,
+ COLOR_FormatYUV420Flexible);
+ mDecFormat = currFormat;
+ break;
+ }
+ AMediaFormat_delete(currFormat);
+ }
+ }
+ }
+ if (fp) fclose(fp);
+ return mDecFormat != nullptr;
+}
+
+void CodecEncoderSurfaceTest::deleteExtractor() {
+ if (mExtractor) {
+ AMediaExtractor_delete(mExtractor);
+ mExtractor = nullptr;
+ }
+ if (mDecFormat) {
+ AMediaFormat_delete(mDecFormat);
+ mDecFormat = nullptr;
+ }
+}
+
+bool CodecEncoderSurfaceTest::configureCodec(bool isAsync, bool signalEOSWithLastFrame) {
+ resetContext(isAsync, signalEOSWithLastFrame);
+ CHECK_STATUS(mAsyncHandleEncoder.setCallBack(mEncoder, isAsync),
+ "AMediaCodec_setAsyncNotifyCallback failed");
+ CHECK_STATUS(AMediaCodec_configure(mEncoder, mEncFormat, nullptr, nullptr,
+ AMEDIACODEC_CONFIGURE_FLAG_ENCODE),
+ "AMediaCodec_configure failed");
+ CHECK_STATUS(AMediaCodec_createInputSurface(mEncoder, &mWindow),
+ "AMediaCodec_createInputSurface failed");
+ CHECK_STATUS(mAsyncHandleDecoder.setCallBack(mDecoder, isAsync),
+ "AMediaCodec_setAsyncNotifyCallback failed");
+ CHECK_STATUS(AMediaCodec_configure(mDecoder, mDecFormat, mWindow, nullptr, 0),
+ "AMediaCodec_configure failed");
+ return !hasSeenError();
+}
+
+void CodecEncoderSurfaceTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) {
+ mAsyncHandleDecoder.resetContext();
+ mAsyncHandleEncoder.resetContext();
+ mIsCodecInAsyncMode = isAsync;
+ mSawDecInputEOS = false;
+ mSawDecOutputEOS = false;
+ mSawEncOutputEOS = false;
+ mSignalEOSWithLastFrame = signalEOSWithLastFrame;
+ mDecInputCount = 0;
+ mDecOutputCount = 0;
+ mEncOutputCount = 0;
+}
+
+void CodecEncoderSurfaceTest::setUpEncoderFormat() {
+ if (mEncFormat) AMediaFormat_delete(mEncFormat);
+ mEncFormat = AMediaFormat_new();
+ int width, height;
+ AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_WIDTH, &width);
+ AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_HEIGHT, &height);
+ AMediaFormat_setString(mEncFormat, AMEDIAFORMAT_KEY_MIME, mMime);
+ AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_WIDTH, width);
+ AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_HEIGHT, height);
+ AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_BIT_RATE, mEncBitrate);
+ AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_FRAME_RATE, mEncFramerate);
+ AMediaFormat_setInt32(mEncFormat, TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES, mMaxBFrames);
+ AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatSurface);
+ AMediaFormat_setFloat(mEncFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1.0F);
+}
+
+bool CodecEncoderSurfaceTest::enqueueDecoderEOS(size_t bufferIndex) {
+ if (!hasSeenError() && !mSawDecInputEOS) {
+ CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, 0, 0,
+ AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM),
+ "Queued Decoder End of Stream Failed");
+ mSawDecInputEOS = true;
+ ALOGV("Queued Decoder End of Stream");
+ }
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::enqueueDecoderInput(size_t bufferIndex) {
+ if (AMediaExtractor_getSampleSize(mExtractor) < 0) {
+ return enqueueDecoderEOS(bufferIndex);
+ } else {
+ uint32_t flags = 0;
+ size_t bufSize = 0;
+ uint8_t* buf = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufSize);
+ if (buf == nullptr) {
+ ALOGE("AMediaCodec_getInputBuffer failed");
+ return false;
+ }
+ ssize_t size = AMediaExtractor_getSampleSize(mExtractor);
+ int64_t pts = AMediaExtractor_getSampleTime(mExtractor);
+ if (size > bufSize) {
+ ALOGE("extractor sample size exceeds codec input buffer size %zu %zu", size, bufSize);
+ return false;
+ }
+ if (size != AMediaExtractor_readSampleData(mExtractor, buf, bufSize)) {
+ ALOGE("AMediaExtractor_readSampleData failed");
+ return false;
+ }
+ if (!AMediaExtractor_advance(mExtractor) && mSignalEOSWithLastFrame) {
+ flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
+ mSawDecInputEOS = true;
+ }
+ CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, size, pts, flags),
+ "AMediaCodec_queueInputBuffer failed");
+ ALOGV("input: id: %zu size: %zu pts: %d flags: %d", bufferIndex, size, (int)pts, flags);
+ if (size > 0) {
+ mOutputBuff->saveInPTS(pts);
+ mDecInputCount++;
+ }
+ }
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::dequeueDecoderOutput(size_t bufferIndex,
+ AMediaCodecBufferInfo* bufferInfo) {
+ if ((bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawDecOutputEOS = true;
+ }
+ if (bufferInfo->size > 0 && (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mDecOutputCount++;
+ }
+ ALOGV("output: id: %zu size: %d pts: %d flags: %d", bufferIndex, bufferInfo->size,
+ (int)bufferInfo->presentationTimeUs, bufferInfo->flags);
+ CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, mWindow != nullptr),
+ "AMediaCodec_releaseOutputBuffer failed");
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::dequeueEncoderOutput(size_t bufferIndex,
+ AMediaCodecBufferInfo* info) {
+ if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawEncOutputEOS = true;
+ }
+ if (info->size > 0) {
+ size_t buffSize;
+ uint8_t* buf = AMediaCodec_getOutputBuffer(mEncoder, bufferIndex, &buffSize);
+ if (mSaveToMem) {
+ mOutputBuff->saveToMemory(buf, info);
+ }
+ if (mMuxer != nullptr) {
+ if (mMuxTrackID == -1) {
+ mMuxTrackID = AMediaMuxer_addTrack(mMuxer, AMediaCodec_getOutputFormat(mEncoder));
+ CHECK_STATUS(AMediaMuxer_start(mMuxer), "AMediaMuxer_start failed");
+ }
+ CHECK_STATUS(AMediaMuxer_writeSampleData(mMuxer, mMuxTrackID, buf, info),
+ "AMediaMuxer_writeSampleData failed");
+ }
+ if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
+ mOutputBuff->saveOutPTS(info->presentationTimeUs);
+ mEncOutputCount++;
+ }
+ }
+ ALOGV("output: id: %zu size: %d pts: %d flags: %d", bufferIndex, info->size,
+ (int)info->presentationTimeUs, info->flags);
+ CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mEncoder, bufferIndex, false),
+ "AMediaCodec_releaseOutputBuffer failed");
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::tryEncoderOutput(long timeOutUs) {
+ if (mIsCodecInAsyncMode) {
+ if (!hasSeenError() && !mSawEncOutputEOS) {
+ callbackObject element = mAsyncHandleEncoder.getOutput();
+ if (element.bufferIndex >= 0) {
+ if (!dequeueEncoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
+ }
+ }
+ } else {
+ AMediaCodecBufferInfo outInfo;
+ if (!mSawEncOutputEOS) {
+ int bufferID = AMediaCodec_dequeueOutputBuffer(mEncoder, &outInfo, timeOutUs);
+ if (bufferID >= 0) {
+ if (!dequeueEncoderOutput(bufferID, &outInfo)) return false;
+ } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ } else {
+ ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", (int)bufferID);
+ return false;
+ }
+ }
+ }
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::waitForAllEncoderOutputs() {
+ if (mIsCodecInAsyncMode) {
+ while (!hasSeenError() && !mSawEncOutputEOS) {
+ if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
+ }
+ } else {
+ while (!mSawEncOutputEOS) {
+ if (!tryEncoderOutput(kQDeQTimeOutUs)) return false;
+ }
+ }
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::queueEOS() {
+ if (mIsCodecInAsyncMode) {
+ if (!hasSeenError() && !mSawDecInputEOS) {
+ callbackObject element = mAsyncHandleDecoder.getInput();
+ if (element.bufferIndex >= 0) {
+ if (!enqueueDecoderEOS(element.bufferIndex)) return false;
+ }
+ }
+ } else {
+ if (!mSawDecInputEOS) {
+ int bufferIndex = AMediaCodec_dequeueInputBuffer(mDecoder, -1);
+ if (bufferIndex >= 0) {
+ if (!enqueueDecoderEOS(bufferIndex)) return false;
+ } else {
+ ALOGE("unexpected return value from *_dequeueInputBufferBuffer: %d",
+ (int)bufferIndex);
+ return false;
+ }
+ }
+ }
+
+ if (mIsCodecInAsyncMode) {
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!hasSeenError() && !mSawDecOutputEOS) {
+ callbackObject element = mAsyncHandleDecoder.getOutput();
+ if (element.bufferIndex >= 0) {
+ if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) return false;
+ }
+ if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+ if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+ if (!tryEncoderOutput(-1)) return false;
+ }
+ }
+ } else {
+ AMediaCodecBufferInfo outInfo;
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!mSawDecOutputEOS) {
+ if (!mSawDecOutputEOS) {
+ ssize_t oBufferID =
+ AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
+ if (oBufferID >= 0) {
+ if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
+ } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ } else {
+ ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", (int)oBufferID);
+ return false;
+ }
+ }
+ if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+ if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+ if (!tryEncoderOutput(-1)) return false;
+ }
+ }
+ }
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::doWork(int frameLimit) {
+ int frameCnt = 0;
+ if (mIsCodecInAsyncMode) {
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) {
+ callbackObject element = mAsyncHandleDecoder.getWork();
+ if (element.bufferIndex >= 0) {
+ if (element.isInput) {
+ if (!enqueueDecoderInput(element.bufferIndex)) return false;
+ frameCnt++;
+ } else {
+ if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) {
+ return false;
+ }
+ }
+ }
+ // check decoder EOS
+ if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+ // encoder output
+ // TODO: remove fixed constant and change it according to encoder latency
+ if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+ if (!tryEncoderOutput(-1)) return false;
+ }
+ }
+ } else {
+ AMediaCodecBufferInfo outInfo;
+ // output processing after queuing EOS is done in waitForAllOutputs()
+ while (!mSawDecInputEOS && frameCnt < frameLimit) {
+ ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs);
+ if (oBufferID >= 0) {
+ if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false;
+ } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+ } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+ } else {
+ ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", (int)oBufferID);
+ return false;
+ }
+ ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs);
+ if (iBufferId >= 0) {
+ if (!enqueueDecoderInput(iBufferId)) return false;
+ frameCnt++;
+ } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+ } else {
+ ALOGE("unexpected return value from *_dequeueInputBuffer: %d", (int)iBufferId);
+ return false;
+ }
+ if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder);
+ // TODO: remove fixed constant and change it according to encoder latency
+ if (mDecOutputCount - mEncOutputCount > mMaxBFrames) {
+ if (!tryEncoderOutput(-1)) return false;
+ }
+ }
+ }
+ return !hasSeenError();
+}
+
+bool CodecEncoderSurfaceTest::testSimpleEncode(const char* encoder, const char* decoder,
+ const char* srcPath, const char* muxOutPath) {
+ bool isPass = true;
+ if (!setUpExtractor(srcPath)) {
+ ALOGE("setUpExtractor failed");
+ return false;
+ }
+ setUpEncoderFormat();
+ bool muxOutput = true;
+
+ /* TODO(b/149027258) */
+ if (true) mSaveToMem = false;
+ else mSaveToMem = true;
+ auto ref = &mRefBuff;
+ auto test = &mTestBuff;
+ int loopCounter = 0;
+ const bool boolStates[]{true, false};
+ for (bool isAsync : boolStates) {
+ if (!isPass) break;
+ AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
+ mOutputBuff = loopCounter == 0 ? ref : test;
+ mOutputBuff->reset();
+
+ /* TODO(b/147348711) */
+ /* Instead of create and delete codec at every iteration, we would like to create
+ * once and use it for all iterations and delete before exiting */
+ mEncoder = AMediaCodec_createCodecByName(encoder);
+ mDecoder = AMediaCodec_createCodecByName(decoder);
+ if (!mDecoder || !mEncoder) {
+ ALOGE("unable to create media codec by name %s or %s", encoder, decoder);
+ isPass = false;
+ continue;
+ }
+
+ FILE* ofp = nullptr;
+ if (muxOutput && loopCounter == 0) {
+ int muxerFormat = 0;
+ if (!strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP8) ||
+ !strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP9)) {
+ muxerFormat = OUTPUT_FORMAT_WEBM;
+ } else {
+ muxerFormat = OUTPUT_FORMAT_MPEG_4;
+ }
+ ofp = fopen(muxOutPath, "wbe+");
+ if (ofp) {
+ mMuxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)muxerFormat);
+ }
+ }
+ if (!configureCodec(mIsCodecInAsyncMode, mSignalEOSWithLastFrame)) return false;
+ CHECK_STATUS(AMediaCodec_start(mEncoder), "AMediaCodec_start failed");
+ CHECK_STATUS(AMediaCodec_start(mDecoder), "AMediaCodec_start failed");
+ if (!doWork(INT32_MAX)) return false;
+ if (!queueEOS()) return false;
+ if (!waitForAllEncoderOutputs()) return false;
+ if (muxOutput) {
+ if (mMuxer != nullptr) {
+ CHECK_STATUS(AMediaMuxer_stop(mMuxer), "AMediaMuxer_stop failed");
+ mMuxTrackID = -1;
+ CHECK_STATUS(AMediaMuxer_delete(mMuxer), "AMediaMuxer_delete failed");
+ mMuxer = nullptr;
+ }
+ if (ofp) fclose(ofp);
+ }
+ CHECK_STATUS(AMediaCodec_stop(mDecoder), "AMediaCodec_stop failed");
+ CHECK_STATUS(AMediaCodec_stop(mEncoder), "AMediaCodec_stop failed");
+ char log[1000];
+ snprintf(log, sizeof(log), "format: %s \n codec: %s, file: %s, mode: %s:: ",
+ AMediaFormat_toString(mEncFormat), encoder, srcPath, (isAsync ? "async" : "sync"));
+ CHECK_ERR((hasSeenError()), log, "has seen error", isPass);
+ CHECK_ERR((0 == mDecInputCount), log, "no input sent", isPass);
+ CHECK_ERR((0 == mDecOutputCount), log, "no decoder output received", isPass);
+ CHECK_ERR((0 == mEncOutputCount), log, "no encoder output received", isPass);
+ CHECK_ERR((mDecInputCount != mDecOutputCount), log, "decoder input count != output count",
+ isPass);
+ /* TODO(b/153127506)
+ * Currently disabling all encoder output checks. Added checks only for encoder timeStamp
+ * is in increasing order or not.
+ * Once issue is fixed remove increasing timestamp check and enable encoder checks.
+ */
+ /*CHECK_ERR((mEncOutputCount != mDecOutputCount), log,
+ "encoder output count != decoder output count", isPass);
+ CHECK_ERR((loopCounter != 0 && !ref->equals(test)), log, "output is flaky", isPass);
+ CHECK_ERR((loopCounter == 0 && !ref->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)),
+ log, "input pts list and output pts list are not identical", isPass);*/
+ CHECK_ERR(loopCounter == 0 && (!ref->isPtsStrictlyIncreasing(INT32_MIN)), log,
+ "Ref pts is not strictly increasing", isPass);
+ CHECK_ERR(loopCounter != 0 && (!test->isPtsStrictlyIncreasing(INT32_MIN)), log,
+ "Test pts is not strictly increasing", isPass);
+
+ loopCounter++;
+ ANativeWindow_release(mWindow);
+ mWindow = nullptr;
+ CHECK_STATUS(AMediaCodec_delete(mEncoder), "AMediaCodec_delete failed");
+ mEncoder = nullptr;
+ CHECK_STATUS(AMediaCodec_delete(mDecoder), "AMediaCodec_delete failed");
+ mDecoder = nullptr;
+ }
+ return isPass;
+}
+
+static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jDecoder,
+ jstring jMime, jstring jtestFile, jstring jmuxFile,
+ jint jBitrate, jint jFramerate) {
+ const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr);
+ const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
+ const char* cMime = env->GetStringUTFChars(jMime, nullptr);
+ const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
+ const char* cMuxFile = env->GetStringUTFChars(jmuxFile, nullptr);
+ auto codecEncoderSurfaceTest =
+ new CodecEncoderSurfaceTest(cMime, (int)jBitrate, (int)jFramerate);
+ bool isPass =
+ codecEncoderSurfaceTest->testSimpleEncode(cEncoder, cDecoder, cTestFile, cMuxFile);
+ delete codecEncoderSurfaceTest;
+ env->ReleaseStringUTFChars(jEncoder, cEncoder);
+ env->ReleaseStringUTFChars(jDecoder, cDecoder);
+ env->ReleaseStringUTFChars(jMime, cMime);
+ env->ReleaseStringUTFChars(jtestFile, cTestFile);
+ env->ReleaseStringUTFChars(jmuxFile, cMuxFile);
+ return static_cast<jboolean>(isPass);
+}
+
+int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env) {
+ const JNINativeMethod methodTable[] = {
+ {"nativeTestSimpleEncode",
+ "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/"
+ "String;II)Z",
+ (void*)nativeTestSimpleEncode},
+ };
+ jclass c = env->FindClass("android/mediav2/cts/CodecEncoderSurfaceTest");
+ return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
+}
diff --git a/tests/media/jni/NativeCodecEncoderTest.cpp b/tests/media/jni/NativeCodecEncoderTest.cpp
index 626c4307..032bd6c 100644
--- a/tests/media/jni/NativeCodecEncoderTest.cpp
+++ b/tests/media/jni/NativeCodecEncoderTest.cpp
@@ -22,7 +22,7 @@
#include <sys/stat.h>
#include "NativeCodecTestBase.h"
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
class CodecEncoderTest final : CodecTestBase {
private:
@@ -1030,6 +1030,8 @@
extern int registerAndroidMediaV2CtsCodecUnitTest(JNIEnv* env);
extern int registerAndroidMediaV2CtsDecoderTest(JNIEnv* env);
+extern int registerAndroidMediaV2CtsDecoderSurfaceTest(JNIEnv* env);
+extern int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env);
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv* env;
@@ -1037,5 +1039,7 @@
if (registerAndroidMediaV2CtsCodecUnitTest(env) != JNI_OK) return JNI_ERR;
if (registerAndroidMediaV2CtsEncoderTest(env) != JNI_OK) return JNI_ERR;
if (registerAndroidMediaV2CtsDecoderTest(env) != JNI_OK) return JNI_ERR;
+ if (registerAndroidMediaV2CtsDecoderSurfaceTest(env) != JNI_OK) return JNI_ERR;
+ if (registerAndroidMediaV2CtsEncoderSurfaceTest(env) != JNI_OK) return JNI_ERR;
return JNI_VERSION_1_6;
-}
\ No newline at end of file
+}
diff --git a/tests/media/jni/NativeCodecUnitTest.cpp b/tests/media/jni/NativeCodecUnitTest.cpp
index dc06169..3709172 100644
--- a/tests/media/jni/NativeCodecUnitTest.cpp
+++ b/tests/media/jni/NativeCodecUnitTest.cpp
@@ -24,7 +24,7 @@
#include <thread>
#include "NativeCodecTestBase.h"
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
class NativeCodecUnitTest final : CodecTestBase {
private:
diff --git a/tests/media/jni/NativeExtractorTest.cpp b/tests/media/jni/NativeExtractorTest.cpp
index 60df304..596026f 100644
--- a/tests/media/jni/NativeExtractorTest.cpp
+++ b/tests/media/jni/NativeExtractorTest.cpp
@@ -25,6 +25,8 @@
#include <cstdlib>
#include <random>
+#include "NativeMediaCommon.h"
+
static bool isExtractorOKonEOS(AMediaExtractor* extractor) {
return AMediaExtractor_getSampleTrackIndex(extractor) < 0 &&
AMediaExtractor_getSampleSize(extractor) < 0 &&
@@ -51,6 +53,7 @@
bool hasTestMime = AMediaFormat_getString(testFormat, AMEDIAFORMAT_KEY_MIME, &testMime);
if (!hasRefMime || !hasTestMime || strcmp(refMime, testMime) != 0) return false;
+ if (!isCSDIdentical(refFormat, testFormat)) return false;
if (!strncmp(refMime, "audio/", strlen("audio/"))) {
int32_t refSampleRate, testSampleRate, refNumChannels, testNumChannels;
bool hasRefSampleRate =
@@ -150,6 +153,11 @@
areTracksIdentical = false;
break;
}
+ if (memcmp(refBuffer, testBuffer, refSz)) {
+ ALOGD("Mime: %s Mismatch in sample data", refMime);
+ areTracksIdentical = false;
+ break;
+ }
bool haveRefSamples = AMediaExtractor_advance(refExtractor);
bool haveTestSamples = AMediaExtractor_advance(testExtractor);
if (haveRefSamples != haveTestSamples) {
diff --git a/tests/media/jni/NativeMediaCommon.cpp b/tests/media/jni/NativeMediaCommon.cpp
new file mode 100644
index 0000000..0debeac
--- /dev/null
+++ b/tests/media/jni/NativeMediaCommon.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMediaCommon"
+#include <log/log.h>
+
+#include <cstdio>
+#include <cstring>
+#include <utility>
+
+#include "NativeMediaCommon.h"
+/* TODO(b/153592281)
+ * Note: constants used by the native media tests but not available in media ndk api
+ */
+const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
+const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
+const char* AMEDIA_MIMETYPE_VIDEO_AV1 = "video/av01";
+const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
+const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
+const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
+const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
+
+const char* AMEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
+const char* AMEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
+const char* AMEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
+const char* AMEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
+const char* AMEDIA_MIMETYPE_AUDIO_OPUS = "audio/opus";
+
+/* TODO(b/153592281) */
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
+const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
+const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
+
+bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat) {
+ const char* mime;
+ AMediaFormat_getString(refFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+ /* TODO(b/154177490) */
+ if ((strcmp(mime, AMEDIA_MIMETYPE_VIDEO_VP9) == 0) ||
+ (strcmp(mime, AMEDIA_MIMETYPE_VIDEO_AV1) == 0)) {
+ return true;
+ }
+ for (int i = 0;; i++) {
+ std::pair<void*, size_t> refCsd;
+ std::pair<void*, size_t> testCsd;
+ char name[16];
+ snprintf(name, sizeof(name), "csd-%d", i);
+ bool hasRefCSD = AMediaFormat_getBuffer(refFormat, name, &refCsd.first, &refCsd.second);
+ bool hasTestCSD = AMediaFormat_getBuffer(testFormat, name, &testCsd.first, &testCsd.second);
+ if (hasRefCSD != hasTestCSD) {
+ ALOGW("mismatch, ref fmt has CSD %d, test fmt has CSD %d", hasRefCSD, hasTestCSD);
+ return false;
+ }
+ if (hasRefCSD) {
+ if (refCsd.second != testCsd.second) {
+ ALOGW("ref/test %s buffer sizes are not identical %zu/%zu", name, refCsd.second,
+ testCsd.second);
+ return false;
+ }
+ if (memcmp(refCsd.first, testCsd.first, refCsd.second)) {
+ ALOGW("ref/test %s buffers are not identical", name);
+ return false;
+ }
+ } else break;
+ }
+ return true;
+}
\ No newline at end of file
diff --git a/tests/media/jni/NativeMediaConstants.h b/tests/media/jni/NativeMediaCommon.h
similarity index 85%
rename from tests/media/jni/NativeMediaConstants.h
rename to tests/media/jni/NativeMediaCommon.h
index 4b60361..f5f8fb2 100644
--- a/tests/media/jni/NativeMediaConstants.h
+++ b/tests/media/jni/NativeMediaCommon.h
@@ -14,11 +14,14 @@
* limitations under the License.
*/
-#ifndef MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
-#define MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
+#ifndef MEDIACTSNATIVE_NATIVE_MEDIA_COMMON_H
+#define MEDIACTSNATIVE_NATIVE_MEDIA_COMMON_H
+
+#include <NdkMediaFormat.h>
extern const char* AMEDIA_MIMETYPE_VIDEO_VP8;
extern const char* AMEDIA_MIMETYPE_VIDEO_VP9;
+extern const char* AMEDIA_MIMETYPE_VIDEO_AV1;
extern const char* AMEDIA_MIMETYPE_VIDEO_AVC;
extern const char* AMEDIA_MIMETYPE_VIDEO_HEVC;
extern const char* AMEDIA_MIMETYPE_VIDEO_MPEG4;
@@ -45,6 +48,7 @@
// from MediaCodecConstants.h (are these going to be deprecated)
constexpr int COLOR_FormatYUV420SemiPlanar = 21;
constexpr int COLOR_FormatYUV420Flexible = 0x7F420888;
+constexpr int COLOR_FormatSurface = 0x7f000789;
// constants not defined in NDK
extern const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME;
@@ -55,4 +59,7 @@
static const int kBitrateModeConstant = 2;
-#endif // MEDIACTSNATIVE_NATIVE_MEDIA_CONSTANTS_H
+// common utility functions
+bool isCSDIdentical(AMediaFormat* refFormat, AMediaFormat* testFormat);
+
+#endif // MEDIACTSNATIVE_NATIVE_MEDIA_COMMON_H
diff --git a/tests/media/jni/NativeMediaConstants.cpp b/tests/media/jni/NativeMediaConstants.cpp
deleted file mode 100644
index e1ab6da..0000000
--- a/tests/media/jni/NativeMediaConstants.cpp
+++ /dev/null
@@ -1,39 +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.
- */
-
-#include "NativeMediaConstants.h"
-
-/* TODO(b/153592281)
- * Note: constants used by the native media tests but not available in media ndk api
- */
-const char* AMEDIA_MIMETYPE_VIDEO_VP8 = "video/x-vnd.on2.vp8";
-const char* AMEDIA_MIMETYPE_VIDEO_VP9 = "video/x-vnd.on2.vp9";
-const char* AMEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
-const char* AMEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
-const char* AMEDIA_MIMETYPE_VIDEO_MPEG4 = "video/mp4v-es";
-const char* AMEDIA_MIMETYPE_VIDEO_H263 = "video/3gpp";
-
-const char* AMEDIA_MIMETYPE_AUDIO_AMR_NB = "audio/3gpp";
-const char* AMEDIA_MIMETYPE_AUDIO_AMR_WB = "audio/amr-wb";
-const char* AMEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
-const char* AMEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
-const char* AMEDIA_MIMETYPE_AUDIO_OPUS = "audio/opus";
-
-/* TODO(b/153592281) */
-const char* TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME = "request-sync";
-const char* TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE = "video-bitrate";
-const char* TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES = "max-bframes";
-const char* TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE = "bitrate-mode";
diff --git a/tests/media/jni/NativeMuxerTest.cpp b/tests/media/jni/NativeMuxerTest.cpp
index 8d73431..9e759bc 100644
--- a/tests/media/jni/NativeMuxerTest.cpp
+++ b/tests/media/jni/NativeMuxerTest.cpp
@@ -32,7 +32,7 @@
#include <map>
#include <vector>
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
/**
* MuxerNativeTestHelper breaks a media file to elements that a muxer can use to rebuild its clone.
@@ -277,8 +277,9 @@
const char* thatMime = nullptr;
AMediaFormat_getString(thatFormat, AMEDIAFORMAT_KEY_MIME, &thatMime);
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;
+ int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
for (int k = 0; k < mBufferInfo[i].size(); k++) {
AMediaCodecBufferInfo* thisInfo = mBufferInfo[i][k];
AMediaCodecBufferInfo* thatInfo = that->mBufferInfo[j][k];
@@ -287,17 +288,21 @@
}
if (thisInfo->size != thatInfo->size) {
sizeDiff++;
+ } else if (memcmp(mBuffer + thisInfo->offset,
+ that->mBuffer + thatInfo->offset, thisInfo->size)) {
+ buffDiff++;
}
if (abs(thisInfo->presentationTimeUs - thatInfo->presentationTimeUs) >
STTS_TOLERANCE) {
tsDiff++;
}
}
- if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0)
+ if (flagsDiff == 0 && sizeDiff == 0 && tsDiff == 0 && buffDiff == 0)
break;
else {
- ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff %d",
- thisMime, (int)mBufferInfo[i].size(), flagsDiff, sizeDiff, tsDiff);
+ ALOGV("For mime %s, Total Samples %d, flagsDiff %d, sizeDiff %d, tsDiff "
+ "%d, buffDiff %d", thisMime, (int)mBufferInfo[i].size(), flagsDiff,
+ sizeDiff, tsDiff, buffDiff);
}
}
}
diff --git a/tests/media/jni/NativeMuxerUnitTest.cpp b/tests/media/jni/NativeMuxerUnitTest.cpp
index a08c921..78af59d 100644
--- a/tests/media/jni/NativeMuxerUnitTest.cpp
+++ b/tests/media/jni/NativeMuxerUnitTest.cpp
@@ -32,7 +32,7 @@
#include <map>
#include <vector>
-#include "NativeMediaConstants.h"
+#include "NativeMediaCommon.h"
static media_status_t insertPerFrameSubtitles(AMediaMuxer* muxer, long pts, size_t trackID) {
const char* greeting = "hello world";
diff --git a/tests/media/res/layout/media_decoder_surface_layout.xml b/tests/media/res/layout/media_decoder_surface_layout.xml
new file mode 100644
index 0000000..bff09bb
--- /dev/null
+++ b/tests/media/res/layout/media_decoder_surface_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+ <SurfaceView android:id="@+id/surface"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ </SurfaceView>
+</LinearLayout>
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
new file mode 100644
index 0000000..5d1bc41
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java
@@ -0,0 +1,582 @@
+/*
+ * 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.MediaCodec;
+import android.media.MediaExtractor;
+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;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+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;
+import java.util.HashSet;
+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 {
+ 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;
+ mReconfigFile = reconfigFile;
+ mCsdBuffers = new ArrayList<>();
+ mAsyncHandle = new CodecAsyncHandler();
+ mIsAudio = mMime.startsWith("audio/");
+ }
+
+ private void setScreenParams(int width, int height, boolean noStretch) {
+ ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
+ final DisplayMetrics dm = mActivityRule.getActivity().getResources().getDisplayMetrics();
+ if (noStretch && width <= dm.widthPixels && height <= dm.heightPixels) {
+ lp.width = width;
+ lp.height = height;
+ } else {
+ int a = dm.widthPixels * height / width;
+ if (a <= dm.heightPixels) {
+ lp.width = dm.widthPixels;
+ lp.height = a;
+ } else {
+ lp.width = dm.heightPixels * width / height;
+ lp.height = dm.heightPixels;
+ }
+ }
+ assertTrue(lp.width <= dm.widthPixels);
+ assertTrue(lp.height <= dm.heightPixels);
+ 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;
+ }
+ 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, mSurface != null);
+ }
+
+ private void decodeAndSavePts(String file, String decoder, long pts, int mode, int frameLimit)
+ throws IOException, InterruptedException {
+ 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();
+ }
+
+ @Rule
+ public ActivityTestRule<CodecTestActivity> mActivityRule =
+ new ActivityTestRule<>(CodecTestActivity.class);
+
+ public void setUpSurface() {
+ CodecTestActivity activity = mActivityRule.getActivity();
+ mSurfaceView = activity.findViewById(R.id.surface);
+ mSurface = mSurfaceView.getHolder().getSurface();
+ }
+
+ public void tearDownSurface() {
+ if (mSurface != null) {
+ mSurface.release();
+ mSurface = null;
+ }
+ }
+
+ @Parameterized.Parameters(name = "{index}({0})")
+ public static Collection<Object[]> input() {
+ Set<String> list = new HashSet<>();
+ if (isHandheld() || isTv() || isAutomotive()) {
+ // sec 2.2.2, 2.3.2, 2.5.2
+ list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+ list.add(MediaFormat.MIMETYPE_VIDEO_MPEG4);
+ list.add(MediaFormat.MIMETYPE_VIDEO_H263);
+ list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+ list.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+ }
+ if (isHandheld()) {
+ // sec 2.2.2
+ list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ }
+ if (isTv()) {
+ // sec 2.3.2
+ list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ list.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
+ }
+ ArrayList<String> cddRequiredMimeList = new ArrayList<>(list);
+ final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+ {MediaFormat.MIMETYPE_VIDEO_MPEG2, "bbb_340x280_768kbps_30fps_mpeg2.mp4",
+ "bbb_520x390_1mbps_30fps_mpeg2.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_340x280_768kbps_30fps_avc.mp4",
+ "bbb_520x390_1mbps_30fps_avc.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_360x640_768kbps_30fps_avc.mp4",
+ "bbb_520x390_1mbps_30fps_avc.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_160x1024_1500kbps_30fps_avc.mp4",
+ "bbb_520x390_1mbps_30fps_avc.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x120_1500kbps_30fps_avc.mp4",
+ "bbb_340x280_768kbps_30fps_avc.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4",
+ "bbb_340x280_768kbps_30fps_hevc.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4",
+ "bbb_176x144_192kbps_15fps_mpeg4.mp4"},
+ {MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp",
+ "bbb_176x144_192kbps_10fps_h263.3gp"},
+ {MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_340x280_768kbps_30fps_vp8.webm",
+ "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_AV1, "bbb_340x280_768kbps_30fps_av1.mp4",
+ "bbb_520x390_1mbps_30fps_av1.mp4"},
+ });
+ return prepareParamList(cddRequiredMimeList, exhaustiveArgsList, false);
+ }
+
+ /**
+ * Tests decoder for codec is in sync and async mode with surface.
+ * In these scenarios, Timestamp and it's ordering is verified.
+ */
+ @LargeTest
+ @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testSimpleDecodeToSurface() throws IOException, InterruptedException {
+ ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+ if (listOfDecoders.isEmpty()) {
+ fail("no suitable codecs found for mime: " + mMime);
+ }
+ boolean[] boolStates = {true, false};
+ OutputManager ref;
+ OutputManager test = new OutputManager();
+ final long pts = 0;
+ final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+ for (String decoder : listOfDecoders) {
+ decodeAndSavePts(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
+ ref = mOutputBuff;
+ assertTrue("input pts list and output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ MediaFormat format = setUpSource(mTestFile);
+ mCodec = MediaCodec.createByCodecName(decoder);
+ setUpSurface();
+ setScreenParams(getWidth(format), getHeight(format), true);
+ for (boolean isAsync : boolStates) {
+ String log = String.format("codec: %s, file: %s, mode: %s:: ", decoder, mTestFile,
+ (isAsync ? "async" : "sync"));
+ mOutputBuff = test;
+ mOutputBuff.reset();
+ mExtractor.seekTo(pts, mode);
+ configureCodec(format, isAsync, true, false);
+ mCodec.start();
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllOutputs();
+ /* TODO(b/147348711) */
+ if (false) mCodec.stop();
+ else mCodec.reset();
+ 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();
+ mExtractor.release();
+ mSurface = null;
+ }
+ tearDownSurface();
+ }
+
+ /**
+ * Tests flush when codec is in sync and async mode with surface. In these scenarios,
+ * Timestamp and the ordering is verified.
+ */
+ @Ignore("TODO(b/147576107)")
+ @LargeTest
+ @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testFlush() throws IOException, InterruptedException {
+ MediaFormat format = setUpSource(mTestFile);
+ mExtractor.release();
+ ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+ if (listOfDecoders.isEmpty()) {
+ fail("no suitable codecs found for mime: " + mMime);
+ }
+ mCsdBuffers.clear();
+ for (int i = 0; ; i++) {
+ String csdKey = "csd-" + i;
+ if (format.containsKey(csdKey)) {
+ mCsdBuffers.add(format.getByteBuffer(csdKey));
+ } else break;
+ }
+ final long pts = 500000;
+ final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+ boolean[] boolStates = {true, false};
+ OutputManager test = new OutputManager();
+ for (String decoder : listOfDecoders) {
+ decodeAndSavePts(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
+ OutputManager ref = mOutputBuff;
+ assertTrue("input pts list and output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ mOutputBuff = test;
+ setUpSource(mTestFile);
+ mCodec = MediaCodec.createByCodecName(decoder);
+ setUpSurface();
+ setScreenParams(getWidth(format), getHeight(format), false);
+ for (boolean isAsync : boolStates) {
+ String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
+ mTestFile, (isAsync ? "async" : "sync"));
+ mExtractor.seekTo(0, mode);
+ configureCodec(format, isAsync, true, false);
+ mCodec.start();
+
+ /* test flush in running state before queuing input */
+ flushCodec();
+ if (mIsCodecInAsyncMode) mCodec.start();
+ queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
+
+ doWork(1);
+ flushCodec();
+ if (mIsCodecInAsyncMode) mCodec.start();
+ queueCodecConfig(); /* flushed codec too soon after start, resubmit csd */
+
+ mExtractor.seekTo(0, mode);
+ test.reset();
+ doWork(23);
+ assertTrue(log + " pts is not strictly increasing",
+ test.isPtsStrictlyIncreasing(mPrevOutputPts));
+
+ /* test flush in running state */
+ flushCodec();
+ if (mIsCodecInAsyncMode) mCodec.start();
+ test.reset();
+ mExtractor.seekTo(pts, mode);
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllOutputs();
+ 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 */
+ flushCodec();
+ if (mIsCodecInAsyncMode) mCodec.start();
+ test.reset();
+ mExtractor.seekTo(pts, mode);
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllOutputs();
+ /* TODO(b/147348711) */
+ if (false) mCodec.stop();
+ else mCodec.reset();
+ 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();
+ mExtractor.release();
+ mSurface = null;
+ }
+ tearDownSurface();
+ }
+
+ /**
+ * Tests reconfigure when codec is in sync and async mode with surface. In these scenarios,
+ * Timestamp and the ordering is verified.
+ */
+ @Ignore("TODO(b/148523403)")
+ @LargeTest
+ @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testReconfigure() throws IOException, InterruptedException {
+ MediaFormat format = setUpSource(mTestFile);
+ mExtractor.release();
+ MediaFormat newFormat = setUpSource(mReconfigFile);
+ mExtractor.release();
+ ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+ if (listOfDecoders.isEmpty()) {
+ fail("no suitable codecs found for mime: " + mMime);
+ }
+ final long pts = 500000;
+ final int mode = MediaExtractor.SEEK_TO_CLOSEST_SYNC;
+ boolean[] boolStates = {true, false};
+ OutputManager test = new OutputManager();
+ for (String decoder : listOfDecoders) {
+ decodeAndSavePts(mTestFile, decoder, pts, mode, Integer.MAX_VALUE);
+ OutputManager ref = mOutputBuff;
+ decodeAndSavePts(mReconfigFile, decoder, pts, mode, Integer.MAX_VALUE);
+ OutputManager configRef = mOutputBuff;
+ assertTrue("input pts list and reference pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ assertTrue("input pts list and reconfig ref output pts list are not identical",
+ configRef.isOutPtsListIdenticalToInpPtsList(false));
+ mOutputBuff = test;
+ mCodec = MediaCodec.createByCodecName(decoder);
+ setUpSurface();
+ setScreenParams(getWidth(format), getHeight(format), false);
+ for (boolean isAsync : boolStates) {
+ setUpSource(mTestFile);
+ String log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
+ mTestFile, (isAsync ? "async" : "sync"));
+ mExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
+ configureCodec(format, isAsync, true, false);
+
+ /* test reconfigure in stopped state */
+ reConfigureCodec(format, !isAsync, false, false);
+ mCodec.start();
+
+ /* test reconfigure in running state before queuing input */
+ reConfigureCodec(format, !isAsync, false, false);
+ mCodec.start();
+ doWork(23);
+
+ /* test reconfigure codec in running state */
+ reConfigureCodec(format, isAsync, true, false);
+ mCodec.start();
+ test.reset();
+ mExtractor.seekTo(pts, mode);
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllOutputs();
+ /* TODO(b/147348711) */
+ if (false) mCodec.stop();
+ else mCodec.reset();
+ 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 */
+ reConfigureCodec(format, !isAsync, false, false);
+ mCodec.start();
+ test.reset();
+ mExtractor.seekTo(pts, mode);
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllOutputs();
+ /* TODO(b/147348711) */
+ if (false) mCodec.stop();
+ else mCodec.reset();
+ 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();
+
+ /* test reconfigure codec for new file */
+ setUpSource(mReconfigFile);
+ log = String.format("decoder: %s, input file: %s, mode: %s:: ", decoder,
+ mReconfigFile, (isAsync ? "async" : "sync"));
+ setScreenParams(getWidth(newFormat), getHeight(newFormat), true);
+ reConfigureCodec(newFormat, isAsync, false, false);
+ mCodec.start();
+ test.reset();
+ mExtractor.seekTo(pts, mode);
+ doWork(Integer.MAX_VALUE);
+ queueEOS();
+ waitForAllOutputs();
+ /* TODO(b/147348711) */
+ if (false) mCodec.stop();
+ else mCodec.reset();
+ 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();
+ }
+ mCodec.release();
+ mSurface = null;
+ }
+ tearDownSurface();
+ }
+
+ private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mime,
+ String testFile, String refFile, float rmsError);
+
+ @LargeTest
+ @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testSimpleDecodeToSurfaceNative() throws IOException {
+ ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+ if (listOfDecoders.isEmpty()) {
+ fail("no suitable codecs found for mime: " + mMime);
+ }
+ MediaFormat format = setUpSource(mTestFile);
+ mExtractor.release();
+ setUpSurface();
+ setScreenParams(getWidth(format), getHeight(format), false);
+ for (String decoder : listOfDecoders) {
+ assertTrue(nativeTestSimpleDecode(decoder, mSurface, mMime, mInpPrefix + mTestFile,
+ mInpPrefix + mReconfigFile, -1.0f));
+ }
+ tearDownSurface();
+ }
+
+ private native boolean nativeTestFlush(String decoder, Surface surface, String mime,
+ String testFile);
+
+ @LargeTest
+ @Test(timeout = PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testFlushNative() throws IOException {
+ ArrayList<String> listOfDecoders = selectCodecs(mMime, null, null, false);
+ if (listOfDecoders.isEmpty()) {
+ fail("no suitable codecs found for mime: " + mMime);
+ }
+ MediaFormat format = setUpSource(mTestFile);
+ mExtractor.release();
+ setUpSurface();
+ setScreenParams(getWidth(format), getHeight(format), true);
+ for (String decoder : listOfDecoders) {
+ assertTrue(nativeTestFlush(decoder, mSurface, mMime, mInpPrefix + mTestFile));
+ }
+ tearDownSurface();
+ }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index b4e3b2d..ca05d0e 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -24,6 +24,7 @@
import android.os.PersistableBundle;
import android.util.Log;
import android.util.Pair;
+import android.view.Surface;
import androidx.test.filters.LargeTest;
import androidx.test.filters.SmallTest;
@@ -43,7 +44,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
import static org.junit.Assert.assertTrue;
@@ -331,23 +334,37 @@
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
- final ArrayList<String> cddRequiredMimeList =
- new ArrayList<>(Arrays.asList(
- MediaFormat.MIMETYPE_AUDIO_MPEG,
- MediaFormat.MIMETYPE_AUDIO_AAC,
- MediaFormat.MIMETYPE_AUDIO_FLAC,
- MediaFormat.MIMETYPE_AUDIO_VORBIS,
- MediaFormat.MIMETYPE_AUDIO_OPUS,
- MediaFormat.MIMETYPE_AUDIO_RAW,
- MediaFormat.MIMETYPE_AUDIO_AMR_NB,
- MediaFormat.MIMETYPE_AUDIO_AMR_WB,
- MediaFormat.MIMETYPE_VIDEO_MPEG4,
- MediaFormat.MIMETYPE_VIDEO_H263,
- MediaFormat.MIMETYPE_VIDEO_AVC,
- MediaFormat.MIMETYPE_VIDEO_HEVC,
- MediaFormat.MIMETYPE_VIDEO_VP8,
- MediaFormat.MIMETYPE_VIDEO_VP9));
- if (isTv()) cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
+ Set<String> list = new HashSet<>();
+ if (hasAudioOutput()) {
+ // sec 5.1.2
+ list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+ list.add(MediaFormat.MIMETYPE_AUDIO_FLAC);
+ list.add(MediaFormat.MIMETYPE_AUDIO_MPEG);
+ list.add(MediaFormat.MIMETYPE_AUDIO_VORBIS);
+ list.add(MediaFormat.MIMETYPE_AUDIO_RAW);
+ list.add(MediaFormat.MIMETYPE_AUDIO_OPUS);
+ }
+ if (isHandheld() || isTv() || isAutomotive()) {
+ // sec 2.2.2, 2.3.2, 2.5.2
+ list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+ list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+ list.add(MediaFormat.MIMETYPE_VIDEO_MPEG4);
+ list.add(MediaFormat.MIMETYPE_VIDEO_H263);
+ list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+ list.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+ }
+ if (isHandheld()) {
+ // sec 2.2.2
+ list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
+ list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
+ list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ }
+ if (isTv()) {
+ // sec 2.3.2
+ list.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ list.add(MediaFormat.MIMETYPE_VIDEO_MPEG2);
+ }
+ ArrayList<String> cddRequiredMimeList = new ArrayList<>(list);
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
{MediaFormat.MIMETYPE_AUDIO_MPEG, "bbb_1ch_8kHz_lame_cbr.mp3",
"bbb_1ch_8kHz_s16le.raw", "bbb_2ch_44kHz_lame_vbr.mp3",
@@ -502,8 +519,8 @@
mExtractor.release();
}
- private native boolean nativeTestSimpleDecode(String decoder, String mime, String testFile,
- String refFile, float rmsError);
+ private native boolean nativeTestSimpleDecode(String decoder, Surface surface, String mime,
+ String testFile, String refFile, float rmsError);
@LargeTest
@@ -514,7 +531,7 @@
fail("no suitable codecs found for mime: " + mMime);
}
for (String decoder : listOfDecoders) {
- assertTrue(nativeTestSimpleDecode(decoder, mMime, mInpPrefix + mTestFile,
+ assertTrue(nativeTestSimpleDecode(decoder, null, mMime, mInpPrefix + mTestFile,
mInpPrefix + mRefFile, mRmsError));
}
}
@@ -644,7 +661,8 @@
}
}
- private native boolean nativeTestFlush(String decoder, String mime, String testFile);
+ private native boolean nativeTestFlush(String decoder, Surface surface, String mime,
+ String testFile);
@Ignore("TODO(b/147576107)")
@LargeTest
@@ -655,7 +673,7 @@
fail("no suitable codecs found for mime: " + mMime);
}
for (String decoder : listOfDecoders) {
- assertTrue(nativeTestFlush(decoder, mMime, mInpPrefix + mTestFile));
+ assertTrue(nativeTestFlush(decoder, null, mMime, mInpPrefix + mTestFile));
}
}
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
index f528065..d6caa62 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderSurfaceTest.java
@@ -38,12 +38,10 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -101,14 +99,12 @@
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
- final ArrayList<String> cddRequiredMimeList =
- new ArrayList<>(Arrays.asList(
- MediaFormat.MIMETYPE_VIDEO_MPEG4,
- MediaFormat.MIMETYPE_VIDEO_H263,
- MediaFormat.MIMETYPE_VIDEO_AVC,
- MediaFormat.MIMETYPE_VIDEO_HEVC,
- MediaFormat.MIMETYPE_VIDEO_VP8,
- MediaFormat.MIMETYPE_VIDEO_VP9));
+ ArrayList<String> cddRequiredMimeList = new ArrayList<>();
+ if (CodecTestBase.isHandheld() || CodecTestBase.isTv() || CodecTestBase.isAutomotive()) {
+ // sec 2.2.2, 2.3.2, 2.5.2
+ cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+ cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+ }
final Object[][] exhaustiveArgsList = new Object[][]{
// Video - CodecMime, test file, bit rate, frame rate
{MediaFormat.MIMETYPE_VIDEO_H263, "bbb_176x144_128kbps_15fps_h263.3gp", 128000, 15},
@@ -133,6 +129,14 @@
}
}
}
+ // TODO(b/154423708): add checks for video o/p port and display length >= 2.5"
+ /* sec 5.2: device implementations include an embedded screen display with the diagonal
+ length of at least 2.5inches or include a video output port or declare the support of a
+ camera */
+ if (CodecTestBase.hasCamera() && !mimes.contains(MediaFormat.MIMETYPE_VIDEO_AVC) &&
+ !mimes.contains(MediaFormat.MIMETYPE_VIDEO_VP8)) {
+ fail("device must support at least one of VP8 or AVC video encoders");
+ }
for (String mime : cddRequiredMimeList) {
if (!mimes.contains(mime)) {
fail("no codec found for mime " + mime + " as required by cdd");
@@ -546,5 +550,34 @@
mDecoder.release();
mExtractor.release();
}
+
+ private native boolean nativeTestSimpleEncode(String encoder, String decoder, String mime,
+ String testFile, String muxFile, int bitrate, int framerate);
+
+ @LargeTest
+ @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS)
+ public void testSimpleEncodeFromSurfaceNative() throws IOException {
+ MediaFormat decoderFormat = setUpSource(mTestFile);
+ MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ String decoder = codecList.findDecoderForFormat(decoderFormat);
+ if (decoder == null) {
+ mExtractor.release();
+ fail("no suitable decoder found for format: " + decoderFormat.toString());
+ }
+ ArrayList<String> listOfEncoders = CodecTestBase.selectCodecs(mMime, null, null, true);
+ assertFalse("no suitable codecs found for mime: " + mMime, listOfEncoders.isEmpty());
+ for (String encoder : listOfEncoders) {
+ String tmpPath = null;
+ if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8) ||
+ mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) {
+ tmpPath = File.createTempFile("tmp", ".webm").getAbsolutePath();
+ } else {
+ tmpPath = File.createTempFile("tmp", ".mp4").getAbsolutePath();
+ }
+ assertTrue(
+ nativeTestSimpleEncode(encoder, decoder, mMime, mInpPrefix + mTestFile, tmpPath,
+ mBitrate, mFrameRate));
+ }
+ }
}
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
index 48470b8..6a4c7d9 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderTest.java
@@ -42,7 +42,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -366,19 +368,26 @@
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
- final ArrayList<String> cddRequiredMimeList =
- new ArrayList<>(Arrays.asList(
- MediaFormat.MIMETYPE_AUDIO_FLAC,
- MediaFormat.MIMETYPE_AUDIO_OPUS,
- MediaFormat.MIMETYPE_AUDIO_AAC,
- MediaFormat.MIMETYPE_AUDIO_AMR_NB,
- MediaFormat.MIMETYPE_AUDIO_AMR_WB,
- MediaFormat.MIMETYPE_VIDEO_MPEG4,
- MediaFormat.MIMETYPE_VIDEO_H263,
- MediaFormat.MIMETYPE_VIDEO_AVC,
- MediaFormat.MIMETYPE_VIDEO_HEVC,
- MediaFormat.MIMETYPE_VIDEO_VP8,
- MediaFormat.MIMETYPE_VIDEO_VP9));
+ Set<String> list = new HashSet<>();
+ if (hasMicrophone()) {
+ // sec 5.1.1
+ // TODO(b/154423550)
+ // list.add(MediaFormat.MIMETYPE_AUDIO_RAW);
+ list.add(MediaFormat.MIMETYPE_AUDIO_FLAC);
+ list.add(MediaFormat.MIMETYPE_AUDIO_OPUS);
+ }
+ if (isHandheld() || isTv() || isAutomotive()) {
+ // sec 2.2.2, 2.3.2, 2.5.2
+ list.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+ list.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+ list.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+ }
+ if (isHandheld()) {
+ // sec 2.2.2
+ list.add(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
+ list.add(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
+ }
+ ArrayList<String> cddRequiredMimeList = new ArrayList<>(list);
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
// Audio - CodecMime, arrays of bit-rates, sample rates, channel counts
{MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 11025,
diff --git a/tests/media/src/android/mediav2/cts/CodecTestActivity.java b/tests/media/src/android/mediav2/cts/CodecTestActivity.java
new file mode 100644
index 0000000..18099dd
--- /dev/null
+++ b/tests/media/src/android/mediav2/cts/CodecTestActivity.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.mediav2.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+public class CodecTestActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.media_decoder_surface_layout);
+ }
+
+ @Override
+ protected void onResume() {
+ setTurnScreenOn(true);
+ setShowWhenLocked(true);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+ super.onResume();
+ }
+}
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index b9db836..3e09a8d 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -437,6 +437,8 @@
static final int PER_TEST_TIMEOUT_SMALL_TEST_MS = 60000;
static final long Q_DEQ_TIMEOUT_US = 5000;
static final String mInpPrefix = WorkDir.getMediaDirString();
+ static final PackageManager pm =
+ InstrumentationRegistry.getInstrumentation().getContext().getPackageManager();
static String codecSelKeys;
CodecAsyncHandler mAsyncHandle;
@@ -487,8 +489,34 @@
}
static boolean isTv() {
- return InstrumentationRegistry.getInstrumentation().getContext().getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
+ static boolean hasMicrophone() {
+ return pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE);
+ }
+
+ static boolean hasCamera() {
+ return pm.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY);
+ }
+
+ static boolean isWatch() {
+ return pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
+ static boolean isAutomotive() {
+ return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
+ static boolean hasAudioOutput() {
+ return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+ }
+
+ static boolean isHandheld() {
+ // handheld nature is not exposed to package manager, for now
+ // we check for touchscreen and NOT watch and NOT tv
+ return pm.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) && !isWatch() && !isTv() &&
+ !isAutomotive();
}
static List<Object[]> prepareParamList(ArrayList<String> cddRequiredMimeList,
@@ -507,6 +535,14 @@
}
}
}
+ // TODO(b/154423708): add checks for video o/p port and display length >= 2.5"
+ /* sec 5.2: device implementations include an embedded screen display with the
+ diagonal length of at least 2.5inches or include a video output port or declare the
+ support of a camera */
+ if (isEncoder && hasCamera() && !mimes.contains(MediaFormat.MIMETYPE_VIDEO_AVC) &&
+ !mimes.contains(MediaFormat.MIMETYPE_VIDEO_VP8)) {
+ fail("device must support at least one of VP8 or AVC video encoders");
+ }
for (String mime : cddRequiredMimeList) {
if (!mimes.contains(mime)) {
fail("no codec found for mime " + mime + " as required by cdd");
diff --git a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
index e99ac4a..9438dcb 100644
--- a/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
+++ b/tests/media/src/android/mediav2/cts/EncoderProfileLevelTest.java
@@ -87,10 +87,13 @@
@Parameterized.Parameters(name = "{index}({0})")
public static Collection<Object[]> input() {
- final List<String> cddEncodersList =
- Arrays.asList(MediaFormat.MIMETYPE_VIDEO_H263, MediaFormat.MIMETYPE_VIDEO_AVC,
- MediaFormat.MIMETYPE_VIDEO_HEVC, MediaFormat.MIMETYPE_VIDEO_VP8,
- MediaFormat.MIMETYPE_VIDEO_VP9);
+ ArrayList<String> cddRequiredMimeList = new ArrayList<>();
+ if (isHandheld() || isTv() || isAutomotive()) {
+ // sec 2.2.2, 2.3.2, 2.5.2
+ cddRequiredMimeList.add(MediaFormat.MIMETYPE_AUDIO_AAC);
+ cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+ cddRequiredMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+ }
final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
// Audio - CodecMime, bit-rate, sample rate, channel count
{MediaFormat.MIMETYPE_AUDIO_AAC, 64000, 8000, 1, -1},
@@ -213,7 +216,7 @@
if (!mimes.contains(type)) mimes.add(type);
}
}
- for (String mime : cddEncodersList) {
+ for (String mime : cddRequiredMimeList) {
if (!mimes.contains(mime)) {
fail("media codec encoder list doesn't contain " + mime +
" as required by cdd");
@@ -229,7 +232,7 @@
}
}
if (miss) {
- if (cddEncodersList.contains(mime)) {
+ if (cddRequiredMimeList.contains(mime)) {
fail("no test vectors available for " + mime);
}
Log.w(LOG_TAG, "no test vectors available for " + mime);
diff --git a/tests/media/src/android/mediav2/cts/ExtractorTest.java b/tests/media/src/android/mediav2/cts/ExtractorTest.java
index 31bba13..a5c0eb9 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorTest.java
@@ -195,11 +195,44 @@
refSample.flags >= 0 && refSample.size >= 0 && refSample.presentationTimeUs >= 0;
}
+ static boolean isCSDIdentical(MediaFormat refFormat, MediaFormat testFormat) {
+ String mime = refFormat.getString(MediaFormat.KEY_MIME);
+ /* TODO(b/154177490) */
+ if (mime.equals(MediaFormat.MIMETYPE_VIDEO_VP9) ||
+ mime.equals(MediaFormat.MIMETYPE_VIDEO_AV1)) {
+ return true;
+ }
+ for (int i = 0; ; i++) {
+ String csdKey = "csd-" + i;
+ boolean refHasCSD = refFormat.containsKey(csdKey);
+ boolean testHasCSD = testFormat.containsKey(csdKey);
+ if (refHasCSD != testHasCSD) {
+ if (ENABLE_LOGS) {
+ Log.w(LOG_TAG, "error, ref fmt has CSD: " + refHasCSD + "test fmt has CSD: " +
+ testHasCSD);
+ }
+ return false;
+ }
+ if (refHasCSD) {
+ ByteBuffer r = refFormat.getByteBuffer(csdKey);
+ ByteBuffer t = testFormat.getByteBuffer(csdKey);
+ if (!r.equals(t)) {
+ if (ENABLE_LOGS) {
+ Log.w(LOG_TAG, "ref CSD and test CSD buffers are not identical");
+ }
+ return false;
+ }
+ } else break;
+ }
+ return true;
+ }
+
private static boolean isFormatSimilar(MediaFormat refFormat, MediaFormat testFormat) {
String refMime = refFormat.getString(MediaFormat.KEY_MIME);
String testMime = testFormat.getString(MediaFormat.KEY_MIME);
if (!refMime.equals(testMime)) return false;
+ if (!isCSDIdentical(refFormat, testFormat)) return false;
if (refMime.startsWith("audio/")) {
return refFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) ==
testFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) &&
@@ -292,6 +325,13 @@
areTracksIdentical = false;
break;
}
+ if (!testBuffer.equals(refBuffer)) {
+ if (ENABLE_LOGS) {
+ Log.d(LOG_TAG, "Mime: " + refMime + "sample data is not identical");
+ }
+ areTracksIdentical = false;
+ break;
+ }
boolean haveRefSamples = refExtractor.advance();
boolean haveTestSamples = testExtractor.advance();
if (haveRefSamples != haveTestSamples) {
diff --git a/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java b/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java
index a28f134..cee70e0 100644
--- a/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java
+++ b/tests/media/src/android/mediav2/cts/ExtractorUnitTest.java
@@ -23,7 +23,6 @@
import androidx.test.filters.SmallTest;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
diff --git a/tests/media/src/android/mediav2/cts/MuxerTest.java b/tests/media/src/android/mediav2/cts/MuxerTest.java
index 7a5c046..d954f01 100644
--- a/tests/media/src/android/mediav2/cts/MuxerTest.java
+++ b/tests/media/src/android/mediav2/cts/MuxerTest.java
@@ -260,7 +260,9 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MuxerTestHelper that = (MuxerTestHelper) o;
-
+ int MAX_SAMPLE_SIZE = 4 * 1024 * 1024;
+ byte[] refBuffer = new byte[MAX_SAMPLE_SIZE];
+ byte[] testBuffer = new byte[MAX_SAMPLE_SIZE];
for (int i = 0; i < mTrackCount; i++) {
MediaFormat thisFormat = mFormat.get(i);
String thisMime = thisFormat.getString(MediaFormat.KEY_MIME);
@@ -269,8 +271,9 @@
MediaFormat thatFormat = that.mFormat.get(j);
String thatMime = thatFormat.getString(MediaFormat.KEY_MIME);
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;
+ int flagsDiff = 0, sizeDiff = 0, tsDiff = 0, buffDiff = 0;
for (int k = 0; k < mBufferInfo.get(i).size(); k++) {
MediaCodec.BufferInfo thisInfo = mBufferInfo.get(i).get(k);
MediaCodec.BufferInfo thatInfo = that.mBufferInfo.get(j).get(k);
@@ -279,6 +282,17 @@
}
if (thisInfo.size != thatInfo.size) {
sizeDiff++;
+ } 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++) {
+ if (refBuffer[count] != testBuffer[count]) {
+ buffDiff++;
+ break;
+ }
+ }
}
if (Math.abs(
thisInfo.presentationTimeUs - thatInfo.presentationTimeUs) >
@@ -286,13 +300,14 @@
tsDiff++;
}
}
- if (flagsDiff != 0 || sizeDiff != 0 || tsDiff != 0) {
+ 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);
+ " tsDiff: " + tsDiff +
+ " buffDiff: " + buffDiff);
}
} else break;
} else {
@@ -304,6 +319,8 @@
}
}
}
+ mBuff.position(0);
+ that.mBuff.position(0);
if (j == that.mTrackCount) {
if (ENABLE_LOGS) {
Log.d(LOG_TAG, "For track: " + thisMime + " Couldn't find a match ");
diff --git a/tests/signature/api-check/shared-libs-api/AndroidManifest.xml b/tests/signature/api-check/shared-libs-api/AndroidManifest.xml
index 00ddd9a..663ef6b 100644
--- a/tests/signature/api-check/shared-libs-api/AndroidManifest.xml
+++ b/tests/signature/api-check/shared-libs-api/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-library android:name="android.test.base" android:required="false"/>
<uses-library android:name="android.test.mock" android:required="false"/>
<uses-library android:name="android.test.runner" android:required="false"/>
+ <uses-library android:name="android.net.ipsec.ike" android:required="false"/>
<uses-library android:name="com.android.future.usb.accessory" android:required="false"/>
<uses-library android:name="com.android.location.provider" android:required="false"/>
<uses-library android:name="com.android.mediadrm.signer" android:required="false"/>
diff --git a/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java b/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java
index 7a9d28f..cbb485d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java
+++ b/tests/tests/graphics/src/android/graphics/cts/EGL15Test.java
@@ -322,6 +322,37 @@
+ " why did call not report that!");
}
}
+
+ @Test
+ public void testEGL15CreateDebugContext() {
+ int error;
+
+ if (mEglVersion < 15) {
+ return;
+ }
+
+ mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
+ new int[] { EGL15.EGL_CONTEXT_OPENGL_DEBUG, EGL14.EGL_FALSE, EGL14.EGL_NONE }, 0);
+ if (mEglContext == EGL15.EGL_NO_CONTEXT) {
+ throw new RuntimeException("eglCreateContext failed");
+ }
+ error = EGL14.eglGetError();
+ if (error != EGL14.EGL_SUCCESS) {
+ throw new RuntimeException("eglCreateContext failed");
+ }
+ EGL14.eglDestroyContext(mEglDisplay, mEglContext);
+
+ mEglContext = EGL14.eglCreateContext(mEglDisplay, mEglConfig, EGL14.EGL_NO_CONTEXT,
+ new int[] { EGL15.EGL_CONTEXT_OPENGL_DEBUG, EGL14.EGL_TRUE, EGL14.EGL_NONE }, 0);
+ if (mEglContext == EGL15.EGL_NO_CONTEXT) {
+ throw new RuntimeException("eglCreateContext failed");
+ }
+ error = EGL14.eglGetError();
+ if (error != EGL14.EGL_SUCCESS) {
+ throw new RuntimeException("eglCreateContext failed");
+ }
+ EGL14.eglDestroyContext(mEglDisplay, mEglContext);
+ }
}
// Note: Need to add tests for eglCreatePlatformWindowSurface
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
index 0374082..a736878 100644
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanDeqpLevelTest.java
@@ -68,7 +68,7 @@
}
}
- @CddTest(requirement = "7.1.4.2/C-1-8")
+ @CddTest(requirement = "7.1.4.2/C-1-8,C-1-9")
@Test
public void testVulkanDeqpLevel() {
if (mVulkanHardwareVersion.version >= VULKAN_1_0) {
diff --git a/tests/tests/media/src/android/media/cts/AudioMetadataTest.java b/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
index bafd83d..9a7cd26 100755
--- a/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.*;
import static org.testng.Assert.assertThrows;
+import android.media.AudioFormat;
import android.media.AudioMetadata;
import android.media.AudioMetadataMap;
import android.media.AudioMetadataReadMap;
@@ -117,6 +118,31 @@
assertEquals(audioMetadata, audioMetadata.dup());
}
+ @Test
+ public void testFormatKeys() throws Exception {
+ final AudioMetadataMap audioMetadata = AudioMetadata.createMap();
+ audioMetadata.set(AudioMetadata.Format.KEY_ATMOS_PRESENT, true);
+ audioMetadata.set(AudioMetadata.Format.KEY_AUDIO_ENCODING, AudioFormat.ENCODING_MP3);
+ audioMetadata.set(AudioMetadata.Format.KEY_BIT_RATE, 64000);
+ audioMetadata.set(AudioMetadata.Format.KEY_BIT_WIDTH, 16);
+ audioMetadata.set(AudioMetadata.Format.KEY_CHANNEL_MASK, AudioFormat.CHANNEL_OUT_STEREO);
+ audioMetadata.set(AudioMetadata.Format.KEY_MIME, "audio/mp3");
+ audioMetadata.set(AudioMetadata.Format.KEY_SAMPLE_RATE, 48000);
+
+ assertEquals(64000, (int)audioMetadata.get(AudioMetadata.Format.KEY_BIT_RATE));
+ assertEquals(AudioFormat.CHANNEL_OUT_STEREO,
+ (int)audioMetadata.get(AudioMetadata.Format.KEY_CHANNEL_MASK));
+ assertEquals("audio/mp3", (String)audioMetadata.get(AudioMetadata.Format.KEY_MIME));
+ assertEquals(48000, (int)audioMetadata.get(AudioMetadata.Format.KEY_SAMPLE_RATE));
+ assertEquals(16, (int)audioMetadata.get(AudioMetadata.Format.KEY_BIT_WIDTH));
+ assertEquals(true, (boolean)audioMetadata.get(AudioMetadata.Format.KEY_ATMOS_PRESENT));
+ assertEquals(AudioFormat.ENCODING_MP3,
+ (int)audioMetadata.get(AudioMetadata.Format.KEY_AUDIO_ENCODING));
+
+ // Additional test to ensure we can survive parceling
+ testPackingAndUnpacking((AudioMetadata.BaseMap)audioMetadata);
+ }
+
// Vendor keys created by direct override of the AudioMetadata interface.
private static final AudioMetadata.Key<Integer>
KEY_VENDOR_INTEGER = new AudioMetadata.Key<Integer>() {
@@ -276,16 +302,7 @@
@Test
public void testUnpackingByteBuffer() throws Exception {
- ByteBuffer bufferPackedAtJava = AudioMetadata.toByteBuffer(
- AUDIO_METADATA_REFERENCE, ByteOrder.nativeOrder());
- assertNotNull(bufferPackedAtJava);
- ByteBuffer buffer = nativeGetByteBuffer(bufferPackedAtJava, bufferPackedAtJava.limit());
- assertNotNull(buffer);
- buffer.order(ByteOrder.nativeOrder());
-
- AudioMetadata.BaseMap metadataFromByteBuffer = AudioMetadata.fromByteBuffer(buffer);
- assertNotNull(metadataFromByteBuffer);
- assertEquals(metadataFromByteBuffer, AUDIO_METADATA_REFERENCE);
+ testPackingAndUnpacking(AUDIO_METADATA_REFERENCE);
}
@Test
@@ -303,6 +320,19 @@
assertNull(metadataFromByteBuffer);
}
+ private static void testPackingAndUnpacking(AudioMetadata.BaseMap audioMetadata) {
+ ByteBuffer bufferPackedAtJava = AudioMetadata.toByteBuffer(
+ audioMetadata, ByteOrder.nativeOrder());
+ assertNotNull(bufferPackedAtJava);
+ ByteBuffer buffer = nativeGetByteBuffer(bufferPackedAtJava, bufferPackedAtJava.limit());
+ assertNotNull(buffer);
+ buffer.order(ByteOrder.nativeOrder());
+
+ AudioMetadata.BaseMap metadataFromByteBuffer = AudioMetadata.fromByteBuffer(buffer);
+ assertNotNull(metadataFromByteBuffer);
+ assertEquals(metadataFromByteBuffer, audioMetadata);
+ }
+
static {
System.loadLibrary("audio_jni");
}
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index 739bc21..39d8f1b 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -16,7 +16,6 @@
package android.os.cts
-import android.Manifest.permission.READ_CALENDAR
import android.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.content.pm.PackageManager
@@ -175,17 +174,18 @@
}
}
- // TODO grantRuntimePermission fails to grant permission
@AppModeFull(reason = "Uses separate apps for testing")
- fun _testInstallGrants_notRevokedImmediately() {
+ fun testInstallGrants_notRevokedImmediately() {
wakeUpScreen()
withUnusedThresholdMs(TimeUnit.DAYS.toMillis(30)) {
withDummyApp {
// Setup
- runWithShellPermissionIdentity {
- instrumentation.uiAutomation
- .grantRuntimePermission(APK_PACKAGE_NAME, READ_CALENDAR)
- }
+ goToPermissions()
+ click("Calendar")
+ click("Allow")
+ goBack()
+ goBack()
+ goBack()
eventually {
assertPermission(PERMISSION_GRANTED)
}
@@ -283,11 +283,7 @@
// }
try {
- context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
- .setData(Uri.fromParts("package", packageName, null))
- .addFlags(FLAG_ACTIVITY_NEW_TASK))
-
- waitFindNode(hasTextThat(containsStringIgnoringCase("Permissions"))).click()
+ goToPermissions(packageName)
waitForIdle()
val ui = instrumentation.uiAutomation.rootInActiveWindow
@@ -315,6 +311,18 @@
}
}
+ private fun goToPermissions(packageName: String = APK_PACKAGE_NAME) {
+ context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", packageName, null))
+ .addFlags(FLAG_ACTIVITY_NEW_TASK))
+
+ click("Permissions")
+ }
+
+ private fun click(label: String) {
+ waitFindNode(hasTextThat(containsStringIgnoringCase(label))).click()
+ }
+
private fun assertWhitelistState(state: Boolean) {
assertThat(
waitFindObject(By.textStartsWith("Auto-revoke whitelisted: ")).text,
diff --git a/tests/tests/permission3/Android.bp b/tests/tests/permission3/Android.bp
index 77e2934..be534b7 100644
--- a/tests/tests/permission3/Android.bp
+++ b/tests/tests/permission3/Android.bp
@@ -38,6 +38,7 @@
":CtsUsePermissionApp28",
":CtsUsePermissionApp29",
":CtsUsePermissionAppLatest",
+ ":CtsUsePermissionAppLatestWithBackground",
],
test_suites: [
"cts",
diff --git a/tests/tests/permission3/AndroidTest.xml b/tests/tests/permission3/AndroidTest.xml
index 7d80f2c..c4adbfc 100644
--- a/tests/tests/permission3/AndroidTest.xml
+++ b/tests/tests/permission3/AndroidTest.xml
@@ -42,6 +42,7 @@
<option name="push" value="CtsUsePermissionApp28.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp28.apk" />
<option name="push" value="CtsUsePermissionApp29.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp29.apk" />
<option name="push" value="CtsUsePermissionAppLatest.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatest.apk" />
+ <option name="push" value="CtsUsePermissionAppLatestWithBackground.apk->/data/local/tmp/cts/permission3/CtsUsePermissionAppLatestWithBackground.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.bp b/tests/tests/permission3/UsePermissionAppLatestWithBackground/Android.bp
similarity index 62%
rename from hostsidetests/appsecurity/test-apps/tinyapp/Android.bp
rename to tests/tests/permission3/UsePermissionAppLatestWithBackground/Android.bp
index b650bb4..65bc605 100644
--- a/hostsidetests/appsecurity/test-apps/tinyapp/Android.bp
+++ b/tests/tests/permission3/UsePermissionAppLatestWithBackground/Android.bp
@@ -1,4 +1,5 @@
-// Copyright (C) 2014 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.
@@ -11,19 +12,15 @@
// 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: "CtsPkgInstallTinyApp",
- defaults: ["cts_support_defaults"],
- // tag this module as a cts test artifact
- test_suites: [
- "cts",
- "vts10",
- "general-tests",
+ name: "CtsUsePermissionAppLatestWithBackground",
+ srcs: [
+ "src/**/*.kt",
],
- srcs: ["src/**/*.java"],
- sdk_version: "current",
- dex_preopt: {
- enabled: false,
- },
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+ certificate: ":cts-testkey2",
}
diff --git a/tests/tests/permission3/UsePermissionAppLatestWithBackground/AndroidManifest.xml b/tests/tests/permission3/UsePermissionAppLatestWithBackground/AndroidManifest.xml
new file mode 100644
index 0000000..adf2eac
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionAppLatestWithBackground/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.permission3.cts.usepermission">
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+
+ <application>
+ <activity android:name=".RequestPermissionsActivity" android:exported="true" />
+ </application>
+</manifest>
diff --git a/tests/tests/permission3/UsePermissionAppLatestWithBackground/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt b/tests/tests/permission3/UsePermissionAppLatestWithBackground/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
new file mode 100644
index 0000000..1c30c87
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionAppLatestWithBackground/src/android/permission3/cts/usepermission/RequestPermissionsActivity.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.permission3.cts.usepermission
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+
+class RequestPermissionsActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val permissions = intent.getStringArrayExtra("$packageName.PERMISSIONS")!!
+ requestPermissions(permissions, 1)
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array<out String>,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+
+ setResult(RESULT_OK, Intent().apply {
+ putExtra("$packageName.PERMISSIONS", permissions)
+ putExtra("$packageName.GRANT_RESULTS", grantResults)
+ })
+ finish()
+ }
+}
diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
index e689188..6672f7e 100644
--- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt
@@ -50,6 +50,8 @@
const val APP_APK_PATH_28 = "$APK_DIRECTORY/CtsUsePermissionApp28.apk"
const val APP_APK_PATH_29 = "$APK_DIRECTORY/CtsUsePermissionApp29.apk"
const val APP_APK_PATH_LATEST = "$APK_DIRECTORY/CtsUsePermissionAppLatest.apk"
+ const val APP_APK_PATH_LATEST_WITH_BACKGROUND =
+ "$APK_DIRECTORY/CtsUsePermissionAppLatestWithBackground.apk"
const val APP_PACKAGE_NAME = "android.permission3.cts.usepermission"
}
@@ -266,10 +268,14 @@
protected fun clickPermissionRequestSettingsLinkAndAllowAlways() {
clickPermissionRequestSettingsLink()
- click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+ clickAllowAlwaysInSettings()
pressBack()
}
+ protected fun clickAllowAlwaysInSettings() {
+ click(By.res("com.android.permissioncontroller:id/allow_always_radio_button"))
+ }
+
protected fun clickPermissionRequestAllowForegroundButton() =
click(By.res("com.android.permissioncontroller:id/permission_allow_foreground_only_button"))
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
new file mode 100644
index 0000000..0abae86
--- /dev/null
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30.kt
@@ -0,0 +1,55 @@
+/*
+ * 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.permission3.cts
+
+import android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
+import android.Manifest.permission.ACCESS_FINE_LOCATION
+import org.junit.Test
+
+/**
+ * Runtime permission behavior apps targeting API 30
+ */
+class PermissionTest30 : BaseUsePermissionTest() {
+
+ @Test
+ fun testCantRequestFgAndBgAtOnce() {
+ installPackage(APP_APK_PATH_LATEST_WITH_BACKGROUND)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(ACCESS_FINE_LOCATION to false,
+ ACCESS_BACKGROUND_LOCATION to false) {
+ // Do nothing, should be automatically denied
+ }
+ }
+
+ @Test
+ fun testRequestBothInSequence() {
+ installPackage(APP_APK_PATH_LATEST_WITH_BACKGROUND)
+ assertAppHasPermission(ACCESS_FINE_LOCATION, false)
+ assertAppHasPermission(ACCESS_BACKGROUND_LOCATION, false)
+
+ requestAppPermissionsAndAssertResult(ACCESS_FINE_LOCATION to true) {
+ clickPermissionRequestAllowForegroundButton()
+ }
+
+ requestAppPermissionsAndAssertResult(ACCESS_BACKGROUND_LOCATION to true) {
+ clickAllowAlwaysInSettings()
+ pressBack()
+ }
+ }
+}
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 106cd6f..0435a45 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
@@ -112,16 +112,22 @@
final long unique2 = System.nanoTime();
final String TEST_TITLE2 = "Title " + unique2;
+ File file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
+ "mediaStoreTest1.jpg");
+ String path = file.getAbsolutePath();
+ ProviderTestUtils.stageFile(R.raw.scenery, file);
+
+ file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
+ "mediaStoreTest2.jpg");
+ path = file.getAbsolutePath();
+ ProviderTestUtils.stageFile(R.raw.scenery, file);
+
Cursor c = Media.query(mContentResolver, mExternalImages, null, null,
"_id ASC");
int previousCount = c.getCount();
c.close();
// insert an image by path
- File file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
- "mediaStoreTest1.jpg");
- String path = file.getAbsolutePath();
- ProviderTestUtils.stageFile(R.raw.scenery, file);
String stringUrl = null;
try {
stringUrl = Media.insertImage(mContentResolver, path, TEST_TITLE1, null);
@@ -134,10 +140,6 @@
assertInsertionSuccess(stringUrl);
// insert another image by path
- file = new File(ProviderTestUtils.stageDir(MediaStore.VOLUME_EXTERNAL),
- "mediaStoreTest2.jpg");
- path = file.getAbsolutePath();
- ProviderTestUtils.stageFile(R.raw.scenery, file);
stringUrl = null;
try {
stringUrl = Media.insertImage(mContentResolver, path, TEST_TITLE2, null);
diff --git a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
index 4e0d109..384213f 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp1/apk/signed-CtsSecureElementAccessControlTestCases1.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
index aac758e..7c2fd22 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp2/apk/signed-CtsSecureElementAccessControlTestCases2.apk
Binary files differ
diff --git a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
index 040251e..8e8fef8 100644
--- a/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
+++ b/tests/tests/secure_element/access_control/AccessControlApp3/apk/signed-CtsSecureElementAccessControlTestCases3.apk
Binary files differ
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/BatteryStatsManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/BatteryStatsManagerTest.java
new file mode 100644
index 0000000..f16c83d
--- /dev/null
+++ b/tests/tests/telephony/current/src/android/telephony/cts/BatteryStatsManagerTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.telephony.cts;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.anyInt;
+
+import android.os.BatteryStatsManager;
+import android.os.connectivity.CellularBatteryStats;
+
+import org.junit.Test;
+
+/**
+ * Test BatteryStatsManager and CellularBatteryStats to ensure that valid data is being reported
+ * and that invalid data is not reported.
+ */
+public class BatteryStatsManagerTest{
+
+ /** Test that {@link CellularBatteryStats} getters return sane values. */
+ @Test
+ public void testGetCellularBatteryStats() {
+ BatteryStatsManager bsm = getContext().getSystemService(BatteryStatsManager.class);
+ CellularBatteryStats cellularBatteryStats = bsm.getCellularBatteryStats();
+
+ assertThat(cellularBatteryStats.getEnergyConsumedMaMillis()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getIdleTimeMillis()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getLoggingDurationMillis()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getKernelActiveTimeMillis()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getMonitoredRailChargeConsumedMaMillis()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getNumBytesRx()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getNumBytesTx()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getNumPacketsRx()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getNumPacketsTx()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getRxTimeMillis()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getSleepTimeMillis()).isAtLeast(0L);
+ assertThat(cellularBatteryStats.getTimeInRatMicros(anyInt())).isAtLeast(-1L);
+ assertThat(cellularBatteryStats.getTimeInRxSignalStrengthLevelMicros(
+ anyInt())).isAtLeast(-1L);
+ }
+}
+
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 ea2108b..b7bd6d0 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/CellInfoTest.java
@@ -552,7 +552,7 @@
if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
int[] bands = nr.getBands();
for (int band: bands) {
- assertTrue("getBand out of range [1, 95] or [257, 261]",
+ assertTrue("getBand out of range [1, 95] or [257, 261], band = " + band,
(band >= BAND_FR1_MIN_NR && band <= BAND_FR1_MAX_NR)
|| (band >= BAND_FR2_MIN_NR && band <= BAND_FR2_MAX_NR));
}
@@ -649,7 +649,7 @@
if (mRadioHalVersion >= RADIO_HAL_VERSION_1_5) {
int[] bands = lte.getBands();
for (int band: bands) {
- assertTrue("getBand out of range [1, 88]",
+ assertTrue("getBand out of range [1, 88], band = " + band,
band >= BAND_MIN_LTE && band <= BAND_MAX_LTE);
}
}
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 0916756..d9ab2f3 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -2815,6 +2815,34 @@
tm -> tm.setOpportunisticNetworkState(isEnabled));
}
+ @Test
+ public void testGetSimApplicationState() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ int simApplicationState = mTelephonyManager.getSimApplicationState();
+ assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+ TelephonyManager.SIM_STATE_PIN_REQUIRED,
+ TelephonyManager.SIM_STATE_PUK_REQUIRED,
+ TelephonyManager.SIM_STATE_NETWORK_LOCKED,
+ TelephonyManager.SIM_STATE_NOT_READY,
+ TelephonyManager.SIM_STATE_PERM_DISABLED,
+ TelephonyManager.SIM_STATE_LOADED).contains(simApplicationState));
+ }
+
+ @Test
+ public void testGetSimCardState() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ return;
+ }
+ int simCardState = mTelephonyManager.getSimCardState();
+ assertTrue(Arrays.asList(TelephonyManager.SIM_STATE_UNKNOWN,
+ TelephonyManager.SIM_STATE_ABSENT,
+ TelephonyManager.SIM_STATE_CARD_IO_ERROR,
+ TelephonyManager.SIM_STATE_CARD_RESTRICTED,
+ TelephonyManager.SIM_STATE_PRESENT).contains(simCardState));
+ }
+
private boolean isDataEnabled() {
return ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
diff --git a/tests/tests/tethering/AndroidTest.xml b/tests/tests/tethering/AndroidTest.xml
index 25051ba..d0a2bce 100644
--- a/tests/tests/tethering/AndroidTest.xml
+++ b/tests/tests/tethering/AndroidTest.xml
@@ -16,6 +16,7 @@
<configuration description="Config for CTS Tethering test cases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="networking" />
+ <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />