Merge "Statsd CTS tests for proc state"
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 77807bb..10c33ba 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -38,12 +38,14 @@
mockito-target-minus-junit4 \
mockwebserver \
compatibility-device-util \
- platform-test-annotations
+ platform-test-annotations \
+ cts-security-test-support-library
LOCAL_JAVA_LIBRARIES += telephony-common
LOCAL_JAVA_LIBRARIES += android.test.runner.stubs
LOCAL_JAVA_LIBRARIES += android.test.base.stubs
LOCAL_JAVA_LIBRARIES += android.test.mock.stubs
+LOCAL_JAVA_LIBRARIES += bouncycastle
LOCAL_PACKAGE_NAME := CtsVerifier
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index cc38c8d..b15532b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1580,7 +1580,7 @@
android:value="@string/test_category_sensors" />
<meta-data
android:name="test_required_features"
- android:value="android.hardware.sensor.accelerometer:android.hardware.sensor.gyroscope:android.hardware.sensor.compass:android.hardware.camera.any" />
+ android:value="android.hardware.sensor.accelerometer:android.hardware.sensor.gyroscope:android.hardware.sensor.compass:android.hardware.camera" />
</activity>
<activity
android:name=".sensors.RVCVRecordActivity"
@@ -2391,6 +2391,14 @@
</intent-filter>
</activity>
+ <activity android:name=".managedprovisioning.KeyChainTestActivity"
+ android:label="@string/provisioning_byod_keychain">
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.KEYCHAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".managedprovisioning.PermissionLockdownTestActivity"
android:label="@string/device_profile_owner_permission_lockdown_test">
<intent-filter>
diff --git a/apps/CtsVerifier/res/layout/keychain_test.xml b/apps/CtsVerifier/res/layout/keychain_test.xml
new file mode 100644
index 0000000..09708c7
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/keychain_test.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="320dip"
+ android:layout_weight="2">
+ <TextView
+ android:id="@+id/provisioning_byod_keychain_instructions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:text="@string/provisioning_byod_keychain_info_start"
+ android:textSize="18dip" />
+ </ScrollView>
+
+ <TextView
+ android:id="@+id/provisioning_byod_keychain_test_log"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:textSize="18dip" />
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+
+ <Button
+ android:id="@+id/prepare_test_button"
+ android:layout_width="204dip"
+ android:layout_height="wrap_content"
+ android:text="@string/provisioning_byod_keyguard_disabled_features_prepare_button"/>
+
+ <Button
+ android:id="@+id/run_test_button"
+ android:layout_width="204dip"
+ android:layout_height="wrap_content"
+ android:text="@string/go_button_text"/>
+
+ </LinearLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 0e6ab31..14706dd 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1935,6 +1935,35 @@
The work profile still has a separate password. Please remove this before continuing.
</string>
+ <string name="provisioning_byod_keychain">KeyChain test</string>
+ <string name="provisioning_byod_keychain_info_start">
+ In this test, you\'ll verify that keys generated by KeyChain keys are as usable as keys
+ installed into KeyChain and that they can be hidden from users.\n
+ The test has two parts:\n
+ 1) Testing that a generated key can be selectable by the user.\n
+ 2) Testing that a generated key can be hidden from users.\n
+ \n
+ Tap \"Prepare Test\" button below to begin.\n
+ \n
+ NOTE: A screen lock must be configured for this test. Otherwise, test preparation
+ will fail to generate a key for use by the test.
+ </string>
+ <string name="provisioning_byod_keychain_info_first_test">
+ Once you press \'Go\', a prompt titled \"Choose certificate\" should appear.\n
+ Verify that the list in this dialog has one item, starting with \'cts-verifier-gen\'.
+ Press \'Select\' to select it.\n
+ If the test passes, you\'ll see the text \"Second test ready\" at the bottom.\n
+ \n
+ Press \'Go\'.\n
+ </string>
+ <string name="provisioning_byod_keychain_info_second_test">
+ Once you press \'Run 2nd test\', the same prompt should appear again.\n
+ This time, verify that the title is \"No certificates found\" and the list is empty,
+ then press \'Cancel\'.\n
+ \n
+ Mark the test as passed if the text at the bottom shows \"PASSED (2/2)\"\n
+ </string>
+
<!-- Strings for DeskClock -->
<string name="deskclock_tests">Alarms and Timers Tests</string>
<string name="deskclock_tests_info">
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 15808a7..d7a5033 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -101,6 +101,7 @@
private DialogTestListItem mConfirmWorkCredentials;
private DialogTestListItem mParentProfilePassword;
private TestListItem mVpnTest;
+ private TestListItem mKeyChainTest;
private TestListItem mAlwaysOnVpnSettingsTest;
private TestListItem mRecentsTest;
private TestListItem mDisallowAppsControlTest;
@@ -415,6 +416,12 @@
new Intent(this, OrganizationInfoTestActivity.class),
null);
+ mKeyChainTest = TestListItem.newTest(this,
+ R.string.provisioning_byod_keychain,
+ KeyChainTestActivity.class.getName(),
+ new Intent(KeyChainTestActivity.ACTION_KEYCHAIN),
+ null);
+
mParentProfilePassword = new DialogTestListItem(this,
R.string.provisioning_byod_parent_profile_password,
"BYOD_ParentProfilePasswordTest",
@@ -563,6 +570,7 @@
}
};
adapter.add(mDisableNfcBeamTest);
+ adapter.add(mKeyChainTest);
}
/* If there is an application that handles RECORD_SOUND_ACTION, test that it handles it
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
index 5053a90..bfa65b7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestHelper.java
@@ -42,7 +42,8 @@
AlwaysOnVpnSettingsTestActivity.class.getName(),
RecentsRedactionActivity.class.getName(),
CommandReceiverActivity.class.getName(),
- SetSupportMessageActivity.class.getName()
+ SetSupportMessageActivity.class.getName(),
+ KeyChainTestActivity.class.getName()
};
for (String component : components) {
mPackageManager.setComponentEnabledSetting(new ComponentName(mContext, component),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index a9cee2b..8486765 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -149,6 +149,7 @@
filter.addAction(ByodHelperActivity.ACTION_SET_ORGANIZATION_INFO);
filter.addAction(ByodHelperActivity.ACTION_TEST_PARENT_PROFILE_PASSWORD);
filter.addAction(SetSupportMessageActivity.ACTION_SET_SUPPORT_MSG);
+ filter.addAction(KeyChainTestActivity.ACTION_KEYCHAIN);
filter.addAction(CommandReceiverActivity.ACTION_EXECUTE_COMMAND);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java
new file mode 100644
index 0000000..a59261c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/KeyChainTestActivity.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import static android.keystore.cts.CertificateUtils.createCertificate;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.security.AttestedKeyPair;
+import android.security.KeyChain;
+import android.security.KeyChainAliasCallback;
+import android.security.KeyChainException;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import java.security.GeneralSecurityException;
+import java.security.Principal;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import javax.security.auth.x500.X500Principal;
+
+/**
+ * Activity to test KeyChain key generation. The following flows are tested: * Generating a key. *
+ * Installing a (self-signed) certificate associated with the key, visible to users. * Setting
+ * visibility of the certificate to not be visible to user.
+ *
+ * <p>After the key generation and certificate installation, it should be possible for a user to
+ * select the key from the certificate selection prompt when {@code KeyChain.choosePrivateKeyAlias}
+ * is called. The test then tests that the key is indeed usable for signing.
+ *
+ * <p>After the visibility is set to not-user-visible, the prompt is shown again, this time the
+ * testes is asked to verify no keys are selectable and cancel the dialog.
+ */
+public class KeyChainTestActivity extends PassFailButtons.Activity {
+ private static final String TAG = "ByodKeyChainActivity";
+
+ public static final String ACTION_KEYCHAIN =
+ "com.android.cts.verifier.managedprovisioning.KEYCHAIN";
+
+ public static final String ALIAS = "cts-verifier-gen-rsa-1";
+ public static final String KEY_ALGORITHM = "RSA";
+
+ private DevicePolicyManager mDevicePolicyManager;
+ private AttestedKeyPair mAttestedKeyPair;
+ private X509Certificate mCert;
+ private TextView mLogView;
+ private TextView mInstructionsView;
+ private Button mSetupButton;
+ private Button mGoButton;
+
+ // Callback interface for when a key is generated.
+ static interface KeyGenerationListener {
+ void onKeyPairGenerated(AttestedKeyPair keyPair);
+ }
+
+ // Task for generating a key pair using {@code DevicePolicyManager.generateKeyPair}.
+ // The listener, if provided, will be invoked after the key has been generated successfully.
+ class GenerateKeyTask extends AsyncTask<KeyGenParameterSpec, Integer, AttestedKeyPair> {
+ KeyGenerationListener mListener;
+
+ public GenerateKeyTask(KeyGenerationListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ protected AttestedKeyPair doInBackground(KeyGenParameterSpec... specs) {
+ Log.i(TAG, "Generating key pair.");
+ try {
+ AttestedKeyPair kp =
+ mDevicePolicyManager.generateKeyPair(
+ DeviceAdminTestReceiver.getReceiverComponentName(),
+ KEY_ALGORITHM,
+ specs[0],
+ 0);
+ if (kp != null) {
+ mLogView.setText("Key generated successfully.");
+ } else {
+ mLogView.setText("Failed generating key.");
+ }
+ return kp;
+ } catch (SecurityException e) {
+ mLogView.setText("Security exception while generating key.");
+ Log.w(TAG, "Security exception", e);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(AttestedKeyPair kp) {
+ super.onPostExecute(kp);
+ if (mListener != null && kp != null) {
+ mListener.onKeyPairGenerated(kp);
+ }
+ }
+ }
+
+ // Helper for generating and installing a self-signed certificate.
+ class CertificateInstaller implements KeyGenerationListener {
+ @Override
+ public void onKeyPairGenerated(AttestedKeyPair keyPair) {
+ mAttestedKeyPair = keyPair;
+ X500Principal issuer = new X500Principal("CN=SelfSigned, O=Android, C=US");
+ X500Principal subject = new X500Principal("CN=Subject, O=Android, C=US");
+ try {
+ mCert = createCertificate(mAttestedKeyPair.getKeyPair(), subject, issuer);
+ boolean installResult = installCertificate(mCert, true);
+ // called from onPostExecute so safe to interact with the UI here.
+ if (installResult) {
+ mLogView.setText("Test ready");
+ mInstructionsView.setText(R.string.provisioning_byod_keychain_info_first_test);
+ mGoButton.setEnabled(true);
+ } else {
+ mLogView.setText("FAILED certificate installation.");
+ }
+ } catch (Exception e) {
+ Log.w(TAG, "Failed installing certificate", e);
+ mLogView.setText("Error generating a certificate.");
+ }
+ }
+ }
+
+ // Helper for calling {@code DevicePolicyManager.setKeyPairCertificate} with the user-visibility
+ // specified in the constructor. Returns true if the call was successful (and no exceptions
+ // were thrown).
+ protected boolean installCertificate(X509Certificate cert, boolean isUserVisible) {
+ try {
+ return mDevicePolicyManager.setKeyPairCertificate(
+ DeviceAdminTestReceiver.getReceiverComponentName(),
+ ALIAS,
+ Arrays.asList(new X509Certificate[] {cert}),
+ isUserVisible);
+ } catch (SecurityException e) {
+ logStatus("Security exception while installing cert.");
+ Log.w(TAG, "Security exception", e);
+ }
+ return false;
+ }
+
+ // Invokes choosePrivateKeyAlias.
+ void selectCertificate(KeyChainAliasCallback callback) {
+ String[] keyTypes = new String[] {KEY_ALGORITHM};
+ Principal[] issuers = new Principal[0];
+ KeyChain.choosePrivateKeyAlias(
+ KeyChainTestActivity.this, callback, keyTypes, issuers, null, null);
+ }
+
+ class TestPreparator implements View.OnClickListener {
+ @Override
+ public void onClick(View v) {
+ mLogView.setText("Starting key generation");
+ KeyGenParameterSpec spec =
+ new KeyGenParameterSpec.Builder(
+ ALIAS,
+ KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setKeySize(2048)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setSignaturePaddings(
+ KeyProperties.SIGNATURE_PADDING_RSA_PSS,
+ KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .build();
+ new GenerateKeyTask(new CertificateInstaller()).execute(spec);
+ }
+ }
+
+ class SelectCertificate implements View.OnClickListener, KeyChainAliasCallback {
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG, "Selecting certificate");
+ mLogView.setText("Waiting for prompt");
+ selectCertificate(this);
+ }
+
+ @Override
+ public void alias(String alias) {
+ Log.i(TAG, "Got alias: " + alias);
+ if (alias == null) {
+ logStatus("FAILED (no alias)");
+ return;
+ } else if (!alias.equals(ALIAS)) {
+ logStatus("FAILED (wrong alias)");
+ return;
+ }
+ logStatus("Got right alias.");
+ try {
+ PrivateKey privateKey = KeyChain.getPrivateKey(KeyChainTestActivity.this, alias);
+ byte[] data = new String("hello").getBytes();
+ Signature sign = Signature.getInstance("SHA256withRSA");
+ sign.initSign(privateKey);
+ sign.update(data);
+ if (sign.sign() != null) {
+ prepareSecondTest();
+ } else {
+ logStatus("FAILED (cannot sign)");
+ }
+ } catch (GeneralSecurityException | KeyChainException | InterruptedException e) {
+ Log.w(TAG, "Failed using the key", e);
+ logStatus("FAILED (key unusable)");
+ }
+ }
+ }
+
+ class SelectCertificateExpectingNone implements View.OnClickListener, KeyChainAliasCallback {
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG, "Selecting certificate");
+ mLogView.setText("Waiting for prompt");
+ selectCertificate(this);
+ }
+
+ @Override
+ public void alias(String alias) {
+ Log.i(TAG, "Got alias: " + alias);
+ if (alias != null) {
+ logStatus("FAILED: Should have no certificate.");
+ } else {
+ logStatus("PASSED (2/2)");
+ runOnUiThread(
+ () -> {
+ getPassButton().setEnabled(true);
+ });
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.keychain_test);
+ setPassFailButtonClickListeners();
+ mDevicePolicyManager =
+ (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
+
+ mLogView = (TextView) findViewById(R.id.provisioning_byod_keychain_test_log);
+ mLogView.setMovementMethod(new ScrollingMovementMethod());
+
+ mInstructionsView = (TextView) findViewById(R.id.provisioning_byod_keychain_instructions);
+
+ mSetupButton = (Button) findViewById(R.id.prepare_test_button);
+ mSetupButton.setOnClickListener(new TestPreparator());
+
+ mGoButton = (Button) findViewById(R.id.run_test_button);
+ mGoButton.setOnClickListener(new SelectCertificate());
+ mGoButton.setEnabled(false);
+
+ // Disable the pass button here, only enable it when the 2nd test passes.
+ getPassButton().setEnabled(false);
+ }
+
+ protected void prepareSecondTest() {
+ Runnable uiChanges;
+ if (installCertificate(mCert, false)) {
+ uiChanges =
+ () -> {
+ mLogView.setText("Second test ready.");
+ mInstructionsView.setText(
+ R.string.provisioning_byod_keychain_info_second_test);
+ mGoButton.setText("Run 2nd test");
+ mGoButton.setOnClickListener(new SelectCertificateExpectingNone());
+ };
+ } else {
+ uiChanges =
+ () -> {
+ mLogView.setText("FAILED second test setup.");
+ mGoButton.setEnabled(false);
+ };
+ }
+
+ runOnUiThread(uiChanges);
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ try {
+ mDevicePolicyManager.removeKeyPair(
+ DeviceAdminTestReceiver.getReceiverComponentName(), ALIAS);
+ Log.i(TAG, "Deleted alias " + ALIAS);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Failed deleting alias", e);
+ }
+ }
+
+ private void logStatus(String status) {
+ runOnUiThread(
+ () -> {
+ mLogView.setText(status);
+ });
+ }
+}
diff --git a/build/compatibility_test_suite.mk b/build/compatibility_test_suite.mk
index d133c3e..9bad76e 100644
--- a/build/compatibility_test_suite.mk
+++ b/build/compatibility_test_suite.mk
@@ -46,6 +46,14 @@
LOCAL_MODULE_TAGS := optional
+# If DynamicConfig.xml exists copy it inside the jar
+ifneq (,$(wildcard $(LOCAL_PATH)/DynamicConfig.xml))
+ dynamic_config_local := $(call intermediates-dir-for,JAVA_LIBRARIES,$(LOCAL_MODULE),true,COMMON)/$(LOCAL_MODULE).dynamic
+ $(eval $(call copy-one-file,$(LOCAL_PATH)/DynamicConfig.xml,$(dynamic_config_local)))
+ LOCAL_JAVA_RESOURCE_FILES += $(dynamic_config_local)
+endif
+
include $(BUILD_HOST_JAVA_LIBRARY)
+dynamic_config_local :=
suite_info_prop :=
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
new file mode 100644
index 0000000..fd05398
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicConditionalTestCase.java
@@ -0,0 +1,54 @@
+/*
+ * 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.compatibility.common.util;
+
+import org.junit.Before;
+
+/**
+ * Device-side base class for tests leveraging the Business Logic service for rules that are
+ * conditionally added based on the device characteristics.
+ */
+public class BusinessLogicConditionalTestCase extends BusinessLogicTestCase {
+
+ @Override
+ @Before
+ public void handleBusinessLogic() {
+ super.loadBuisnessLogic();
+ ensureAuthenticated();
+ super.executeBusinessLogic();
+ }
+
+ protected void ensureAuthenticated() {
+ if (!mCanReadBusinessLogic) {
+ // super class handles the condition that the service is unavailable.
+ return;
+ }
+
+ if (!mBusinessLogic.mConditionalTestsEnabled) {
+ skipTest("Execution of device specific tests is not enabled. "
+ + "Enable with '--conditional-business-logic-tests-enabled'");
+ }
+
+ if (mBusinessLogic.isAuthorized()) {
+ // Run test as normal.
+ return;
+ }
+ String message = mBusinessLogic.getAuthenticationStatusMessage();
+
+ // Fail test since request was not authorized.
+ failTest(String.format("Unable to execute because %s.", message));
+ }
+}
\ No newline at end of file
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
index 2316637..45f979a 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/BusinessLogicTestCase.java
@@ -45,21 +45,16 @@
/* Test name rule that tracks the current test method under execution */
@Rule public TestName mTestCase = new TestName();
- private static BusinessLogic mBusinessLogic;
- private static boolean mCanReadBusinessLogic = true;
-
- @BeforeClass
- public static void prepareBusinessLogic() {
- File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
- if (businessLogicFile.canRead()) {
- mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
- } else {
- mCanReadBusinessLogic = false;
- }
- }
+ protected BusinessLogic mBusinessLogic;
+ protected boolean mCanReadBusinessLogic = true;
@Before
- public void executeBusinessLogic() {
+ public void handleBusinessLogic() {
+ loadBuisnessLogic();
+ executeBusinessLogic();
+ }
+
+ protected void executeBusinessLogic() {
String methodName = mTestCase.getMethodName();
assertTrue(String.format("Test \"%s\" is unable to execute as it depends on the missing "
+ "remote configuration.", methodName), mCanReadBusinessLogic);
@@ -75,6 +70,15 @@
}
}
+ protected void loadBuisnessLogic() {
+ File businessLogicFile = new File(BusinessLogic.DEVICE_FILE);
+ if (businessLogicFile.canRead()) {
+ mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
+ } else {
+ mCanReadBusinessLogic = false;
+ }
+ }
+
protected static Instrumentation getInstrumentation() {
return InstrumentationRegistry.getInstrumentation();
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
index d33a471..e4ad8fd 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/BusinessLogicPreparer.java
@@ -18,6 +18,7 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.tradefed.util.DynamicConfigFileReader;
import com.android.compatibility.common.util.BusinessLogic;
+import com.android.compatibility.common.util.BusinessLogicFactory;
import com.android.compatibility.common.util.FeatureUtil;
import com.android.compatibility.common.util.PropertyUtil;
import com.android.tradefed.build.IBuildInfo;
@@ -36,12 +37,19 @@
import com.android.tradefed.util.net.HttpHelper;
import com.android.tradefed.util.net.IHttpHelper;
+import com.google.api.client.auth.oauth2.Credential;
+import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
+import com.google.common.base.Strings;
+
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -59,13 +67,15 @@
private static final String FILE_LOCATION = "business-logic";
/* Extension of business logic files */
private static final String FILE_EXT = ".bl";
-
- /* Default amount of time to attempt connection to the business logic service, in seconds */
- private static final int DEFAULT_CONNECTION_TIME = 10;
-
+ /* URI of api scope to use when retrieving business logic rules */
+ private static final String APE_API_SCOPE = "https://www.googleapis.com/auth/androidPartner";
/* Dynamic config constants */
private static final String DYNAMIC_CONFIG_FEATURES_KEY = "business_logic_device_features";
private static final String DYNAMIC_CONFIG_PROPERTIES_KEY = "business_logic_device_properties";
+ private static final String DYNAMIC_CONFIG_CONDITIONAL_TESTS_ENABLED_KEY =
+ "conditional_business_logic_tests_enabled";
+ /* Format used to append the enabled attribute to the serialized business logic string. */
+ private static final String ENABLED_ATTRIBUTE_SNIPPET = ", \"%s\":%s }";
@Option(name = "business-logic-url", description = "The URL to use when accessing the " +
"business logic service, parameters not included", mandatory = true)
@@ -83,9 +93,10 @@
"suite invocation if retrieval of business logic fails.")
private boolean mIgnoreFailure = false;
- @Option(name = "business-logic-connection-time", description = "Amount of time to attempt " +
- "connection to the business logic service, in seconds.")
- private int mMaxConnectionTime = DEFAULT_CONNECTION_TIME;
+ @Option(name="conditional-business-logic-tests-enabled",
+ description="Setting to true will ensure the device specific tests are executed.")
+ private boolean mConditionalTestsEnabled = false;
+
private String mDeviceFilePushed;
private String mHostFilePushed;
@@ -99,18 +110,11 @@
String requestString = buildRequestString(device, buildInfo);
// Retrieve business logic string from service
String businessLogicString = null;
- long start = System.currentTimeMillis();
- boolean success = false;
- CLog.i("Attempting to connect to business logic service...");
- while (System.currentTimeMillis() < (start + (mMaxConnectionTime * 1000))) {
- try {
- URL request = new URL(requestString);
- businessLogicString = StreamUtil.getStringFromStream(request.openStream());
- success = true;
- break;
- } catch (IOException e) {} // ignore, re-attempt connection with remaining time
- }
- if (!success || businessLogicString == null) {
+ try {
+ URL request = new URL(requestString);
+ businessLogicString = StreamUtil.getStringFromStream(request.openStream());
+ businessLogicString = addRuntimeConfig(businessLogicString, buildInfo);
+ } catch (IOException e) {
if (mIgnoreFailure) {
CLog.e("Failed to connect to business logic service.\nProceeding with test "
+ "invocation, tests depending on the remote configuration will fail.\n");
@@ -152,9 +156,15 @@
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo);
String baseUrl = mUrl.replace(SUITE_PLACEHOLDER, getSuiteName());
MultiMap<String, String> paramMap = new MultiMap<>();
- paramMap.put("key", mApiKey);
paramMap.put("suite_version", buildHelper.getSuiteVersion());
paramMap.put("oem", PropertyUtil.getManufacturer(device));
+ String accessToken = getToken();
+ // Add api key (not authenticated) or Oath token, but not both.
+ if (Strings.isNullOrEmpty(accessToken)) {
+ paramMap.put("key", mApiKey);
+ } else {
+ paramMap.put("access_token", accessToken);
+ }
for (String feature : getBusinessLogicFeatures(device, buildInfo)) {
paramMap.put("features", feature);
}
@@ -209,6 +219,37 @@
}
/**
+ * Append runtime configuration attributes to the end of the Json string.
+ * Determine if conditional tests should execute and add the value to the serialized business
+ * logic settings.
+ */
+ private String addRuntimeConfig(String businessLogicString, IBuildInfo buildInfo) {
+ int indexOfClosingParen = businessLogicString.lastIndexOf("}");
+ // Replace the closing paren with th enabled flag and closing paren. ex
+ // { "a":4 } -> {"a":4, "enabled":true }
+ return businessLogicString.substring(0, indexOfClosingParen) +
+ String.format(ENABLED_ATTRIBUTE_SNIPPET,
+ BusinessLogicFactory.CONDITIONAL_TESTS_ENABLED,
+ shouldExecuteConditionalTests(buildInfo));
+ }
+
+ /**
+ * Execute device specific test if enabled in config or through the command line.
+ * Otherwise skip all conditional tests.
+ */
+ private boolean shouldExecuteConditionalTests(IBuildInfo buildInfo) {
+ boolean enabledInConfig = false;
+ try {
+ String enabledInConfigValue = DynamicConfigFileReader.getValueFromConfig(
+ buildInfo, getSuiteName(), DYNAMIC_CONFIG_CONDITIONAL_TESTS_ENABLED_KEY);
+ enabledInConfig = Boolean.parseBoolean(enabledInConfigValue);
+ } catch (XmlPullParserException | IOException e) {
+ CLog.e("Failed to pull business logic features from dynamic config");
+ }
+ return enabledInConfig || mConditionalTestsEnabled;
+ }
+
+ /**
* {@inheritDoc}
*/
@Override
@@ -229,4 +270,29 @@
private static void removeDeviceFile(ITestDevice device) throws DeviceNotAvailableException {
device.executeShellCommand(String.format("rm -rf %s", BusinessLogic.DEVICE_FILE));
}
+
+ /*
+ * Returns an OAuth2 token string obtained using a service account json key file.
+ *
+ * Uses the service account key file location stored in environment variable 'APE_API_KEY'
+ * to request an OAuth2 token.
+ */
+ private String getToken() {
+ String keyFilePath = System.getenv("APE_API_KEY");
+ if (Strings.isNullOrEmpty(keyFilePath)) {
+ CLog.d("Environment variable APE_API_KEY not set.");
+ return null;
+ }
+ try {
+ Credential credential = GoogleCredential.fromStream(new FileInputStream(keyFilePath))
+ .createScoped(Collections.singleton(APE_API_SCOPE));
+ credential.refreshToken();
+ return credential.getAccessToken();
+ } catch (FileNotFoundException e) {
+ CLog.e(String.format("Service key file %s doesn't exist.", keyFilePath));
+ } catch (IOException e) {
+ CLog.e(String.format("Can't read the service key file, %s", keyFilePath));
+ }
+ return null;
+ }
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
index 3d34f3e..0e23165 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
@@ -15,6 +15,7 @@
*/
package com.android.compatibility.common.tradefed.targetprep;
+import com.android.annotations.VisibleForTesting;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.compatibility.common.util.DynamicConfig;
import com.android.compatibility.common.util.DynamicConfigHandler;
@@ -25,9 +26,11 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.targetprep.BaseTargetPreparer;
import com.android.tradefed.targetprep.BuildError;
import com.android.tradefed.targetprep.ITargetCleaner;
import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.StreamUtil;
import org.json.JSONException;
@@ -36,13 +39,14 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.net.URL;
/**
* Pushes dynamic config files from config repository
*/
@OptionClass(alias="dynamic-config-pusher")
-public class DynamicConfigPusher implements ITargetCleaner {
+public class DynamicConfigPusher extends BaseTargetPreparer implements ITargetCleaner {
public enum TestTarget {
DEVICE,
HOST
@@ -66,6 +70,18 @@
"from the server, e.g. \"1.0\". Defaults to suite version string.")
private String mVersion;
+ // Options for getting the dynamic file from resources.
+ @Option(name = "extract-from-resource",
+ description = "Whether to look for the local dynamic config inside the jar resources "
+ + "or on the local disk.")
+ private boolean mExtractFromResource = false;
+
+ @Option(name = "dynamic-resource-name",
+ description = "When using --extract-from-resource, this option allow to specify the "
+ + "resource name, instead of the module name for the lookup. File will still be "
+ + "logged under the module name.")
+ private String mResourceFileName = null;
+
private String mDeviceFilePushed;
void setModuleName(String moduleName) {
@@ -81,13 +97,7 @@
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(buildInfo);
- File localConfigFile = null;
- try {
- localConfigFile = buildHelper.getTestFile(mModuleName + ".dynamic");
- } catch (FileNotFoundException e) {
- throw new TargetSetupError("Cannot get local dynamic config file from test directory",
- e, device.getDeviceDescriptor());
- }
+ File localConfigFile = getLocalConfigFile(buildHelper, device);
if (mVersion == null) {
mVersion = buildHelper.getSuiteVersion();
@@ -148,4 +158,34 @@
device.executeShellCommand("rm -r " + mDeviceFilePushed);
}
}
+
+ @VisibleForTesting
+ final File getLocalConfigFile(CompatibilityBuildHelper buildHelper, ITestDevice device)
+ throws TargetSetupError {
+ File localConfigFile = null;
+ if (mExtractFromResource) {
+ String lookupName = (mResourceFileName != null) ? mResourceFileName : mModuleName;
+ InputStream dynamicFileRes = getClass().getResourceAsStream(
+ String.format("/%s.dynamic", lookupName));
+ try {
+ localConfigFile = FileUtil.createTempFile(lookupName, ".dynamic");
+ FileUtil.writeToFile(dynamicFileRes, localConfigFile);
+ } catch (IOException e) {
+ FileUtil.deleteFile(localConfigFile);
+ throw new TargetSetupError(
+ String.format("Fail to unpack '%s.dynamic' from resources", lookupName),
+ e, device.getDeviceDescriptor());
+ }
+ return localConfigFile;
+ }
+
+ // If not from resources look at local path.
+ try {
+ localConfigFile = buildHelper.getTestFile(String.format("%s.dynamic", mModuleName));
+ } catch (FileNotFoundException e) {
+ throw new TargetSetupError("Cannot get local dynamic config file from test directory",
+ e, device.getDeviceDescriptor());
+ }
+ return localConfigFile;
+ }
}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicConditionalHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicConditionalHostTestBase.java
new file mode 100644
index 0000000..1bee3a3
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicConditionalHostTestBase.java
@@ -0,0 +1,54 @@
+/*
+ * 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.compatibility.common.tradefed.testtype;
+
+import org.junit.Before;
+
+/**
+ * Host-side base class for tests leveraging the Business Logic service.
+ */
+public class BusinessLogicConditionalHostTestBase extends BusinessLogicHostTestBase {
+
+ @Override
+ @Before
+ public void handleBusinessLogic() {
+ super.loadBusinessLogic();
+ ensureAuthenticated();
+ super.executeBusinessLogic();
+ }
+
+ protected void ensureAuthenticated() {
+ if (!mCanReadBusinessLogic) {
+ // super class handles the condition that the service is unavailable.
+ return;
+ }
+
+ if (!mBusinessLogic.mConditionalTestsEnabled) {
+ skipTest("Execution of device specific tests is not enabled. "
+ + "Enable with '--conditional-business-logic-tests-enabled'");
+ }
+
+ if (mBusinessLogic.isAuthorized()) {
+ // Run test as normal.
+ return;
+ }
+ String message = mBusinessLogic.getAuthenticationStatusMessage();
+
+ // Fail test since request was not authorized.
+ failTest(String.format("Unable to execute because %s.", message));
+ }
+
+}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
index 749be97..ed6e7bc 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/BusinessLogicHostTestBase.java
@@ -30,6 +30,7 @@
import com.android.compatibility.common.util.BusinessLogicHostExecutor;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+import com.android.tradefed.testtype.suite.TestSuiteInfo;
import java.io.File;
@@ -44,23 +45,16 @@
/* Test name rule that tracks the current test method under execution */
@Rule public TestName mTestCase = new TestName();
- private static BusinessLogic mBusinessLogic;
- private static boolean mCanReadBusinessLogic = true;
+ protected BusinessLogic mBusinessLogic;
+ protected boolean mCanReadBusinessLogic = true;
@Before
- public void executeBusinessLogic() {
- // Business logic must be retrieved in this @Before method, since the build info contains
- // the location of the business logic file and cannot be referenced from a static context
- if (mBusinessLogic == null) {
- CompatibilityBuildHelper helper = new CompatibilityBuildHelper(getBuild());
- File businessLogicFile = helper.getBusinessLogicHostFile();
- if (businessLogicFile != null && businessLogicFile.canRead()) {
- mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
- } else {
- mCanReadBusinessLogic = false; // failed to retrieve business logic
- }
- }
+ public void handleBusinessLogic() {
+ loadBusinessLogic();
+ executeBusinessLogic();
+ }
+ protected void executeBusinessLogic() {
String methodName = mTestCase.getMethodName();
assertTrue(String.format("Test \"%s\" is unable to execute as it depends on the missing "
+ "remote configuration.", methodName), mCanReadBusinessLogic);
@@ -77,6 +71,16 @@
}
}
+ protected void loadBusinessLogic() {
+ CompatibilityBuildHelper helper = new CompatibilityBuildHelper(getBuild());
+ File businessLogicFile = helper.getBusinessLogicHostFile();
+ if (businessLogicFile != null && businessLogicFile.canRead()) {
+ mBusinessLogic = BusinessLogicFactory.createFromFile(businessLogicFile);
+ } else {
+ mCanReadBusinessLogic = false; // failed to retrieve business logic
+ }
+ }
+
public static void skipTest(String message) {
assumeTrue(message, false);
}
@@ -85,3 +89,4 @@
fail(message);
}
}
+
diff --git a/common/host-side/tradefed/tests/Android.mk b/common/host-side/tradefed/tests/Android.mk
index c61594a..9d8220b 100644
--- a/common/host-side/tradefed/tests/Android.mk
+++ b/common/host-side/tradefed/tests/Android.mk
@@ -17,8 +17,6 @@
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
-LOCAL_JAVA_RESOURCE_DIRS := ../res
include cts/error_prone_rules.mk
LOCAL_SUITE_BUILD_NUMBER := 2
@@ -28,6 +26,7 @@
LOCAL_SUITE_VERSION := 1
LOCAL_MODULE := compatibility-mock-tradefed
+LOCAL_STATIC_JAVA_LIBRARIES := cts-tradefed-harness
include cts/error_prone_rules.mk
include $(BUILD_COMPATIBILITY_SUITE)
diff --git a/common/host-side/tradefed/tests/res/test-dynamic-config.dynamic b/common/host-side/tradefed/tests/res/test-dynamic-config.dynamic
new file mode 100644
index 0000000..8762861
--- /dev/null
+++ b/common/host-side/tradefed/tests/res/test-dynamic-config.dynamic
@@ -0,0 +1,20 @@
+<!-- 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.
+-->
+
+<dynamicConfig>
+ <entry key="media_files_url">
+ <value>https://dl.google.com/dl/android/cts/android-cts-media-1.4.zip</value>
+ </entry>
+</dynamicConfig>
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index 960d788..7f63f00 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -30,6 +30,7 @@
import com.android.compatibility.common.tradefed.result.ResultReporterBuildInfoTest;
import com.android.compatibility.common.tradefed.result.ResultReporterTest;
import com.android.compatibility.common.tradefed.result.SubPlanHelperTest;
+import com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusherTest;
import com.android.compatibility.common.tradefed.targetprep.MediaPreparerTest;
import com.android.compatibility.common.tradefed.targetprep.PropertyCheckTest;
import com.android.compatibility.common.tradefed.targetprep.SettingsPreparerTest;
@@ -83,6 +84,7 @@
SubPlanHelperTest.class,
// targetprep
+ DynamicConfigPusherTest.class,
MediaPreparerTest.class,
PropertyCheckTest.class,
SettingsPreparerTest.class,
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusherTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusherTest.java
new file mode 100644
index 0000000..a59634b
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusherTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.compatibility.common.tradefed.targetprep;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.targetprep.TargetSetupError;
+import com.android.tradefed.util.FileUtil;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+/**
+ * Unit tests for {@link DynamicConfigPusher}.
+ */
+@RunWith(JUnit4.class)
+public class DynamicConfigPusherTest {
+ private static final String RESOURCE_DYNAMIC_CONFIG = "test-dynamic-config";
+ private DynamicConfigPusher mPreparer;
+ private ITestDevice mMockDevice;
+ private CompatibilityBuildHelper mMockBuildHelper;
+ private IBuildInfo mMockBuildInfo;
+
+ @Before
+ public void setUp() {
+ mPreparer = new DynamicConfigPusher();
+ mMockDevice = EasyMock.createMock(ITestDevice.class);
+ mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
+ mMockBuildHelper = new CompatibilityBuildHelper(mMockBuildInfo);
+ EasyMock.expect(mMockDevice.getDeviceDescriptor()).andStubReturn(null);
+ }
+
+ /**
+ * Test that when we look up resources locally, we search them from the build helper.
+ */
+ @Test
+ public void testLocalRead() throws Exception {
+ OptionSetter setter = new OptionSetter(mPreparer);
+ setter.setOptionValue("config-filename", "config-test-name");
+ setter.setOptionValue("extract-from-resource", "false");
+
+ File check = new File("anyfilewilldo");
+ mMockBuildHelper = new CompatibilityBuildHelper(mMockBuildInfo) {
+ @Override
+ public File getTestFile(String filename) throws FileNotFoundException {
+ return check;
+ }
+ };
+
+ EasyMock.replay(mMockDevice, mMockBuildInfo);
+ File res = mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+ assertEquals(check, res);
+ EasyMock.verify(mMockDevice, mMockBuildInfo);
+ }
+
+ /**
+ * Test that when we look up resources locally, we search them from the build helper and throw
+ * if it's not found.
+ */
+ @Test
+ public void testLocalRead_fileNotFound() throws Exception {
+ OptionSetter setter = new OptionSetter(mPreparer);
+ setter.setOptionValue("config-filename", "config-test-name");
+ setter.setOptionValue("extract-from-resource", "false");
+
+ mMockBuildHelper = new CompatibilityBuildHelper(mMockBuildInfo) {
+ @Override
+ public File getTestFile(String filename) throws FileNotFoundException {
+ throw new FileNotFoundException("test");
+ }
+ };
+ try {
+ EasyMock.replay(mMockDevice, mMockBuildInfo);
+ mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+ fail("Should have thrown an exception.");
+ } catch (TargetSetupError expected) {
+ // expected
+ assertEquals("Cannot get local dynamic config file from test directory null",
+ expected.getMessage());
+ }
+ EasyMock.verify(mMockDevice, mMockBuildInfo);
+ }
+
+ /**
+ * Test when we try to unpack a resource but it does not exists.
+ */
+ @Test
+ public void testResourceRead_notFound() throws Exception {
+ OptionSetter setter = new OptionSetter(mPreparer);
+ setter.setOptionValue("config-filename", "not-an-existing-resource-name");
+ setter.setOptionValue("extract-from-resource", "true");
+ try {
+ EasyMock.replay(mMockDevice, mMockBuildInfo);
+ mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+ fail("Should have thrown an exception.");
+ } catch (TargetSetupError expected) {
+ // expected
+ assertEquals("Fail to unpack 'not-an-existing-resource-name.dynamic' from resources "
+ + "null", expected.getMessage());
+ }
+ EasyMock.verify(mMockDevice, mMockBuildInfo);
+ }
+
+ /**
+ * Test when we get a config from the resources.
+ */
+ @Test
+ public void testResourceRead() throws Exception {
+ OptionSetter setter = new OptionSetter(mPreparer);
+ setter.setOptionValue("config-filename", RESOURCE_DYNAMIC_CONFIG);
+ setter.setOptionValue("extract-from-resource", "true");
+ File res = null;
+ try {
+ EasyMock.replay(mMockDevice, mMockBuildInfo);
+ res = mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+ assertTrue(res.exists());
+ assertTrue(FileUtil.readStringFromFile(res).contains("<dynamicConfig>"));
+ } finally {
+ FileUtil.deleteFile(res);
+ }
+ EasyMock.verify(mMockDevice, mMockBuildInfo);
+ }
+
+ /**
+ * Test when we get a config from the resources under the alternative name.
+ */
+ @Test
+ public void testResourceRead_resourceFileName() throws Exception {
+ OptionSetter setter = new OptionSetter(mPreparer);
+ setter.setOptionValue("config-filename", "moduleName");
+ setter.setOptionValue("extract-from-resource", "true");
+ // Look up the file under that name instead of the config-filename
+ setter.setOptionValue("dynamic-resource-name", RESOURCE_DYNAMIC_CONFIG);
+ File res = null;
+ try {
+ EasyMock.replay(mMockDevice, mMockBuildInfo);
+ res = mPreparer.getLocalConfigFile(mMockBuildHelper, mMockDevice);
+ assertTrue(res.exists());
+ assertTrue(FileUtil.readStringFromFile(res).contains("<dynamicConfig>"));
+ } finally {
+ FileUtil.deleteFile(res);
+ }
+ EasyMock.verify(mMockDevice, mMockBuildInfo);
+ }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogic.java b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
index adab1eb..26fae1e 100644
--- a/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogic.java
@@ -30,6 +30,9 @@
/* A map from testcase name to the business logic rules for the test case */
protected Map<String, List<BusinessLogicRule>> mRules;
+ /* Feature flag determining if device specific tests are executed. */
+ public boolean mConditionalTestsEnabled;
+ private AuthenticationStatusEnum mAuthenticationStatus = AuthenticationStatusEnum.UNKNOWN;
/**
* Determines whether business logic exists for a given test name
@@ -61,6 +64,40 @@
}
}
+ public void setAuthenticationStatus(String authenticationStatus) {
+ try {
+ mAuthenticationStatus = Enum.valueOf(AuthenticationStatusEnum.class,
+ authenticationStatus);
+ } catch (IllegalArgumentException e) {
+ // Invalid value, set to unknown
+ mAuthenticationStatus = AuthenticationStatusEnum.UNKNOWN;
+ }
+ }
+
+ public boolean isAuthorized() {
+ return AuthenticationStatusEnum.AUTHORIZED.equals(mAuthenticationStatus);
+ }
+
+ /**
+ * Builds a user readable string tha explains the authentication status and the effect on tests
+ * which require authentication to execute.
+ */
+ public String getAuthenticationStatusMessage() {
+ switch (mAuthenticationStatus) {
+ case AUTHORIZED:
+ return "Authorized";
+ case NOT_AUTHENTICATED:
+ return "authorization failed, please ensure the service account key is "
+ + "properly installed.";
+ case NOT_AUTHORIZED:
+ return "service account is not authorized to access information for this device. "
+ + "Please verify device properties are set correctly and account "
+ + "permissions are configured in Google's systems.";
+ default:
+ return "something went wrong, please try again.";
+ }
+ }
+
/**
* Nested class representing an Business Logic Rule. Stores a collection of conditions
* and actions for later invokation.
@@ -153,4 +190,15 @@
mMethodArgs.toArray(new String[mMethodArgs.size()]));
}
}
+
+ /**
+ * Nested enum of the possible authentication statuses.
+ */
+ protected enum AuthenticationStatusEnum {
+ UNKNOWN,
+ NOT_AUTHENTICATED,
+ NOT_AUTHORIZED,
+ AUTHORIZED
+ }
+
}
diff --git a/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
index 2d3db01..4a0087e 100644
--- a/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
+++ b/common/util/src/com/android/compatibility/common/util/BusinessLogicFactory.java
@@ -51,6 +51,9 @@
private static final String METHOD_NAME = "methodName";
// Name of method args array of strings
private static final String METHOD_ARGS = "methodArgs";
+ // Name of the field in the response object that stores that the auth status of the request.
+ private static final String AUTHENTICATION_STATUS = "authenticationStatus";
+ public static final String CONDITIONAL_TESTS_ENABLED = "conditionalTestsEnabled";
/**
* Create a BusinessLogic instance from a file of business logic data, formatted in JSON.
@@ -65,6 +68,14 @@
String businessLogicString = readFile(f);
JSONObject root = new JSONObject(businessLogicString);
JSONArray rulesLists = null;
+ if (root.has(AUTHENTICATION_STATUS)){
+ String authStatus = root.getString(AUTHENTICATION_STATUS);
+ bl.setAuthenticationStatus(authStatus);
+ }
+ if (root.has(CONDITIONAL_TESTS_ENABLED)){
+ boolean enabled = root.getBoolean(CONDITIONAL_TESTS_ENABLED);
+ bl.mConditionalTestsEnabled = enabled;
+ }
try {
rulesLists = root.getJSONArray(BUSINESS_LOGIC_RULES_LISTS);
} catch (JSONException e) {
diff --git a/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java b/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java
deleted file mode 100644
index f6f9d10..0000000
--- a/hostsidetests/content/src/android/content/cts/SyncAdapterAccountAccessHostTest.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * 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.
- */
-
-package android.content.cts;
-
-import android.appsecurity.cts.Utils;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-
-/**
- * Set of tests that verify behavior of the content framework.
- */
-public class SyncAdapterAccountAccessHostTest extends DeviceTestCase
- implements IAbiReceiver, IBuildReceiver {
- private static final String ACCOUNT_ACCESS_TESTS_OTHER_CERT_APK =
- "CtsSyncAccountAccessOtherCertTestCases.apk";
- private static final String ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG =
- "com.android.cts.content";
-
- private static final String ACCOUNT_ACCESS_TESTS_SAME_CERT_APK =
- "CtsSyncAccountAccessSameCertTestCases.apk";
- private static final String ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG =
- "com.android.cts.content";
-
- private static final String STUBS_APK =
- "CtsSyncAccountAccessStubs.apk";
- private static final String STUBS_PKG =
- "com.android.cts.stub";
-
- private IAbi mAbi;
- private CompatibilityBuildHelper mBuildHelper;
-
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mBuildHelper = new CompatibilityBuildHelper(buildInfo);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- getDevice().uninstallPackage(STUBS_PKG);
-
- assertNull(getDevice().installPackage(mBuildHelper
- .getTestFile(STUBS_APK), false, false));
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- getDevice().uninstallPackage(STUBS_PKG);
- }
-
- public void testSameCertAuthenticatorCanSeeAccount() throws Exception {
- getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG);
-
- assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
- ACCOUNT_ACCESS_TESTS_SAME_CERT_APK), false, false));
- try {
- runDeviceTests(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG,
- "com.android.cts.content.CtsSyncAccountAccessSameCertTestCases",
- "testAccountAccess_sameCertAsAuthenticatorCanSeeAccount");
- } finally {
- getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_SAME_CERT_PKG);
- }
- }
-
- public void testOtherCertAuthenticatorCanSeeAccount() throws Exception {
- getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG);
-
- assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
- ACCOUNT_ACCESS_TESTS_OTHER_CERT_APK), false, false));
- try {
- runDeviceTests(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG,
- "com.android.cts.content.CtsSyncAccountAccessOtherCertTestCases",
- "testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount");
- } finally {
- getDevice().uninstallPackage(ACCOUNT_ACCESS_TESTS_OTHER_CERT_PKG);
- }
- }
-
- private void runDeviceTests(String packageName, String testClassName, String testMethodName)
- throws DeviceNotAvailableException {
- Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
- }
-}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk b/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk
deleted file mode 100644
index 116da94..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/Android.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# 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.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
- ctstestrunner \
- ub-uiautomator \
- compatibility-device-util
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
- ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java \
- ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java \
- ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java \
- ../CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
-
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessOtherCertTestCases
-
-LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
deleted file mode 100644
index 2ecd27d..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/AndroidManifest.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.content">
-
- <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
- <application>
- <uses-library android:name="android.test.runner" />
-
- <activity android:name=".StubActivity"/>
-
- <service android:name=".SyncService">
- <intent-filter>
- <action android:name="android.content.SyncAdapter"/>
- </intent-filter>
- <meta-data android:name="android.content.SyncAdapter"
- android:resource="@xml/syncadapter" />
- </service>
-
- </application>
-
- <instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.content" />
-
-</manifest>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml b/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml
deleted file mode 100644
index f55a19a..0000000
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/res/xml/syncadapter.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?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.
--->
-
-<sync-adapter
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:contentAuthority="com.android.cts.stub.provider"
- android:accountType="com.stub"
- android:userVisible="false"
- android:supportsUploading="false"
- android:allowParallelSyncs="false"
- android:isAlwaysSyncable="true">
-</sync-adapter>
diff --git a/hostsidetests/devicepolicy/Android.mk b/hostsidetests/devicepolicy/Android.mk
index 9cbd49e..95af5e7 100644
--- a/hostsidetests/devicepolicy/Android.mk
+++ b/hostsidetests/devicepolicy/Android.mk
@@ -27,7 +27,7 @@
LOCAL_CTS_TEST_PACKAGE := android.adminhostside
# tag this module as a cts test artifact
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_COMPATIBILITY_SUITE := cts arcts vts general-tests
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index e0ea522..f170f81 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -39,7 +39,6 @@
compatibility-device-util \
android-support-v4 \
android-support-test \
- bouncycastle \
cts-security-test-support-library
LOCAL_SDK_VERSION := test_current
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
index 0910b70..4863192 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
@@ -18,7 +18,7 @@
import java.lang.Character;
-public class LockScreenInfoTest extends BaseAffiliatedProfileOwnerTest {
+public class LockScreenInfoTest extends BaseDeviceOwnerTest {
@Override
public void tearDown() throws Exception {
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
index 0d22c38..868ae3b 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferIncomingTest.java
@@ -15,28 +15,59 @@
*/
package com.android.cts.transferowner;
+import static junit.framework.Assert.assertTrue;
+
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.PersistableBundle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import org.junit.Before;
+import org.junit.Test;
@SmallTest
public class DeviceAndProfileOwnerTransferIncomingTest {
public static class BasicAdminReceiver extends DeviceAdminReceiver {
public BasicAdminReceiver() {}
+
+ @Override
+ public void onTransferOwnershipComplete(Context context, PersistableBundle bundle) {
+ putBooleanPref(context, KEY_TRANSFER_COMPLETED_CALLED, true);
+ }
}
+ private final static String SHARED_PREFERENCE_NAME = "shared-preference-name";
+ private final static String KEY_TRANSFER_COMPLETED_CALLED = "key-transfer-completed-called";
+
+ protected Context mContext;
protected ComponentName mIncomingComponentName;
protected DevicePolicyManager mDevicePolicyManager;
@Before
public void setUp() throws Exception {
- Context context = InstrumentationRegistry.getTargetContext();
- mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
- mIncomingComponentName = new ComponentName(context, BasicAdminReceiver.class.getName());
+ mContext = InstrumentationRegistry.getTargetContext();
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+ mIncomingComponentName = new ComponentName(mContext, BasicAdminReceiver.class.getName());
+ }
+
+ @Test
+ public void testTransferCompleteCallbackIsCalled() {
+ assertTrue(getBooleanPref(mContext, KEY_TRANSFER_COMPLETED_CALLED));
+ }
+
+ private static SharedPreferences getPrefs(Context context) {
+ return context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE);
+ }
+
+ private static void putBooleanPref(Context context, String key, boolean value) {
+ getPrefs(context).edit().putBoolean(key, value).apply();
+ }
+
+ private static boolean getBooleanPref(Context context, String key) {
+ return getPrefs(context).getBoolean(key, false);
}
}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
index 2b20c59..b33da04 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerIncomingApp/src/com/android/cts/transferowner/TransferDeviceOwnerIncomingTest.java
@@ -37,16 +37,16 @@
assertEquals(Collections.singletonList("test.package"),
mDevicePolicyManager.getKeepUninstalledPackages(mIncomingComponentName));
assertEquals(123, mDevicePolicyManager.getPasswordMinimumLength(mIncomingComponentName));
- assertTrue(areSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
- mDevicePolicyManager.getSystemUpdatePolicy()));
+ assertSystemPoliciesEqual(SystemUpdatePolicy.createWindowedInstallPolicy(123, 456),
+ mDevicePolicyManager.getSystemUpdatePolicy());
assertThrows(SecurityException.class, () -> {
mDevicePolicyManager.getParentProfileInstance(mIncomingComponentName);
});
}
- private boolean areSystemPoliciesEqual(SystemUpdatePolicy policy1, SystemUpdatePolicy policy2) {
- return policy1.getPolicyType() == policy2.getPolicyType()
+ private void assertSystemPoliciesEqual(SystemUpdatePolicy policy1, SystemUpdatePolicy policy2) {
+ assertTrue(policy1.getPolicyType() == policy2.getPolicyType()
&& policy1.getInstallWindowStart() == policy2.getInstallWindowStart()
- && policy1.getInstallWindowEnd() == policy2.getInstallWindowEnd();
+ && policy1.getInstallWindowEnd() == policy2.getInstallWindowEnd());
}
}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
index 888ca49..328a7ae 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/DeviceAndProfileOwnerTransferOutgoingTest.java
@@ -15,15 +15,20 @@
*/
package com.android.cts.transferowner;
+import static junit.framework.Assert.assertNotNull;
+
import static org.testng.Assert.assertThrows;
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.os.PersistableBundle;
import android.support.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
+
import org.junit.Before;
import org.junit.Test;
@@ -38,20 +43,26 @@
"com.android.cts.transferownerincoming";
private static final String TRANSFER_OWNER_INCOMING_TEST_RECEIVER_CLASS =
"com.android.cts.transferowner.DeviceAndProfileOwnerTransferIncomingTest$BasicAdminReceiver";
- static final ComponentName mIncomingComponentName =
+ static final ComponentName INCOMING_COMPONENT_NAME =
new ComponentName(
TRANSFER_OWNER_INCOMING_PKG, TRANSFER_OWNER_INCOMING_TEST_RECEIVER_CLASS);
- private static final ComponentName mInvalidTargetComponent =
+ private static final ComponentName INVALID_TARGET_COMPONENT =
new ComponentName("com.android.cts.intent.receiver", ".BroadcastIntentReceiver");
protected DevicePolicyManager mDevicePolicyManager;
protected ComponentName mOutgoingComponentName;
+ protected Context mContext;
+ private String mOwnerChangedBroadcastAction;
@Before
public void setUp() throws Exception {
- Context context = InstrumentationRegistry.getTargetContext();
- mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
- mOutgoingComponentName = new ComponentName(context, BasicAdminReceiver.class.getName());
+ mContext = InstrumentationRegistry.getTargetContext();
+ mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
+ mOutgoingComponentName = new ComponentName(mContext, BasicAdminReceiver.class.getName());
+ }
+
+ protected final void setupTestParameters(String ownerChangedBroadcastAction) {
+ mOwnerChangedBroadcastAction = ownerChangedBroadcastAction;
}
@Test
@@ -60,7 +71,7 @@
assertThrows(
IllegalArgumentException.class,
() -> {
- transferOwner(
+ transferOwnership(
mOutgoingComponentName, mOutgoingComponentName, b);
});
}
@@ -71,20 +82,40 @@
assertThrows(
IllegalArgumentException.class,
() -> {
- transferOwner(
- mOutgoingComponentName, mInvalidTargetComponent, b);
+ transferOwnership(mOutgoingComponentName, INVALID_TARGET_COMPONENT, b);
});
}
- protected void transferOwner(ComponentName outgoing, ComponentName incoming,
+ protected void transferOwnership(ComponentName outgoing, ComponentName incoming,
PersistableBundle parameters)
throws Throwable {
try {
- mDevicePolicyManager.getClass().getMethod("transferOwner",
+ mDevicePolicyManager.getClass().getMethod("transferOwnership",
ComponentName.class, ComponentName.class, PersistableBundle.class)
.invoke(mDevicePolicyManager, outgoing, incoming, parameters);
} catch (InvocationTargetException e) {
throw e.getTargetException();
}
}
+
+ @Test
+ public void testTransferOwnerChangedBroadcast() throws Throwable {
+ BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(mContext,
+ mOwnerChangedBroadcastAction);
+ try {
+ receiver.register();
+ PersistableBundle b = new PersistableBundle();
+ transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+ Intent intent = receiver.awaitForBroadcast();
+ assertNotNull(intent);
+ } finally {
+ receiver.unregisterQuietly();
+ }
+ }
+
+ @Test
+ public void testTransferOwner() throws Throwable {
+ PersistableBundle b = new PersistableBundle();
+ transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
index 67c3760..3a1709f 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferDeviceOwnerOutgoingTest.java
@@ -20,6 +20,7 @@
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertThrows;
+import android.app.admin.DevicePolicyManager;
import android.app.admin.SystemUpdatePolicy;
import android.os.PersistableBundle;
import android.support.test.filters.SmallTest;
@@ -30,6 +31,13 @@
@SmallTest
public class TransferDeviceOwnerOutgoingTest extends DeviceAndProfileOwnerTransferOutgoingTest {
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setupTestParameters(DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED);
+ }
+
@Test
public void testTransferWithPoliciesOutgoing() throws Throwable {
int passwordLength = 123;
@@ -41,15 +49,15 @@
SystemUpdatePolicy.createWindowedInstallPolicy(123, 456));
PersistableBundle b = new PersistableBundle();
- transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+ transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
}
@Test
public void testTransfer() throws Throwable {
PersistableBundle b = new PersistableBundle();
- transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
- assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
- assertTrue(mDevicePolicyManager.isDeviceOwnerApp(mIncomingComponentName.getPackageName()));
+ transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+ assertTrue(mDevicePolicyManager.isAdminActive(INCOMING_COMPONENT_NAME));
+ assertTrue(mDevicePolicyManager.isDeviceOwnerApp(INCOMING_COMPONENT_NAME.getPackageName()));
assertFalse(
mDevicePolicyManager.isDeviceOwnerApp(mOutgoingComponentName.getPackageName()));
assertFalse(mDevicePolicyManager.isAdminActive(mOutgoingComponentName));
diff --git a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
index 370539d..cf760c6 100644
--- a/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
+++ b/hostsidetests/devicepolicy/app/TransferOwnerOutgoingApp/src/com/android/cts/transferowner/TransferProfileOwnerOutgoingTest.java
@@ -28,6 +28,13 @@
@SmallTest
public class TransferProfileOwnerOutgoingTest extends DeviceAndProfileOwnerTransferOutgoingTest {
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ setupTestParameters(DevicePolicyManager.ACTION_PROFILE_OWNER_CHANGED);
+ }
+
@Test
public void testTransferWithPoliciesOutgoing() throws Throwable {
int passwordLength = 123;
@@ -41,15 +48,15 @@
mOutgoingComponentName, passwordExpirationTimeout);
PersistableBundle b = new PersistableBundle();
- transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
+ transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
}
@Test
public void testTransfer() throws Throwable {
PersistableBundle b = new PersistableBundle();
- transferOwner(mOutgoingComponentName, mIncomingComponentName, b);
- assertTrue(mDevicePolicyManager.isAdminActive(mIncomingComponentName));
- assertTrue(mDevicePolicyManager.isProfileOwnerApp(mIncomingComponentName.getPackageName()));
+ transferOwnership(mOutgoingComponentName, INCOMING_COMPONENT_NAME, b);
+ assertTrue(mDevicePolicyManager.isAdminActive(INCOMING_COMPONENT_NAME));
+ assertTrue(mDevicePolicyManager.isProfileOwnerApp(INCOMING_COMPONENT_NAME.getPackageName()));
assertFalse(
mDevicePolicyManager.isProfileOwnerApp(mOutgoingComponentName.getPackageName()));
assertFalse(mDevicePolicyManager.isAdminActive(mOutgoingComponentName));
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
similarity index 75%
rename from hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java
rename to hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
index b9cb16b..2456eb1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerHostSideTransferTest.java
@@ -1,6 +1,6 @@
package com.android.cts.devicepolicy;
-public class DeviceAndProfileOwnerTransferTest extends BaseDevicePolicyTest {
+public abstract class DeviceAndProfileOwnerHostSideTransferTest extends BaseDevicePolicyTest {
protected static final String TRANSFER_OWNER_OUTGOING_PKG =
"com.android.cts.transferowneroutgoing";
protected static final String TRANSFER_OWNER_OUTGOING_APK = "CtsTransferOwnerOutgoingApp.apk";
@@ -21,7 +21,6 @@
if (!mHasFeature) {
return;
}
- installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
mOutgoingTestClassName,
"testTransfer", mUserId);
@@ -31,7 +30,6 @@
if (!mHasFeature) {
return;
}
- installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
mOutgoingTestClassName,
"testTransferSameAdmin", mUserId);
@@ -51,7 +49,6 @@
if (!mHasFeature) {
return;
}
- installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
mOutgoingTestClassName,
"testTransferWithPoliciesOutgoing", mUserId);
@@ -60,6 +57,30 @@
"testTransferPoliciesAreRetainedAfterTransfer", mUserId);
}
+ public void testTransferOwnerChangedBroadcast() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+ mOutgoingTestClassName,
+ "testTransferOwnerChangedBroadcast", mUserId);
+ }
+
+ public void testTransferCompleteCallback() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ runDeviceTestsAsUser(TRANSFER_OWNER_OUTGOING_PKG,
+ mOutgoingTestClassName,
+ "testTransferOwner", mUserId);
+
+ waitForBroadcastIdle();
+
+ runDeviceTestsAsUser(TRANSFER_OWNER_INCOMING_PKG,
+ mIncomingTestClassName,
+ "testTransferCompleteCallbackIsCalled", mUserId);
+ }
+
protected void setupTestParameters(int userId, String outgoingTestClassName,
String incomingTestClassName) {
mUserId = userId;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index ed9b51d..e37c494 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -122,14 +122,6 @@
executeDeviceOwnerTest("LockScreenInfoTest");
}
- public void testLockScreenInfo_affiliatedSecondaryUser() throws Exception {
- if (!mHasFeature || !canCreateAdditionalUsers(1)) {
- return;
- }
- final int userId = createAffiliatedSecondaryUser();
- executeAffiliatedProfileOwnerTest("LockScreenInfoTest", userId);
- }
-
public void testWifi() throws Exception {
if (!hasDeviceFeature("android.hardware.wifi")) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
similarity index 67%
rename from hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java
rename to hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
index 6fbc087..1521282 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerHostSideTransferTest.java
@@ -22,7 +22,8 @@
* process, first we setup some policies in the client side in CtsTransferOwnerOutgoingApp and then
* we verify the policies are still there in CtsTransferOwnerIncomingApp.
*/
-public class MixedDeviceOwnerTransferTest extends DeviceAndProfileOwnerTransferTest {
+public class MixedDeviceOwnerHostSideTransferTest extends
+ DeviceAndProfileOwnerHostSideTransferTest {
private static final String TRANSFER_DEVICE_OWNER_OUTGOING_TEST =
"com.android.cts.transferowner.TransferDeviceOwnerOutgoingTest";
private static final String TRANSFER_DEVICE_OWNER_INCOMING_TEST =
@@ -32,15 +33,17 @@
protected void setUp() throws Exception {
super.setUp();
if (mHasFeature) {
- setupDeviceOwner(TRANSFER_OWNER_OUTGOING_APK,
- TRANSFER_OWNER_OUTGOING_TEST_RECEIVER);
- setupTestParameters(mPrimaryUserId, TRANSFER_DEVICE_OWNER_OUTGOING_TEST,
- TRANSFER_DEVICE_OWNER_INCOMING_TEST);
+ installAppAsUser(TRANSFER_OWNER_OUTGOING_APK, mPrimaryUserId);
+ if (setDeviceOwner(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, mPrimaryUserId,
+ false)) {
+ setupTestParameters(mPrimaryUserId, TRANSFER_DEVICE_OWNER_OUTGOING_TEST,
+ TRANSFER_DEVICE_OWNER_INCOMING_TEST);
+ installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
+ } else {
+ removeAdmin(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, mUserId);
+ getDevice().uninstallPackage(TRANSFER_OWNER_OUTGOING_PKG);
+ fail("Failed to set device owner");
+ }
}
}
-
- private void setupDeviceOwner(String apkName, String adminReceiverClassName) throws Exception {
- installAppAsUser(apkName, mPrimaryUserId);
- setDeviceOwnerOrFail(adminReceiverClassName, mPrimaryUserId);
- }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
similarity index 75%
rename from hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java
rename to hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
index 3167729..f5a25d0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTransferTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerHostSideTransferTest.java
@@ -22,7 +22,8 @@
* process, first we setup some policies in the client side in CtsTransferOwnerOutgoingApp and then
* we verify the policies are still there in CtsTransferOwnerIncomingApp.
*/
-public class MixedProfileOwnerTransferTest extends DeviceAndProfileOwnerTransferTest {
+public class MixedProfileOwnerHostSideTransferTest extends
+ DeviceAndProfileOwnerHostSideTransferTest {
private static final String TRANSFER_PROFILE_OWNER_OUTGOING_TEST =
"com.android.cts.transferowner.TransferProfileOwnerOutgoingTest";
private static final String TRANSFER_PROFILE_OWNER_INCOMING_TEST =
@@ -36,16 +37,25 @@
if (mHasFeature) {
int profileOwnerUserId = setupManagedProfile(TRANSFER_OWNER_OUTGOING_APK,
TRANSFER_OWNER_OUTGOING_TEST_RECEIVER);
- setupTestParameters(profileOwnerUserId, TRANSFER_PROFILE_OWNER_OUTGOING_TEST,
- TRANSFER_PROFILE_OWNER_INCOMING_TEST);
+ if (profileOwnerUserId != -1) {
+ setupTestParameters(profileOwnerUserId, TRANSFER_PROFILE_OWNER_OUTGOING_TEST,
+ TRANSFER_PROFILE_OWNER_INCOMING_TEST);
+ installAppAsUser(TRANSFER_OWNER_INCOMING_APK, mUserId);
+ }
}
}
private int setupManagedProfile(String apkName, String adminReceiverClassName)
throws Exception {
final int userId = createManagedProfile(mPrimaryUserId);
+
installAppAsUser(apkName, userId);
- setProfileOwnerOrFail(adminReceiverClassName, userId);
+ if (!setProfileOwner(adminReceiverClassName, userId, false)) {
+ removeAdmin(TRANSFER_OWNER_OUTGOING_TEST_RECEIVER, userId);
+ getDevice().uninstallPackage(TRANSFER_OWNER_OUTGOING_PKG);
+ fail("Failed to set device owner");
+ return -1;
+ }
startUser(userId);
return userId;
}
diff --git a/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
new file mode 100644
index 0000000..fa23d9a
--- /dev/null
+++ b/hostsidetests/incident/src/com/android/server/cts/JobSchedulerIncidentTest.java
@@ -0,0 +1,317 @@
+/*
+ * 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.app.JobParametersProto;
+import android.net.NetworkCapabilitiesProto;
+import android.net.NetworkRequestProto;
+import com.android.server.job.ConstantsProto;
+import com.android.server.job.DataSetProto;
+import com.android.server.job.JobPackageHistoryProto;
+import com.android.server.job.JobPackageTrackerDumpProto;
+import com.android.server.job.JobSchedulerServiceDumpProto;
+import com.android.server.job.JobStatusDumpProto;
+import com.android.server.job.JobStatusShortInfoProto;
+import com.android.server.job.StateControllerProto;
+
+/** Test to check that the jobscheduler service properly outputs its dump state. */
+public class JobSchedulerIncidentTest extends ProtoDumpTestCase {
+ public void testJobSchedulerServiceDump() throws Exception {
+ final JobSchedulerServiceDumpProto dump =
+ getDump(JobSchedulerServiceDumpProto.parser(), "dumpsys jobscheduler --proto");
+
+ testConstantsProto(dump.getSettings());
+
+ for (int u : dump.getStartedUsersList()) {
+ assertTrue(0 <= u);
+ }
+
+ for (JobSchedulerServiceDumpProto.RegisteredJob rj : dump.getRegisteredJobsList()) {
+ testJobStatusShortInfoProto(rj.getInfo());
+ testJobStatusDumpProto(rj.getDump());
+ }
+
+ for (StateControllerProto c : dump.getControllersList()) {
+ testStateControllerProto(c);
+ }
+
+ for (JobSchedulerServiceDumpProto.PriorityOverride po : dump.getPriorityOverridesList()) {
+ assertTrue(0 <= po.getUid());
+ }
+
+ for (int buu : dump.getBackingUpUidsList()) {
+ assertTrue(0 <= buu);
+ }
+
+ testJobPackageHistoryProto(dump.getHistory());
+
+ testJobPackageTrackerDumpProto(dump.getPackageTracker());
+
+ for (JobSchedulerServiceDumpProto.PendingJob pj : dump.getPendingJobsList()) {
+ testJobStatusShortInfoProto(pj.getInfo());
+ testJobStatusDumpProto(pj.getDump());
+ assertTrue(0 <= pj.getEnqueuedDurationMs());
+ }
+
+ for (JobSchedulerServiceDumpProto.ActiveJob aj : dump.getActiveJobsList()) {
+ JobSchedulerServiceDumpProto.ActiveJob.InactiveJob ajIj = aj.getInactive();
+ assertTrue(0 <= ajIj.getTimeSinceStoppedMs());
+
+ JobSchedulerServiceDumpProto.ActiveJob.RunningJob ajRj = aj.getRunning();
+ testJobStatusShortInfoProto(ajRj.getInfo());
+ assertTrue(0 <= ajRj.getRunningDurationMs());
+ assertTrue(0 <= ajRj.getTimeUntilTimeoutMs());
+ testJobStatusDumpProto(ajRj.getDump());
+ assertTrue(0 <= ajRj.getTimeSinceMadeActiveMs());
+ assertTrue(0 <= ajRj.getPendingDurationMs());
+ }
+
+ assertTrue(0 <= dump.getMaxActiveJobs());
+ }
+
+ private void testConstantsProto(ConstantsProto c) throws Exception {
+ assertNotNull(c);
+
+ assertTrue(0 <= c.getMinIdleCount());
+ assertTrue(0 <= c.getMinChargingCount());
+ assertTrue(0 <= c.getMinBatteryNotLowCount());
+ assertTrue(0 <= c.getMinStorageNotLowCount());
+ assertTrue(0 <= c.getMinConnectivityCount());
+ assertTrue(0 <= c.getMinContentCount());
+ assertTrue(0 <= c.getMinReadyJobsCount());
+ assertTrue(0 <= c.getHeavyUseFactor());
+ assertTrue(0 <= c.getModerateUseFactor());
+ assertTrue(0 <= c.getFgJobCount());
+ assertTrue(0 <= c.getBgNormalJobCount());
+ assertTrue(0 <= c.getBgModerateJobCount());
+ assertTrue(0 <= c.getBgLowJobCount());
+ assertTrue(0 <= c.getBgCriticalJobCount());
+ assertTrue(0 <= c.getMaxStandardRescheduleCount());
+ assertTrue(0 <= c.getMaxWorkRescheduleCount());
+ assertTrue(0 <= c.getMinLinearBackoffTimeMs());
+ assertTrue(0 <= c.getMinExpBackoffTimeMs());
+ assertTrue(0 <= c.getStandbyHeartbeatTimeMs());
+ for (int sb : c.getStandbyBeatsList()) {
+ assertTrue(0 <= sb);
+ }
+ }
+
+ private void testDataSetProto(DataSetProto ds) throws Exception {
+ assertNotNull(ds);
+
+ assertTrue(0 <= ds.getStartClockTimeMs());
+ assertTrue(0 <= ds.getElapsedTimeMs());
+ assertTrue(0 <= ds.getPeriodMs());
+
+ for (DataSetProto.PackageEntryProto pe : ds.getPackageEntriesList()) {
+ assertTrue(0 <= pe.getUid());
+
+ assertTrue(0 <= pe.getPendingState().getDurationMs());
+ assertTrue(0 <= pe.getPendingState().getCount());
+ assertTrue(0 <= pe.getActiveState().getDurationMs());
+ assertTrue(0 <= pe.getActiveState().getCount());
+ assertTrue(0 <= pe.getActiveTopState().getDurationMs());
+ assertTrue(0 <= pe.getActiveTopState().getCount());
+
+ for (DataSetProto.PackageEntryProto.StopReasonCount src : pe.getStopReasonsList()) {
+ assertTrue(JobParametersProto.CancelReason.getDescriptor().getValues()
+ .contains(src.getReason().getValueDescriptor()));
+ assertTrue(0 <= src.getCount());
+ }
+ }
+ assertTrue(0 <= ds.getMaxConcurrency());
+ assertTrue(0 <= ds.getMaxForegroundConcurrency());
+ }
+
+ private void testJobPackageHistoryProto(JobPackageHistoryProto jph) throws Exception {
+ assertNotNull(jph);
+
+ for (JobPackageHistoryProto.HistoryEvent he : jph.getHistoryEventList()) {
+ assertTrue(JobPackageHistoryProto.Event.getDescriptor().getValues()
+ .contains(he.getEvent().getValueDescriptor()));
+ assertTrue(0 <= he.getTimeSinceEventMs()); // Should be positive.
+ assertTrue(0 <= he.getUid());
+ assertTrue(JobParametersProto.CancelReason.getDescriptor().getValues()
+ .contains(he.getStopReason().getValueDescriptor()));
+ }
+ }
+
+ private void testJobPackageTrackerDumpProto(JobPackageTrackerDumpProto jptd) throws Exception {
+ assertNotNull(jptd);
+
+ for (DataSetProto ds : jptd.getHistoricalStatsList()) {
+ testDataSetProto(ds);
+ }
+ testDataSetProto(jptd.getCurrentStats());
+ }
+
+ private void testJobStatusShortInfoProto(JobStatusShortInfoProto jssi) throws Exception {
+ assertNotNull(jssi);
+
+ assertTrue(0 <= jssi.getCallingUid());
+ }
+
+ private void testJobStatusDumpProto(JobStatusDumpProto jsd) throws Exception {
+ assertNotNull(jsd);
+
+ assertTrue(0 <= jsd.getCallingUid());
+ assertTrue(0 <= jsd.getSourceUid());
+ assertTrue(0 <= jsd.getSourceUserId());
+
+ JobStatusDumpProto.JobInfo ji = jsd.getJobInfo();
+ if (ji.getIsPeriodic()) {
+ assertTrue(0 <= ji.getPeriodIntervalMs());
+ assertTrue(0 <= ji.getPeriodFlexMs());
+ }
+ assertTrue(0 <= ji.getTriggerContentUpdateDelayMs());
+ assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
+ testNetworkRequestProto(ji.getRequiredNetwork());
+ assertTrue(0 <= ji.getTotalNetworkBytes());
+ assertTrue(0 <= ji.getMinLatencyMs());
+ assertTrue(0 <= ji.getMaxExecutionDelayMs());
+ JobStatusDumpProto.JobInfo.Backoff bp = ji.getBackoffPolicy();
+ assertTrue(JobStatusDumpProto.JobInfo.Backoff.Policy.getDescriptor().getValues()
+ .contains(bp.getPolicy().getValueDescriptor()));
+ assertTrue(0 <= bp.getInitialBackoffMs());
+
+ for (JobStatusDumpProto.Constraint c : jsd.getRequiredConstraintsList()) {
+ assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+ .contains(c.getValueDescriptor()));
+ }
+ for (JobStatusDumpProto.Constraint c : jsd.getSatisfiedConstraintsList()) {
+ assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+ .contains(c.getValueDescriptor()));
+ }
+ for (JobStatusDumpProto.Constraint c : jsd.getUnsatisfiedConstraintsList()) {
+ assertTrue(JobStatusDumpProto.Constraint.getDescriptor().getValues()
+ .contains(c.getValueDescriptor()));
+ }
+
+ for (JobStatusDumpProto.TrackingController tc : jsd.getTrackingControllersList()) {
+ assertTrue(JobStatusDumpProto.TrackingController.getDescriptor().getValues()
+ .contains(tc.getValueDescriptor()));
+ }
+
+ for (JobStatusDumpProto.JobWorkItem jwi : jsd.getPendingWorkList()) {
+ assertTrue(0 <= jwi.getDeliveryCount());
+ }
+ for (JobStatusDumpProto.JobWorkItem jwi : jsd.getExecutingWorkList()) {
+ assertTrue(0 <= jwi.getDeliveryCount());
+ }
+
+ assertTrue(JobStatusDumpProto.Bucket.getDescriptor().getValues()
+ .contains(jsd.getStandbyBucket().getValueDescriptor()));
+
+ assertTrue(0 <= jsd.getEnqueueDurationMs());
+
+ assertTrue(0 <= jsd.getNumFailures());
+
+ assertTrue(0 <= jsd.getLastSuccessfulRunTime());
+ assertTrue(0 <= jsd.getLastFailedRunTime());
+ }
+
+ private void testNetworkRequestProto(NetworkRequestProto nr) throws Exception {
+ assertNotNull(nr);
+
+ assertTrue(NetworkRequestProto.Type.getDescriptor().getValues()
+ .contains(nr.getType().getValueDescriptor()));
+ testNetworkCapabilitesProto(nr.getNetworkCapabilities());
+ }
+
+ private void testNetworkCapabilitesProto(NetworkCapabilitiesProto nc) throws Exception {
+ assertNotNull(nc);
+
+ for (NetworkCapabilitiesProto.Transport t : nc.getTransportsList()) {
+ assertTrue(NetworkCapabilitiesProto.Transport.getDescriptor().getValues()
+ .contains(t.getValueDescriptor()));
+ }
+ for (NetworkCapabilitiesProto.NetCapability c : nc.getCapabilitiesList()) {
+ assertTrue(NetworkCapabilitiesProto.NetCapability.getDescriptor().getValues()
+ .contains(c.getValueDescriptor()));
+ }
+
+ assertTrue(0 <= nc.getLinkUpBandwidthKbps());
+ assertTrue(0 <= nc.getLinkDownBandwidthKbps());
+ }
+
+ private void testStateControllerProto(StateControllerProto sc) throws Exception {
+ assertNotNull(sc);
+
+ StateControllerProto.AppIdleController aic = sc.getAppIdle();
+ for (StateControllerProto.AppIdleController.TrackedJob tj : aic.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ StateControllerProto.BackgroundJobsController bjc = sc.getBackground();
+ for (StateControllerProto.BackgroundJobsController.TrackedJob tj : bjc.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ StateControllerProto.BatteryController bc = sc.getBattery();
+ for (StateControllerProto.BatteryController.TrackedJob tj : bc.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ StateControllerProto.ConnectivityController cc = sc.getConnectivity();
+ for (StateControllerProto.ConnectivityController.TrackedJob tj : cc.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ testNetworkRequestProto(tj.getRequiredNetwork());
+ }
+ StateControllerProto.ContentObserverController coc = sc.getContentObserver();
+ for (StateControllerProto.ContentObserverController.TrackedJob tj : coc.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ for (StateControllerProto.ContentObserverController.Observer o : coc.getObserversList()) {
+ assertTrue(0 <= o.getUserId());
+
+ for (StateControllerProto.ContentObserverController.Observer.TriggerContentData tcd : o.getTriggersList()) {
+ for (StateControllerProto.ContentObserverController.Observer.TriggerContentData.JobInstance ji : tcd.getJobsList()) {
+ testJobStatusShortInfoProto(ji.getInfo());
+
+ assertTrue(0 <= ji.getSourceUid());
+ assertTrue(0 <= ji.getTriggerContentUpdateDelayMs());
+ assertTrue(0 <= ji.getTriggerContentMaxDelayMs());
+ }
+ }
+ }
+ StateControllerProto.DeviceIdleJobsController dijc = sc.getDeviceIdle();
+ for (StateControllerProto.DeviceIdleJobsController.TrackedJob tj : dijc.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ StateControllerProto.IdleController ic = sc.getIdle();
+ for (StateControllerProto.IdleController.TrackedJob tj : ic.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ StateControllerProto.StorageController scr = sc.getStorage();
+ for (StateControllerProto.StorageController.TrackedJob tj : scr.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ StateControllerProto.TimeController tc = sc.getTime();
+ assertTrue(0 <= tc.getNowElapsedRealtime());
+ assertTrue(0 <= tc.getTimeUntilNextDelayAlarmMs());
+ assertTrue(0 <= tc.getTimeUntilNextDeadlineAlarmMs());
+ for (StateControllerProto.TimeController.TrackedJob tj : tc.getTrackedJobsList()) {
+ testJobStatusShortInfoProto(tj.getInfo());
+ assertTrue(0 <= tj.getSourceUid());
+ }
+ }
+}
diff --git a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
index 5339629..0f83f74 100644
--- a/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
+++ b/hostsidetests/multiuser/src/android/host/multiuser/CreateUsersNoAppCrashesTest.java
@@ -39,8 +39,7 @@
mInitialUserId = getDevice().getCurrentUser();
}
- // TODO (b/71573557): Re-add to presubmit
- //@Presubmit
+ @Presubmit
public void testCanCreateGuestUser() throws Exception {
if (!mSupportsMultiUser) {
return;
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index ed581db..8104245 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -24,12 +24,15 @@
import android.app.RemoteInput;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.drawable.Icon;
import android.net.Uri;
+import android.os.Build;
import android.os.Parcel;
import android.test.AndroidTestCase;
import android.widget.RemoteViews;
-import org.mockito.internal.matchers.Not;
+import java.util.ArrayList;
public class NotificationTest extends AndroidTestCase {
private static final String TEXT_RESULT_KEY = "text";
@@ -236,6 +239,61 @@
assertEquals(true, mAction.getAllowGeneratedReplies());
}
+ public void testNotification_addPerson() {
+ String name = "name";
+ String key = "key";
+ String uri = "name:name";
+ Notification.Person person = new Notification.Person()
+ .setName(name)
+ .setIcon(Icon.createWithResource(mContext, 1))
+ .setKey(key)
+ .setUri(uri);
+ mNotification = new Notification.Builder(mContext, CHANNEL.getId())
+ .setSmallIcon(1)
+ .setContentTitle(CONTENT_TITLE)
+ .addPerson(person)
+ .build();
+
+ ArrayList<Notification.Person> restoredPeople = mNotification.extras.getParcelableArrayList(
+ Notification.EXTRA_PEOPLE_LIST);
+ assertNotNull(restoredPeople);
+ Notification.Person restoredPerson = restoredPeople.get(0);
+ assertNotNull(restoredPerson);
+ assertNotNull(restoredPerson.getIcon());
+ assertEquals(name, restoredPerson.getName());
+ assertEquals(key, restoredPerson.getKey());
+ assertEquals(uri, restoredPerson.getUri());
+ }
+
+ public void testNotification_MessagingStyle_people() {
+ String name = "name";
+ String key = "key";
+ String uri = "name:name";
+ Notification.Person user = new Notification.Person()
+ .setName(name)
+ .setIcon(Icon.createWithResource(mContext, 1))
+ .setKey(key)
+ .setUri(uri);
+ Notification.Person participant = new Notification.Person().setName("sender");
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle(user)
+ .addMessage("text", 0, participant)
+ .addMessage(new Message("text 2", 0, participant));
+ mNotification = new Notification.Builder(mContext, CHANNEL.getId())
+ .setSmallIcon(1)
+ .setStyle(messagingStyle)
+ .build();
+
+ Notification.Person restoredPerson = mNotification.extras.getParcelable(
+ Notification.EXTRA_MESSAGING_PERSON);
+ assertNotNull(restoredPerson);
+ assertNotNull(restoredPerson.getIcon());
+ assertEquals(name, restoredPerson.getName());
+ assertEquals(key, restoredPerson.getKey());
+ assertEquals(uri, restoredPerson.getUri());
+ assertNotNull(mNotification.extras.getParcelableArray(Notification.EXTRA_MESSAGES));
+ }
+
+
public void testMessagingStyle_historicMessages() {
mNotification = new Notification.Builder(mContext, CHANNEL.getId())
.setSmallIcon(1)
@@ -253,17 +311,65 @@
}
public void testMessagingStyle_isGroupConversation() {
- Notification.MessagingStyle messagingStyle =
- new Notification.MessagingStyle("self name")
- .setGroupConversation(true);
- mNotification = new Notification.Builder(mContext, "test id")
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+ .setGroupConversation(true)
+ .setConversationTitle("test conversation title");
+ Notification notification = new Notification.Builder(mContext, "test id")
.setSmallIcon(1)
.setContentTitle("test title")
.setStyle(messagingStyle)
.build();
assertTrue(messagingStyle.isGroupConversation());
- assertTrue(mNotification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+ assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+ }
+
+ public void testMessagingStyle_isGroupConversation_noConversationTitle() {
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.P;
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+ .setGroupConversation(true)
+ .setConversationTitle(null);
+ Notification notification = new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ assertTrue(messagingStyle.isGroupConversation());
+ assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+ }
+
+ public void testMessagingStyle_isGroupConversation_withConversationTitle_legacy() {
+ // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+ .setGroupConversation(false)
+ .setConversationTitle("test conversation title");
+ Notification notification = new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ assertTrue(messagingStyle.isGroupConversation());
+ assertFalse(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
+ }
+
+ public void testMessagingStyle_isGroupConversation_withoutConversationTitle_legacy() {
+ // In legacy (version < P), isGroupConversation is controlled by conversationTitle.
+ mContext.getApplicationInfo().targetSdkVersion = Build.VERSION_CODES.O;
+ Notification.MessagingStyle messagingStyle = new Notification.MessagingStyle("self name")
+ .setGroupConversation(true)
+ .setConversationTitle(null);
+ Notification notification = new Notification.Builder(mContext, "test id")
+ .setSmallIcon(1)
+ .setContentTitle("test title")
+ .setStyle(messagingStyle)
+ .build();
+
+ assertFalse(messagingStyle.isGroupConversation());
+ assertTrue(notification.extras.getBoolean(Notification.EXTRA_IS_GROUP_CONVERSATION));
}
public void testToString() {
diff --git a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
index a1ff068..488c0f3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/FastBasicsTest.java
@@ -57,6 +57,7 @@
private static final int WAIT_FOR_PICTURE_TIMEOUT_MS = 5000;
private static final int FRAMES_TO_WAIT_FOR_CAPTURE = 100;
+ @Presubmit
public void testCamera2() throws Exception {
for (int i = 0; i < mCameraIds.length; i++) {
try {
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
index 5880cd1..a9f960d 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerActivityVisibilityTests.java
@@ -23,6 +23,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityManagerState.STATE_PAUSED;
import static android.server.am.ActivityManagerState.STATE_RESUMED;
import static org.junit.Assert.assertFalse;
@@ -58,13 +59,6 @@
private static final String TURN_SCREEN_ON_WITH_RELAYOUT_ACTIVITY =
"TurnScreenOnWithRelayoutActivity";
- @After
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- tearDownLockCredentials();
- }
-
@Presubmit
@Test
public void testTranslucentActivityOnTopOfPinnedStack() throws Exception {
@@ -156,9 +150,9 @@
launchActivity(TURN_SCREEN_ON_ACTIVITY_NAME);
mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ACTIVITY_NAME });
mAmWmState.assertVisibility(TURN_SCREEN_ON_ACTIVITY_NAME, true);
+ assertTrue(isDisplayOn());
}
- @FlakyTest(bugId = 69229402)
@Presubmit
@Test
public void testFinishActivityInNonFocusedStack() throws Exception {
@@ -175,6 +169,10 @@
mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
// Finish activity in non-focused (docked) stack.
executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+ mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_PAUSED);
+ mAmWmState.waitForAllExitingWindows();
+
mAmWmState.computeState(new String[] { LAUNCHING_ACTIVITY });
mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true);
mAmWmState.assertVisibility(BROADCAST_RECEIVER_ACTIVITY, false);
@@ -338,13 +336,15 @@
public void testTurnScreenOnAttrWithLockScreen() throws Exception {
assumeTrue(isHandheld());
- setLockCredential();
- sleepDevice();
- final String logSeparator = clearLogcat();
- launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
- mAmWmState.computeState(new String[] { TURN_SCREEN_ON_ATTR_ACTIVITY_NAME });
- assertFalse(isDisplayOn());
- assertSingleLaunchAndStop(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ sleepDevice();
+ final String logSeparator = clearLogcat();
+ launchActivity(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME);
+ mAmWmState.computeState(new String[]{TURN_SCREEN_ON_ATTR_ACTIVITY_NAME});
+ assertFalse(isDisplayOn());
+ assertSingleLaunchAndStop(TURN_SCREEN_ON_ATTR_ACTIVITY_NAME, logSeparator);
+ }
}
@Test
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
index 404e5c7..b669db1 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerAppConfigurationTests.java
@@ -38,6 +38,7 @@
import android.content.ComponentName;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
import org.junit.Test;
@@ -100,6 +101,7 @@
*/
@Presubmit
@Test
+ @FlakyTest(bugId = 71792393)
public void testConfigurationUpdatesWhenResizedFromDockedStack() throws Exception {
assumeTrue("Skipping test: no multi-window support", supportsSplitScreenMultiWindow());
@@ -415,6 +417,7 @@
*/
@Presubmit
@Test
+ @FlakyTest(bugId = 71792393)
public void testTaskMoveToBackOrientation() throws Exception {
// Start landscape activity.
launchActivity(LANDSCAPE_ACTIVITY_NAME);
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
index b17881f..4837963 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayKeyguardTests.java
@@ -43,27 +43,22 @@
setLockDisabled(false);
}
- @After
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- tearDownLockCredentials();
- }
-
/**
* Tests whether a FLAG_DISMISS_KEYGUARD activity on a secondary display is visible (for an
* insecure keyguard).
*/
@Test
public void testDismissKeyguardActivity_secondaryDisplay() throws Exception {
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ }
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
index c773651..c3c5d33 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayLockedKeyguardTests.java
@@ -20,6 +20,7 @@
import static android.server.am.ActivityManagerState.STATE_STOPPED;
import static org.junit.Assume.assumeTrue;
+
import android.server.am.ActivityManagerState.ActivityDisplay;
import org.junit.After;
@@ -46,16 +47,6 @@
assumeTrue(supportsMultiDisplay());
assumeTrue(isHandheld());
-
- setLockCredential();
- }
-
-
- @After
- @Override
- public void tearDown() throws Exception {
- tearDownLockCredentials();
- super.tearDown();
}
/**
@@ -63,25 +54,30 @@
*/
@Test
public void testVirtualDisplayHidesContentWhenLocked() throws Exception {
- // Create new usual virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+ final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ // Create new usual virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- // Lock the device.
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- // Unlock and check if visibility is back.
- unlockDeviceWithCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ // Lock the device.
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, false /* visible */);
+
+ // Unlock and check if visibility is back.
+ lockCredentialSession.unlockDeviceWithCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ }
}
/**
@@ -89,34 +85,41 @@
*/
@Test
public void testDismissKeyguard_secondaryDisplay() throws Exception {
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+ final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
- enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.assertKeyguardGone();
- mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+ lockCredentialSession.enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ }
}
@Test
public void testDismissKeyguard_whileOccluded_secondaryDisplay() throws Exception {
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession();
+ final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
- mAmWmState.computeState(
- new WaitForValidActivityState.Builder(SHOW_WHEN_LOCKED_ACTIVITY).build());
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
- launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
- enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.assertKeyguardGone();
- mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ launchActivityOnDisplay(DISMISS_KEYGUARD_ACTIVITY, newDisplay.mId);
+ lockCredentialSession.enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertVisibility(DISMISS_KEYGUARD_ACTIVITY, true);
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ }
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
index bc53952..09d793f 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTestBase.java
@@ -24,13 +24,18 @@
import static org.junit.Assert.assertTrue;
import android.content.res.Configuration;
+import android.provider.Settings;
import android.server.am.ActivityManagerState.ActivityDisplay;
-
-import org.junit.After;
+import android.server.am.settings.SettingsSession;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.util.Size;
import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Collections;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Base class for ActivityManager display tests.
@@ -39,26 +44,14 @@
* @see ActivityManagerDisplayLockedKeyguardTests
*/
public class ActivityManagerDisplayTestBase extends ActivityManagerTestBase {
+ private static final String WM_SIZE = "wm size";
+ private static final String WM_DENSITY = "wm density";
static final int CUSTOM_DENSITY_DPI = 222;
private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
private static final int INVALID_DENSITY_DPI = -1;
- private boolean mVirtualDisplayCreated;
- private boolean mDisplaySimulated;
-
- /** Temp storage used for parsing. */
- final LinkedList<String> mDumpLines = new LinkedList<>();
-
- @After
- @Override
- public void tearDown() throws Exception {
- destroyVirtualDisplays();
- destroySimulatedDisplays();
- super.tearDown();
- }
-
ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int displayId) {
for (ActivityDisplay display : displays) {
if (display.mId == displayId) {
@@ -103,110 +96,101 @@
return result;
}
- /**
- * Create new virtual display.
- * @param densityDpi provide custom density for the display.
- * @param launchInSplitScreen start {@link VirtualDisplayActivity} to side from
- * {@link LaunchingActivity} on primary display.
- * @param canShowWithInsecureKeyguard allow showing content when device is showing an insecure
- * keyguard.
- * @param mustBeCreated should assert if the display was or wasn't created.
- * @param publicDisplay make display public.
- * @param resizeDisplay should resize display when surface size changes.
- * @param launchActivity should launch test activity immediately after display creation.
- * @return A list of {@link ActivityDisplay} that represent newly created displays.
- * @throws Exception
- */
- private List<ActivityDisplay> createVirtualDisplays(int densityDpi,
- boolean launchInSplitScreen, boolean canShowWithInsecureKeyguard, boolean mustBeCreated,
- boolean publicDisplay, boolean resizeDisplay, String launchActivity, int displayCount)
- throws Exception {
- // Start an activity that is able to create virtual displays.
- if (launchInSplitScreen) {
- getLaunchActivityBuilder().setToSide(true)
- .setTargetActivityName(VIRTUAL_DISPLAY_ACTIVITY).execute();
- } else {
- launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
- }
- mAmWmState.computeState(false /* compareTaskAndStackBounds */,
- new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
- final List<ActivityDisplay> originalDS = getDisplaysStates();
+ static class ReportedDisplayMetrics {
+ private static final String WM_SIZE = "wm size";
+ private static final String WM_DENSITY = "wm density";
+ private static final Pattern PHYSICAL_SIZE =
+ Pattern.compile("Physical size: (\\d+)x(\\d+)");
+ private static final Pattern OVERRIDE_SIZE =
+ Pattern.compile("Override size: (\\d+)x(\\d+)");
+ private static final Pattern PHYSICAL_DENSITY =
+ Pattern.compile("Physical density: (\\d+)");
+ private static final Pattern OVERRIDE_DENSITY =
+ Pattern.compile("Override density: (\\d+)");
- // Create virtual display with custom density dpi.
- executeShellCommand(getCreateVirtualDisplayCommand(densityDpi, canShowWithInsecureKeyguard,
- publicDisplay, resizeDisplay, launchActivity, displayCount));
- mVirtualDisplayCreated = true;
+ @NonNull
+ final Size physicalSize;
+ final int physicalDensity;
- return assertAndGetNewDisplays(mustBeCreated ? displayCount : -1, originalDS);
- }
+ @Nullable
+ final Size overrideSize;
+ @Nullable
+ final Integer overrideDensity;
- /**
- * Simulate new display.
- * @param densityDpi provide custom density for the display.
- * @return {@link ActivityDisplay} of newly created display.
- */
- private List<ActivityDisplay> simulateDisplay(int densityDpi)
- throws Exception {
- final List<ActivityDisplay> originalDs = getDisplaysStates();
-
- // Create virtual display with custom density dpi.
- executeShellCommand(getSimulateDisplayCommand(densityDpi));
- mDisplaySimulated = true;
-
- return assertAndGetNewDisplays(1, originalDs);
- }
-
- /**
- * Wait for desired number of displays to be created and get their properties.
- * @param newDisplayCount expected display count, -1 if display should not be created.
- * @param originalDS display states before creation of new display(s).
- */
- private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
- List<ActivityDisplay> originalDS) throws Exception {
- final int originalDisplayCount = originalDS.size();
-
- // Wait for the display(s) to be created and get configurations.
- final List<ActivityDisplay> ds = getDisplayStateAfterChange(
- originalDisplayCount + newDisplayCount);
- if (newDisplayCount != -1) {
- assertEquals("New virtual display(s) must be created",
- originalDisplayCount + newDisplayCount, ds.size());
- } else {
- assertEquals("New virtual display must not be created",
- originalDisplayCount, ds.size());
- return null;
+ /** Get physical and override display metrics from WM. */
+ static ReportedDisplayMetrics getDisplayMetrics() throws Exception {
+ return new ReportedDisplayMetrics(
+ executeShellCommand(WM_SIZE) + executeShellCommand(WM_DENSITY));
}
- // Find the newly added display(s).
- final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
- assertTrue("New virtual display must be created", newDisplayCount == newDisplays.size());
+ void setDisplayMetrics(final Size size, final int density) {
+ setSize(size);
+ setDensity(density);
+ }
- return newDisplays;
- }
+ void restoreDisplayMetrics() {
+ if (overrideSize != null) {
+ setSize(overrideSize);
+ } else {
+ executeShellCommand(WM_SIZE + " reset");
+ }
+ if (overrideDensity != null) {
+ setDensity(overrideDensity);
+ } else {
+ executeShellCommand(WM_DENSITY + " reset");
+ }
+ }
- /**
- * Destroy existing virtual display.
- */
- void destroyVirtualDisplays() throws Exception {
- if (mVirtualDisplayCreated) {
- executeShellCommand(getDestroyVirtualDisplayCommand());
- mVirtualDisplayCreated = false;
+ private void setSize(final Size size) {
+ executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
+ }
+
+ private void setDensity(final int density) {
+ executeShellCommand(WM_DENSITY + " " + density);
+ }
+
+ /** Get display size that WM operates with. */
+ Size getSize() {
+ return overrideSize != null ? overrideSize : physicalSize;
+ }
+
+ /** Get density that WM operates with. */
+ int getDensity() {
+ return overrideDensity != null ? overrideDensity : physicalDensity;
+ }
+
+ private ReportedDisplayMetrics(final String lines) throws Exception {
+ Matcher matcher = PHYSICAL_SIZE.matcher(lines);
+ assertTrue("Physical display size must be reported", matcher.find());
+ log(matcher.group());
+ physicalSize = new Size(
+ Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
+
+ matcher = PHYSICAL_DENSITY.matcher(lines);
+ assertTrue("Physical display density must be reported", matcher.find());
+ log(matcher.group());
+ physicalDensity = Integer.parseInt(matcher.group(1));
+
+ matcher = OVERRIDE_SIZE.matcher(lines);
+ if (matcher.find()) {
+ log(matcher.group());
+ overrideSize = new Size(
+ Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
+ } else {
+ overrideSize = null;
+ }
+
+ matcher = OVERRIDE_DENSITY.matcher(lines);
+ if (matcher.find()) {
+ log(matcher.group());
+ overrideDensity = Integer.parseInt(matcher.group(1));
+ } else {
+ overrideDensity = null;
+ }
}
}
- /**
- * Destroy existing simulated display.
- */
- private void destroySimulatedDisplays() throws Exception {
- if (mDisplaySimulated) {
- executeShellCommand(getDestroySimulatedDisplayCommand());
- mDisplaySimulated = false;
- }
- }
-
- static class VirtualDisplayBuilder {
- private final ActivityManagerDisplayTestBase mTests;
-
+ protected class VirtualDisplaySession implements AutoCloseable {
private int mDensityDpi = CUSTOM_DENSITY_DPI;
private boolean mLaunchInSplitScreen = false;
private boolean mCanShowWithInsecureKeyguard = false;
@@ -216,99 +200,196 @@
private boolean mSimulateDisplay = false;
private boolean mMustBeCreated = true;
- public VirtualDisplayBuilder(ActivityManagerDisplayTestBase tests) {
- mTests = tests;
- }
+ private boolean mVirtualDisplayCreated = false;
+ private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
+ new OverlayDisplayDevicesSession();
- public VirtualDisplayBuilder setDensityDpi(int densityDpi) {
+ public VirtualDisplaySession setDensityDpi(int densityDpi) {
mDensityDpi = densityDpi;
return this;
}
- public VirtualDisplayBuilder setLaunchInSplitScreen(boolean launchInSplitScreen) {
+ public VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
mLaunchInSplitScreen = launchInSplitScreen;
return this;
}
- public VirtualDisplayBuilder setCanShowWithInsecureKeyguard(
+ public VirtualDisplaySession setCanShowWithInsecureKeyguard(
boolean canShowWithInsecureKeyguard) {
mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
return this;
}
- public VirtualDisplayBuilder setPublicDisplay(boolean publicDisplay) {
+ public VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
mPublicDisplay = publicDisplay;
return this;
}
- public VirtualDisplayBuilder setResizeDisplay(boolean resizeDisplay) {
+ public VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
mResizeDisplay = resizeDisplay;
return this;
}
- public VirtualDisplayBuilder setLaunchActivity(String launchActivity) {
+ public VirtualDisplaySession setLaunchActivity(String launchActivity) {
mLaunchActivity = launchActivity;
return this;
}
- public VirtualDisplayBuilder setSimulateDisplay(boolean simulateDisplay) {
+ public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
mSimulateDisplay = simulateDisplay;
return this;
}
- public VirtualDisplayBuilder setMustBeCreated(boolean mustBeCreated) {
+ public VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
mMustBeCreated = mustBeCreated;
return this;
}
- public ActivityDisplay build() throws Exception {
- final List<ActivityDisplay> displays = build(1);
- return displays != null && !displays.isEmpty() ? displays.get(0) : null;
+ @Nullable
+ public ActivityDisplay createDisplay() throws Exception {
+ return createDisplays(1).stream().findFirst().orElse(null);
}
- public List<ActivityDisplay> build(int count) throws Exception {
+ @NonNull
+ public List<ActivityDisplay> createDisplays(int count) throws Exception {
if (mSimulateDisplay) {
- return mTests.simulateDisplay(mDensityDpi);
+ return simulateDisplay();
+ } else {
+ return createVirtualDisplays(count);
+ }
+ }
+
+ @Override
+ public void close() throws Exception {
+ mOverlayDisplayDeviceSession.close();
+ if (mVirtualDisplayCreated) {
+ destroyVirtualDisplays();
+ mVirtualDisplayCreated = false;
+ }
+ }
+
+ /**
+ * Simulate new display.
+ * <pre>
+ * <code>mDensityDpi</code> provide custom density for the display.
+ * </pre>
+ * @return {@link ActivityDisplay} of newly created display.
+ */
+ private List<ActivityDisplay> simulateDisplay() throws Exception {
+ final List<ActivityDisplay> originalDs = getDisplaysStates();
+
+ // Create virtual display with custom density dpi.
+ mOverlayDisplayDeviceSession.set("1024x768/" + mDensityDpi);
+
+ return assertAndGetNewDisplays(1, originalDs);
+ }
+
+ /**
+ * Create new virtual display.
+ * <pre>
+ * <code>mDensityDpi</code> provide custom density for the display.
+ * <code>mLaunchInSplitScreen</code> start {@link VirtualDisplayActivity} to side from
+ * {@link LaunchingActivity} on primary display.
+ * <code>mCanShowWithInsecureKeyguard</code> allow showing content when device is
+ * showing an insecure keyguard.
+ * <code>mMustBeCreated</code> should assert if the display was or wasn't created.
+ * <code>mPublicDisplay</code> make display public.
+ * <code>mResizeDisplay</code> should resize display when surface size changes.
+ * <code>LaunchActivity</code> should launch test activity immediately after display
+ * creation.
+ * </pre>
+ * @param displayCount number of displays to be created.
+ * @return A list of {@link ActivityDisplay} that represent newly created displays.
+ * @throws Exception
+ */
+ private List<ActivityDisplay> createVirtualDisplays(int displayCount) throws Exception {
+ // Start an activity that is able to create virtual displays.
+ if (mLaunchInSplitScreen) {
+ getLaunchActivityBuilder()
+ .setToSide(true)
+ .setTargetActivityName(VIRTUAL_DISPLAY_ACTIVITY)
+ .execute();
+ } else {
+ launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
+ }
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+ final List<ActivityDisplay> originalDS = getDisplaysStates();
+
+ // Create virtual display with custom density dpi.
+ final StringBuilder createVirtualDisplayCommand = new StringBuilder(
+ getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
+ .append(" -f 0x20000000")
+ .append(" --es command create_display");
+ if (mDensityDpi != INVALID_DENSITY_DPI) {
+ createVirtualDisplayCommand
+ .append(" --ei density_dpi ")
+ .append(mDensityDpi);
+ }
+ createVirtualDisplayCommand.append(" --ei count ").append(displayCount)
+ .append(" --ez can_show_with_insecure_keyguard ")
+ .append(mCanShowWithInsecureKeyguard)
+ .append(" --ez public_display ").append(mPublicDisplay)
+ .append(" --ez resize_display ").append(mResizeDisplay);
+ if (mLaunchActivity != null) {
+ createVirtualDisplayCommand
+ .append(" --es launch_target_activity ")
+ .append(mLaunchActivity);
+ }
+ executeShellCommand(createVirtualDisplayCommand.toString());
+ mVirtualDisplayCreated = true;
+
+ return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
+ }
+
+ /**
+ * Destroy existing virtual display.
+ */
+ void destroyVirtualDisplays() throws Exception {
+ final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
+ + " -f 0x20000000"
+ + " --es command destroy_display";
+ executeShellCommand(destroyVirtualDisplayCommand);
+ }
+
+ /**
+ * Wait for desired number of displays to be created and get their properties.
+ * @param newDisplayCount expected display count, -1 if display should not be created.
+ * @param originalDS display states before creation of new display(s).
+ * @return list of new displays, empty list if no new display is created.
+ */
+ private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
+ List<ActivityDisplay> originalDS) throws Exception {
+ final int originalDisplayCount = originalDS.size();
+
+ // Wait for the display(s) to be created and get configurations.
+ final List<ActivityDisplay> ds = getDisplayStateAfterChange(
+ originalDisplayCount + newDisplayCount);
+ if (newDisplayCount != -1) {
+ assertEquals("New virtual display(s) must be created",
+ originalDisplayCount + newDisplayCount, ds.size());
+ } else {
+ assertEquals("New virtual display must not be created",
+ originalDisplayCount, ds.size());
+ return Collections.emptyList();
}
- return mTests.createVirtualDisplays(mDensityDpi, mLaunchInSplitScreen,
- mCanShowWithInsecureKeyguard, mMustBeCreated, mPublicDisplay, mResizeDisplay,
- mLaunchActivity, count);
+ // Find the newly added display(s).
+ final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
+ assertTrue("New virtual display must be created",
+ newDisplayCount == newDisplays.size());
+
+ return newDisplays;
}
}
- private static String getCreateVirtualDisplayCommand(int densityDpi,
- boolean canShowWithInsecureKeyguard, boolean publicDisplay, boolean resizeDisplay,
- String launchActivity, int displayCount) {
- final StringBuilder commandBuilder
- = new StringBuilder(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY));
- commandBuilder.append(" -f 0x20000000");
- commandBuilder.append(" --es command create_display");
- if (densityDpi != INVALID_DENSITY_DPI) {
- commandBuilder.append(" --ei density_dpi ").append(densityDpi);
+ /** Helper class to save, set, and restore overlay_display_devices preference. */
+ private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
+ OverlayDisplayDevicesSession() {
+ super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
+ Settings.Global::getString,
+ Settings.Global::putString);
}
- commandBuilder.append(" --ei count ").append(displayCount);
- commandBuilder.append(" --ez can_show_with_insecure_keyguard ")
- .append(canShowWithInsecureKeyguard);
- commandBuilder.append(" --ez public_display ").append(publicDisplay);
- commandBuilder.append(" --ez resize_display ").append(resizeDisplay);
- if (launchActivity != null) {
- commandBuilder.append(" --es launch_target_activity ").append(launchActivity);
- }
- return commandBuilder.toString();
- }
-
- private static String getDestroyVirtualDisplayCommand() {
- return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
- " --es command destroy_display";
- }
-
- private static String getSimulateDisplayCommand(int densityDpi) {
- return "settings put global overlay_display_devices 1024x768/" + densityDpi;
- }
-
- private static String getDestroySimulatedDisplayCommand() {
- return "settings delete global overlay_display_devices";
}
/** Wait for provided number of displays and report their configurations. */
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
index 3229905..d82c7c5 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerDisplayTests.java
@@ -16,124 +16,27 @@
package android.server.am;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
-import static android.server.am.ActivityManagerState.STATE_RESUMED;
-import static android.server.am.ActivityManagerState.STATE_STOPPED;
-import static android.server.am.StateLogger.log;
-import static android.server.am.StateLogger.logE;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
import android.server.am.ActivityManagerState.ActivityDisplay;
-import android.server.am.displayservice.DisplayHelper;
-import android.support.test.filters.FlakyTest;
+import android.util.Size;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import java.util.Collections;
-import java.util.LinkedList;
import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Build/Install/Run:
* atest CtsActivityManagerDeviceTestCases:ActivityManagerDisplayTests
*/
public class ActivityManagerDisplayTests extends ActivityManagerDisplayTestBase {
- private static final String WM_SIZE = "wm size";
- private static final String WM_DENSITY = "wm density";
-
private static final String TEST_ACTIVITY_NAME = "TestActivity";
- private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
- private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
- private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
- private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
- private static final String SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME = "ShowWhenLockedAttrActivity";
- private static final String SECOND_PACKAGE = "android.server.am.second";
- private static final String THIRD_PACKAGE = "android.server.am.third";
- private static final ComponentName SECOND_ACTIVITY = ComponentName.createRelative(
- SECOND_PACKAGE, ".SecondActivity");
- private static final ComponentName SECOND_NO_EMBEDDING_ACTIVITY = ComponentName.createRelative(
- SECOND_PACKAGE, ".SecondActivityNoEmbedding");
- private static final ComponentName LAUNCH_BROADCAST_RECEIVER = ComponentName.createRelative(
- SECOND_PACKAGE, ".LaunchBroadcastReceiver");
- /** See AndroidManifest.xml of appSecondUid. */
- private static final String LAUNCH_BROADCAST_ACTION =
- SECOND_PACKAGE + ".LAUNCH_BROADCAST_ACTION";
- private static final ComponentName THIRD_ACTIVITY = ComponentName.createRelative(
- THIRD_PACKAGE, ".ThirdActivity");
- private static final int VR_VIRTUAL_DISPLAY_WIDTH = 700;
- private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 900;
- private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
-
- private DisplayHelper mExternalDisplayHelper;
-
- /** Physical display metrics and overrides in the beginning of the test. */
- private ReportedDisplayMetrics mInitialDisplayMetrics;
-
- // TODO: We might consider breaking this class up into
- // ActivityManagerDisplayTests for general
- // ActivityManagerMultiDisplayTests
- // ActivityManagerVrDisplayTests
- // so the assumption can be put in the setUp().
- @Before
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mInitialDisplayMetrics = getDisplayMetrics();
- }
-
- @After
- @Override
- public void tearDown() throws Exception {
- enablePersistentVrMode(false);
- restoreDisplayMetricsOverrides();
- if (mExternalDisplayHelper != null) {
- mExternalDisplayHelper.releaseDisplay();
- mExternalDisplayHelper = null;
- }
- setPrimaryDisplayState(true);
- super.tearDown();
- }
-
- private void enablePersistentVrMode(boolean enabled) throws Exception {
- if (enabled) {
- executeShellCommand("setprop vr_virtualdisplay true");
- executeShellCommand("vr set-persistent-vr-mode-enabled true");
- } else {
- executeShellCommand("vr set-persistent-vr-mode-enabled false");
- executeShellCommand("setprop vr_virtualdisplay false");
- }
- }
-
- private void restoreDisplayMetricsOverrides() throws Exception {
- if (mInitialDisplayMetrics.sizeOverrideSet) {
- executeShellCommand(WM_SIZE + " " + mInitialDisplayMetrics.overrideWidth + "x"
- + mInitialDisplayMetrics.overrideHeight);
- } else {
- executeShellCommand("wm size reset");
- }
- if (mInitialDisplayMetrics.densityOverrideSet) {
- executeShellCommand(WM_DENSITY + " " + mInitialDisplayMetrics.overrideDensity);
- } else {
- executeShellCommand("wm density reset");
- }
- }
/**
* Tests that the global configuration is equal to the default display's override configuration.
@@ -154,11 +57,13 @@
*/
@Test
public void testCreateVirtualDisplayWithCustomConfig() throws Exception {
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
- // Find the density of created display.
- final int newDensityDpi = newDisplay.mFullConfiguration.densityDpi;
- assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
+ // Find the density of created display.
+ final int newDensityDpi = newDisplay.mFullConfiguration.densityDpi;
+ assertEquals(CUSTOM_DENSITY_DPI, newDensityDpi);
+ }
}
/**
@@ -171,1567 +76,41 @@
// Only check devices with the feature disabled.
assumeFalse(supportsMultiDisplay());
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
- mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ TEST_ACTIVITY_NAME);
- // Check that activity is on the right display.
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
- final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed",
- getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
- assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
- frontStack.mDisplayId);
- mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
- }
-
- /**
- * Tests that any new activity launch in Vr mode is in Vr display.
- */
- @Test
- public void testVrActivityLaunch() throws Exception {
- assumeTrue(supportsVrMode());
- assumeTrue(supportsMultiDisplay());
-
- // Put the device in persistent vr mode.
- enablePersistentVrMode(true);
-
- // Launch the VR activity.
- launchActivity(VR_TEST_ACTIVITY_NAME);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
- mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
- // Launch the non-VR 2D activity and check where it ends up.
- launchActivity(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
- // Ensure that the subsequent activity is visible
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
- // Check that activity is launched in focused stack on primary display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
- final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
- final ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(focusedStackId);
- assertEquals("Launched activity must be resumed in focused stack",
- getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
- // Check if the launch activity is in Vr virtual display id.
- final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
- final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
- VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
- assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
- // Check if the focused activity is on this virtual stack.
- assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
- focusedStack.mDisplayId);
-
- // Put the device out of persistent vr mode.
- enablePersistentVrMode(false);
- }
-
- /**
- * Tests that any activity already present is re-launched in Vr display in vr mode.
- */
- @Test
- public void testVrActivityReLaunch() throws Exception {
- assumeTrue(supportsVrMode());
- assumeTrue(supportsMultiDisplay());
-
- // Launch a 2D activity.
- launchActivity(LAUNCHING_ACTIVITY);
-
- // Put the device in persistent vr mode.
- enablePersistentVrMode(true);
-
- // Launch the VR activity.
- launchActivity(VR_TEST_ACTIVITY_NAME);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
- mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
- // Re-launch the non-VR 2D activity and check where it ends up.
- launchActivity(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
- // Ensure that the subsequent activity is visible
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
- // Check that activity is launched in focused stack on primary display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
- final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
- final ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(focusedStackId);
- assertEquals("Launched activity must be resumed in focused stack",
- getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
- // Check if the launch activity is in Vr virtual display id.
- final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
- final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
- VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
- assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
- // Check if the focused activity is on this virtual stack.
- assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
- focusedStack.mDisplayId);
-
- // Put the device out of persistent vr mode.
- enablePersistentVrMode(false);
- }
-
- /**
- * Tests that any new activity launch post Vr mode is in the main display.
- */
- @Test
- public void testActivityLaunchPostVr() throws Exception {
- assumeTrue(supportsVrMode());
- assumeTrue(supportsMultiDisplay());
-
- // Put the device in persistent vr mode.
- enablePersistentVrMode(true);
-
- // Launch the VR activity.
- launchActivity(VR_TEST_ACTIVITY_NAME);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(VR_TEST_ACTIVITY_NAME).build());
- mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
-
- // Launch the non-VR 2D activity and check where it ends up.
- launchActivity(ALT_LAUNCHING_ACTIVITY);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
-
- // Ensure that the subsequent activity is visible
- mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
-
- // Check that activity is launched in focused stack on primary display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", ALT_LAUNCHING_ACTIVITY);
- final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
- final ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(focusedStackId);
- assertEquals("Launched activity must be resumed in focused stack",
- getActivityComponentName(ALT_LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
-
- // Check if the launch activity is in Vr virtual display id.
- final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
- final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
- VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
- assertNotNull("Vr mode should have a virtual display", vrDisplay);
-
- // Check if the focused activity is on this virtual stack.
- assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
- focusedStack.mDisplayId);
-
- // Put the device out of persistent vr mode.
- enablePersistentVrMode(false);
-
- // There isn't a direct launch of activity which can take an user out of persistent VR mode.
- // This sleep is to account for that delay and let device settle once it comes out of VR
- // mode.
- try {
- Thread.sleep(2000);
- } catch (Exception e) {
- e.printStackTrace();
+ // Check that activity is on the right display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed",
+ getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+ assertEquals("Front stack must be on the default display", DEFAULT_DISPLAY_ID,
+ frontStack.mDisplayId);
+ mAmWmState.assertFocusedStack("Focus must be on the default display", frontStackId);
}
-
- // Launch the non-VR 2D activity and check where it ends up.
- launchActivity(RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY_NAME).build());
-
- // Ensure that the subsequent activity is visible
- mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-
- // Check that activity is launched in focused stack on primary display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", RESIZEABLE_ACTIVITY_NAME);
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
- final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(RESIZEABLE_ACTIVITY_NAME), frontStack.mResumedActivity);
- assertEquals("Front stack must be on primary display",
- DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
}
@Test
public void testCreateMultipleVirtualDisplays() throws Exception {
- List<ActivityDisplay> originalDs = getDisplaysStates();
- // Create new virtual displays
- new VirtualDisplayBuilder(this).build(3);
- getDisplayStateAfterChange(originalDs.size() + 3);
- destroyVirtualDisplays();
+ final List<ActivityDisplay> originalDs = getDisplaysStates();
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual displays
+ virtualDisplaySession.createDisplays(3);
+ getDisplayStateAfterChange(originalDs.size() + 3);
+ }
getDisplayStateAfterChange(originalDs.size());
}
/**
- * Tests launching an activity on virtual display.
- */
- @Presubmit
- @Test
- public void testLaunchActivityOnSecondaryDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Launch activity on new secondary display.
- final String logSeparator = clearLogcat();
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- TEST_ACTIVITY_NAME);
-
- // Check that activity is on the right display.
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be on the secondary display and resumed",
- getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- // Check that activity config corresponds to display config.
- final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
- logSeparator);
- assertEquals("Activity launched on secondary display must have proper configuration",
- CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
- }
-
- /**
- * Tests launching a non-resizeable activity on virtual display. It should land on the
- * default display.
- */
- @Test
- public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME)
- .build());
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- NON_RESIZEABLE_ACTIVITY_NAME);
-
- // Check that activity is on the right display.
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
- final ActivityManagerState.ActivityStack frontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be on the primary display and resumed",
- getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
- frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
- }
-
- /**
- * Tests launching a non-resizeable activity on virtual display while split-screen is active
- * on the primary display. It should land on the primary display and dismiss docked stack.
- */
- @Test
- public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
- assumeTrue(supportsMultiDisplay());
- assumeTrue(supportsSplitScreenMultiWindow());
-
- // Start launching activity.
- launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
- // Create new virtual display.
- final ActivityDisplay newDisplay =
- new VirtualDisplayBuilder(this).setLaunchInSplitScreen(true).build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- NON_RESIZEABLE_ACTIVITY_NAME);
-
- // Check that activity is on the right display.
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
- final ActivityManagerState.ActivityStack frontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be on the primary display and resumed",
- getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
- frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
- mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
- WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
- }
-
- /**
- * Tests moving a non-resizeable activity to a virtual display. It should stay on the default
- * display with no action performed.
- */
- @Test
- public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- // Launch a non-resizeable activity on a primary display.
- launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY_NAME);
- // Launch a resizeable activity on new secondary display to create a new stack there.
- launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
- final int externalFrontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
-
- // Try to move the non-resizeable activity to new secondary display.
- moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, externalFrontStackId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME)
- .build());
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- RESIZEABLE_ACTIVITY_NAME);
-
- // Check that activity is in the same stack
- final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
- final ActivityManagerState.ActivityStack defaultFrontStack =
- mAmWmState.getAmState().getStackById(defaultFrontStackId);
- assertEquals("Launched activity must be on the primary display and resumed",
- getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
- defaultFrontStack.getTopTask().mRealActivity);
- mAmWmState.assertFocusedStack("Focus must remain on the secondary display",
- externalFrontStackId);
- }
-
- /**
- * Tests launching a non-resizeable activity on virtual display from activity there. It should
- * land on the secondary display based on the resizeability of the root activity of the task.
- */
- @Test
- public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
- .build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- BROADCAST_RECEIVER_ACTIVITY);
-
- // Check that launching activity is on the secondary display.
- int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- ActivityManagerState.ActivityStack frontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be on the secondary display and resumed",
- getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
- frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
-
- // Launch non-resizeable activity from secondary display.
- executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
- + "--ez new_task true --es target_activity " + NON_RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(NON_RESIZEABLE_ACTIVITY_NAME).build());
-
- // Check that non-resizeable activity is on the secondary display, because of the resizeable
- // root of the task.
- frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- frontStack = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be on the primary display and resumed",
- getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
- frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
- }
-
- /**
- * Tests launching a non-resizeable activity on virtual display from activity there. It should
- * land on some different suitable display (usually - on the default one).
- */
- @Test
- public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- LAUNCHING_ACTIVITY);
-
- // Check that launching activity is on the secondary display.
- int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- ActivityManagerState.ActivityStack frontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be on the secondary display and resumed",
- getActivityComponentName(LAUNCHING_ACTIVITY),
- frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
-
- // Launch non-resizeable activity from secondary display.
- getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME)
- .setNewTask(true).setMultipleTask(true).execute();
-
- // Check that non-resizeable activity is on the primary display.
- frontStackId = mAmWmState.getAmState().getFocusedStackId();
- frontStack = mAmWmState.getAmState().getStackById(frontStackId);
- assertFalse("Launched activity must be on a different display",
- newDisplay.mId == frontStack.mDisplayId);
- assertEquals("Launched activity must be resumed",
- getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
- frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on a just launched activity", frontStackId);
- }
-
- /**
- * Tests launching an activity on a virtual display without special permission must not be
- * allowed.
- */
- @Test
- public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- final String logSeparator = clearLogcat();
-
- // Try to launch an activity and check it security exception was triggered.
- final String broadcastTarget = "-a " + LAUNCH_BROADCAST_ACTION
- + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName();
- final String includeStoppedPackagesFlag = " -f 0x00000020";
- executeShellCommand("am broadcast " + broadcastTarget
- + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
- + " --es package_name " + componentName
- + " --ei display_id " + newDisplay.mId
- + includeStoppedPackagesFlag);
-
- assertSecurityException("LaunchBroadcastReceiver", logSeparator);
-
- mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
- assertFalse("Restricted activity must not be launched",
- mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
- }
-
- /**
- * Tests launching an activity on a virtual display without special permission must be allowed
- * for activities with same UID.
- */
- @Test
- public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Try to launch an activity and check it security exception was triggered.
- final String broadcastTarget = "-a " + componentName + ".LAUNCH_BROADCAST_ACTION"
- + " -p " + componentName;
- executeShellCommand("am broadcast " + broadcastTarget
- + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
- + " --es package_name " + componentName
- + " --ei display_id " + newDisplay.mId);
-
- mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
-
- final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- final ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState()
- .getStackById(externalFocusedStackId);
- assertEquals("Focused stack must be on secondary display", newDisplay.mId,
- focusedStack.mDisplayId);
-
- mAmWmState.assertFocusedActivity("Focus must be on newly launched app", TEST_ACTIVITY_NAME);
- assertEquals("Activity launched by owner must be on external display",
- externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
- }
-
- /**
- * Tests launching an activity on virtual display and then launching another activity via shell
- * command and without specifying the display id - the second activity must appear on the
- * primary display.
- */
- @Presubmit
- @Test
- public void testConsequentLaunchActivity() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- TEST_ACTIVITY_NAME);
-
- // Launch second activity without specifying display.
- launchActivity(LAUNCHING_ACTIVITY);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
- // Check that activity is launched in focused stack on primary display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", LAUNCHING_ACTIVITY);
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
- final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
- assertEquals("Front stack must be on primary display",
- DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
- }
-
- /**
- * Tests launching an activity on simulated display and then launching another activity from the
- * first one - it must appear on the secondary display, because it was launched from there.
- */
- @FlakyTest(bugId = 71564456)
- @Presubmit
- @Test
- public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
- .build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
- LAUNCHING_ACTIVITY);
-
- // Launch second activity from app on secondary display without specifying display id.
- getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
- mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
- // Check that activity is launched in focused stack on external display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
- }
-
- /**
- * Tests launching an activity on virtual display and then launching another activity from the
- * first one - it must appear on the secondary display, because it was launched from there.
- */
- @Test
- public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
- mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
- LAUNCHING_ACTIVITY);
-
- // Launch second activity from app on secondary display without specifying display id.
- getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
- mAmWmState.computeState(new String[] {TEST_ACTIVITY_NAME});
-
- // Check that activity is launched in focused stack on external display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
- }
-
- /**
- * Tests launching an activity on virtual display and then launching another activity from the
- * first one with specifying the target display - it must appear on the secondary display.
- */
- @Test
- public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
- mAmWmState.computeState(LAUNCHING_ACTIVITY);
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
- LAUNCHING_ACTIVITY);
-
- // Launch second activity from app on secondary display specifying same display id.
- getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY)
- .setDisplayId(newDisplay.mId).execute();
- mAmWmState.computeState(TEST_ACTIVITY_NAME);
-
- // Check that activity is launched in focused stack on external display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_ACTIVITY);
- int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed in front stack",
- SECOND_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
-
- // Launch other activity with different uid and check if it has launched successfully.
- getLaunchActivityBuilder()
- .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
- .setDisplayId(newDisplay.mId)
- .setTargetActivity(THIRD_ACTIVITY)
- .execute();
- mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
-
- // Check that activity is launched in focused stack on external display.
- mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_ACTIVITY);
- frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- frontStack = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed in front stack",
- THIRD_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
- }
-
- /**
- * Tests launching an activity on virtual display and then launching another activity that
- * doesn't allow embedding - it should fail with security exception.
- */
- @Test
- public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
- mAmWmState.computeState(new String[] {LAUNCHING_ACTIVITY});
-
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be resumed",
- LAUNCHING_ACTIVITY);
-
- final String logSeparator = clearLogcat();
-
- // Launch second activity from app on secondary display specifying same display id.
- getLaunchActivityBuilder().setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY)
- .setDisplayId(newDisplay.mId)
- .execute();
-
- assertSecurityException("ActivityLauncher", logSeparator);
- }
-
- /**
- * Tests launching an activity to secondary display from activity on primary display.
- */
- @Test
- public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Start launching activity.
- launchActivity(LAUNCHING_ACTIVITY);
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
- .build();
-
- // Launch activity on secondary display from the app on primary display.
- getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
- .setDisplayId(newDisplay.mId).execute();
-
- // Check that activity is launched on external display.
- mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- TEST_ACTIVITY_NAME);
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack frontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Launched activity must be resumed in front stack",
- getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
- }
-
- /**
- * Tests launching activities on secondary and then on primary display to see if the stack
- * visibility is not affected.
- */
- @Presubmit
- @Test
- public void testLaunchActivitiesAffectsVisibility() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Start launching activity.
- launchActivity(LAUNCHING_ACTIVITY);
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
- // Launch activity on primary display and check if it doesn't affect activity on secondary
- // display.
- getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
- mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
- }
-
- /**
- * Test that move-task works when moving between displays.
- */
- @Presubmit
- @Test
- public void testMoveTaskBetweenDisplays() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
- VIRTUAL_DISPLAY_ACTIVITY);
- final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
- ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(defaultDisplayStackId);
- assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
- focusedStack.mDisplayId);
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Focus must be on secondary display", TEST_ACTIVITY_NAME);
- int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
- focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
- assertEquals("Focused stack must be on secondary display",
- newDisplay.mId, focusedStack.mDisplayId);
-
- // Move activity from secondary display to primary.
- moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
- mAmWmState.waitForFocusedStack(defaultDisplayStackId);
- mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
- focusedStackId = mAmWmState.getAmState().getFocusedStackId();
- focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
- assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
- focusedStack.mDisplayId);
- }
-
- /**
- * Tests launching activities on secondary display and then removing it to see if stack focus
- * is moved correctly.
- * This version launches virtual display creator to fullscreen stack in split-screen.
- */
- @FlakyTest(bugId = 69573940)
- @Presubmit
- @Test
- public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
- assumeTrue(supportsMultiDisplay());
- assumeTrue(supportsSplitScreenMultiWindow());
-
- // Start launching activity into docked stack.
- launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
- tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- }
-
- /**
- * Tests launching activities on secondary display and then removing it to see if stack focus
- * is moved correctly.
- * This version launches virtual display creator to docked stack in split-screen.
- */
- @Test
- public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
- assumeTrue(supportsMultiDisplay());
- assumeTrue(supportsSplitScreenMultiWindow());
-
- // Setup split-screen.
- launchActivitiesInSplitScreen(RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY);
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
-
- tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
- WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
- }
-
- /**
- * Tests launching activities on secondary display and then removing it to see if stack focus
- * is moved correctly.
- * This version works without split-screen.
- */
- @Test
- public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Start an activity on default display to determine default stack.
- launchActivity(BROADCAST_RECEIVER_ACTIVITY);
- final int focusedStackWindowingMode =
- mAmWmState.getAmState().getFrontStackWindowingMode(DEFAULT_DISPLAY_ID);
- // Finish probing activity.
- executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
- tryCreatingAndRemovingDisplayWithActivity(
- false /* splitScreen */, focusedStackWindowingMode);
- }
-
- /**
- * Create a virtual display, launch a test activity there, destroy the display and check if test
- * activity is moved to a stack on the default display.
- */
- private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)
- throws Exception {
- // Create new virtual display.
- final VirtualDisplayBuilder builder = new VirtualDisplayBuilder(this)
- .setPublicDisplay(true);
- if (splitScreen) {
- builder.setLaunchInSplitScreen(true);
- }
- final ActivityDisplay newDisplay = builder.build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- if (splitScreen) {
- mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
- }
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Focus must be on secondary display",
- TEST_ACTIVITY_NAME);
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- // Destroy virtual display.
- final String logSeparator = clearLogcat();
- destroyVirtualDisplays();
- assertActivityLifecycle(TEST_ACTIVITY_NAME, false /* relaunched */, logSeparator);
- mAmWmState.waitForValidState(TEST_ACTIVITY_NAME, windowingMode, ACTIVITY_TYPE_STANDARD);
- mAmWmState.assertSanity();
- mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
-
- // Check if the focus is switched back to primary display.
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertFocusedStack(
- "Default stack on primary display must be focused after display removed",
- windowingMode, ACTIVITY_TYPE_STANDARD);
- mAmWmState.assertFocusedActivity(
- "Focus must be switched back to activity on primary display",
- TEST_ACTIVITY_NAME);
- }
-
- /**
- * Tests launching activities on secondary display and then removing it to see if stack focus
- * is moved correctly.
- */
- @Test
- public void testStackFocusSwitchOnStackEmptied() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Focus must be on secondary display",
- BROADCAST_RECEIVER_ACTIVITY);
-
- // Lock the device, so that activity containers will be detached.
- sleepDevice();
-
- // Finish activity on secondary display.
- executeShellCommand(FINISH_ACTIVITY_BROADCAST);
-
- // Unlock and check if the focus is switched back to primary display.
- wakeUpAndUnlockDevice();
- mAmWmState.waitForFocusedStack(focusedStackId);
- mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY);
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
- VIRTUAL_DISPLAY_ACTIVITY);
- }
-
- /**
- * Tests that input events on the primary display take focus from the virtual display.
- */
- @Test
- public void testStackFocusSwitchOnTouchEvent() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- mAmWmState.computeState(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
- mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
- VIRTUAL_DISPLAY_ACTIVITY);
-
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
- mAmWmState.computeState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
- mAmWmState.assertFocusedActivity("Activity launched on secondary display must be focused",
- TEST_ACTIVITY_NAME);
-
- final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
- final int width = displayMetrics.getWidth();
- final int height = displayMetrics.getHeight();
- executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
-
- mAmWmState.computeState(new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
- mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
- VIRTUAL_DISPLAY_ACTIVITY);
- }
-
- /** Test that shell is allowed to launch on secondary displays. */
- @Test
- public void testPermissionLaunchFromShell() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
- VIRTUAL_DISPLAY_ACTIVITY);
- final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
- assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
- focusedStack.mDisplayId);
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Focus must be on secondary display",
- TEST_ACTIVITY_NAME);
- final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
- assertEquals("Focused stack must be on secondary display", newDisplay.mId,
- focusedStack.mDisplayId);
-
- // Launch other activity with different uid and check it is launched on dynamic stack on
- // secondary display.
- final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
- + " --display " + newDisplay.mId;
- executeShellCommand(startCmd);
-
- mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
- mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
- assertEquals("Activity launched by system must be on external display",
- externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
- }
-
- /** Test that launching from app that is on external display is allowed. */
- @Test
- public void testPermissionLaunchFromAppOnSecondary() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
- .build();
-
- // Launch activity with different uid on secondary display.
- final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
- + " --display " + newDisplay.mId;
- executeShellCommand(startCmd);
-
- mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
- mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
- final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(externalFocusedStackId);
- assertEquals("Focused stack must be on secondary display", newDisplay.mId,
- focusedStack.mDisplayId);
-
- // Launch another activity with third different uid from app on secondary display and check
- // it is launched on secondary display.
- final String targetActivity = " --es target_activity " + THIRD_ACTIVITY.getShortClassName()
- + " --es package_name " + THIRD_ACTIVITY.getPackageName()
- + " --ei display_id " + newDisplay.mId;
- final String includeStoppedPackagesFlag = " -f 0x00000020";
- executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
- + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
- + targetActivity + includeStoppedPackagesFlag);
-
- mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
- mAmWmState.assertFocusedActivity("Focus must be on newly launched app", THIRD_ACTIVITY);
- assertEquals("Activity launched by app on secondary display must be on that display",
- externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
- }
-
- /** Tests that an activity can launch an activity from a different UID into its own task. */
- @Test
- public void testPermissionLaunchMultiUidTask() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
- .build();
-
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
- // Check that the first activity is launched onto the secondary display
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- ActivityManagerState.ActivityStack frontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity launched on secondary display must be resumed",
- getActivityComponentName(LAUNCHING_ACTIVITY),
- frontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- // Launch an activity from a different UID into the first activity's task
- getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY)
- .execute();
-
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
- frontStack = mAmWmState.getAmState().getStackById(frontStackId);
- mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
- assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
- }
-
- /**
- * Test that launching from display owner is allowed even when the the display owner
- * doesn't have anything on the display.
- */
- @Test
- public void testPermissionLaunchFromOwner() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
- VIRTUAL_DISPLAY_ACTIVITY);
- final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
- assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
- focusedStack.mDisplayId);
-
- // Launch other activity with different uid on secondary display.
- final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
- + " --display " + newDisplay.mId;
- executeShellCommand(startCmd);
-
- mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
- mAmWmState.assertFocusedActivity("Focus must be on newly launched app", SECOND_ACTIVITY);
- final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
- assertEquals("Focused stack must be on secondary display", newDisplay.mId,
- focusedStack.mDisplayId);
-
- // Check that owner uid can launch its own activity on secondary display.
- final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
- executeShellCommand("am broadcast -a " + broadcastAction + " -p " + componentName
- + " --ez launch_activity true --ez new_task true --ez multiple_task true"
- + " --ei display_id " + newDisplay.mId);
-
- mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
- mAmWmState.assertFocusedActivity("Focus must be on newly launched app", TEST_ACTIVITY_NAME);
- assertEquals("Activity launched by owner must be on external display",
- externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
- }
-
- /**
- * Test that launching from app that is not present on external display and doesn't own it to
- * that external display is not allowed.
- */
- @Test
- public void testPermissionLaunchFromDifferentApp() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
- VIRTUAL_DISPLAY_ACTIVITY);
- final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
- assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
- focusedStack.mDisplayId);
-
- // Launch activity on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Focus must be on secondary display",
- TEST_ACTIVITY_NAME);
- final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
- focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
- assertEquals("Focused stack must be on secondary display", newDisplay.mId,
- focusedStack.mDisplayId);
-
- final String logSeparator = clearLogcat();
-
- // Launch other activity with different uid and check security exception is triggered.
- final String includeStoppedPackagesFlag = " -f 0x00000020";
- executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
- + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
- + " --ei display_id " + newDisplay.mId
- + includeStoppedPackagesFlag);
-
- assertSecurityException("LaunchBroadcastReceiver", logSeparator);
-
- mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
- new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
- mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY_NAME);
- assertEquals("Focused stack must be on secondary display's stack",
- externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
- }
-
- private void assertSecurityException(String component, String logSeparator) throws Exception {
- int tries = 0;
- boolean match = false;
- final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
- while (tries < 5 && !match) {
- String[] logs = getDeviceLogsForComponent(component, logSeparator);
- for (String line : logs) {
- Matcher m = pattern.matcher(line);
- if (m.matches()) {
- match = true;
- break;
- }
- }
- tries++;
- try {
- Thread.sleep(500);
- } catch (InterruptedException e) {
- }
- }
-
- assertTrue("Expected exception not found", match);
- }
-
- /**
- * Test that only private virtual display can show content with insecure keyguard.
- */
- @Test
- public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Try to create new show-with-insecure-keyguard public virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this)
- .setPublicDisplay(true)
- .setCanShowWithInsecureKeyguard(true)
- .setMustBeCreated(false)
- .build();
-
- // Check that the display is not created.
- assertNull(newDisplay);
- }
-
- /**
- * Test that all activities that were on the private display are destroyed on display removal.
- */
- @FlakyTest(bugId = 63404575)
- @Presubmit
- @Test
- public void testContentDestroyOnDisplayRemoved() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new private virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
- // Launch activities on new secondary display.
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertFocusedActivity("Launched activity must be focused", TEST_ACTIVITY_NAME);
- launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertFocusedActivity("Launched activity must be focused",
- RESIZEABLE_ACTIVITY_NAME);
-
- // Destroy the display and check if activities are removed from system.
- final String logSeparator = clearLogcat();
- destroyVirtualDisplays();
- final String activityName1
- = ActivityManagerTestBase.getActivityComponentName(TEST_ACTIVITY_NAME);
- final String activityName2
- = ActivityManagerTestBase.getActivityComponentName(RESIZEABLE_ACTIVITY_NAME);
- final String windowName1
- = ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
- final String windowName2
- = ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
- mAmWmState.waitForWithAmState(
- (state) -> !state.containsActivity(activityName1)
- && !state.containsActivity(activityName2),
- "Waiting for activity to be removed");
- mAmWmState.waitForWithWmState(
- (state) -> !state.containsWindow(windowName1)
- && !state.containsWindow(windowName2),
- "Waiting for activity window to be gone");
-
- // Check AM state.
- assertFalse("Activity from removed display must be destroyed",
- mAmWmState.getAmState().containsActivity(activityName1));
- assertFalse("Activity from removed display must be destroyed",
- mAmWmState.getAmState().containsActivity(activityName2));
- // Check WM state.
- assertFalse("Activity windows from removed display must be destroyed",
- mAmWmState.getWmState().containsWindow(windowName1));
- assertFalse("Activity windows from removed display must be destroyed",
- mAmWmState.getWmState().containsWindow(windowName2));
- // Check activity logs.
- assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
- assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
- }
-
- /**
- * Test that the update of display metrics updates all its content.
- */
- @Presubmit
- @Test
- public void testDisplayResize() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
- // Launch a resizeable activity on new secondary display.
- final String initialLogSeparator = clearLogcat();
- launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
- mAmWmState.assertFocusedActivity("Launched activity must be focused",
- RESIZEABLE_ACTIVITY_NAME);
-
- // Grab reported sizes and compute new with slight size change.
- final ReportedSizes initialSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
- initialLogSeparator);
-
- // Resize the docked stack, so that activity with virtual display will also be resized.
- final String logSeparator = clearLogcat();
- executeShellCommand(getResizeVirtualDisplayCommand());
-
- mAmWmState.waitForWithAmState(amState -> {
- try {
- return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
- && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
- } catch (Exception e) {
- logE("Error waiting for valid state: " + e.getMessage());
- return false;
- }
- }, "Wait for the configuration change to happen and for activity to be resumed.");
-
- mAmWmState.computeState(false /* compareTaskAndStackBounds */,
- new WaitForValidActivityState.Builder(RESIZEABLE_ACTIVITY_NAME).build(),
- new WaitForValidActivityState.Builder(VIRTUAL_DISPLAY_ACTIVITY).build());
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
- mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
-
- // Check if activity in virtual display was resized properly.
- assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
- 1 /* numConfigChange */, logSeparator);
-
- final ReportedSizes updatedSize = getLastReportedSizesForActivity(RESIZEABLE_ACTIVITY_NAME,
- logSeparator);
- assertTrue(updatedSize.widthDp <= initialSize.widthDp);
- assertTrue(updatedSize.heightDp <= initialSize.heightDp);
- assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
- assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
- }
-
- /** Read the number of configuration changes sent to activity from logs. */
- private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
- return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
- }
-
- /**
- * Tests that when an activity is launched with displayId specified and there is an existing
- * matching task on some other display - that task will moved to the target display.
- */
- @Test
- public void testMoveToDisplayOnLaunch() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Launch activity with unique affinity, so it will the only one in its task.
- launchActivity(LAUNCHING_ACTIVITY);
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
- // Launch something to that display so that a new stack is created. We need this to be able
- // to compare task numbers in stacks later.
- launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
-
- final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
- .mStacks.size();
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
- .getTasks().size();
-
- // Launch activity on new secondary display.
- // Using custom command here, because normally we add flags Intent#FLAG_ACTIVITY_NEW_TASK
- // and Intent#FLAG_ACTIVITY_MULTIPLE_TASK when launching on some specific display. We don't
- // do it here as we want an existing task to be used.
- final String launchCommand = "am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY)
- + " --display " + newDisplay.mId;
- executeShellCommand(launchCommand);
- mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
-
- // Check that activity is brought to front.
- mAmWmState.assertFocusedActivity("Existing task must be brought to front",
- LAUNCHING_ACTIVITY);
- mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
-
- // Check that activity is on the right display.
- final ActivityManagerState.ActivityStack firstFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity must be moved to the secondary display",
- getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- // Check that task has moved from primary display to secondary.
- final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
- .mStacks.size();
- assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
- stackNumFinal);
- final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
- .getTasks().size();
- assertEquals("Task number in stack on external display must be incremented.",
- taskNumOnSecondary + 1, taskNumFinalOnSecondary);
- }
-
- /**
- * Tests that when an activity is launched with displayId specified and there is an existing
- * matching task on some other display - that task will moved to the target display.
- */
- @Test
- public void testMoveToEmptyDisplayOnLaunch() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Launch activity with unique affinity, so it will the only one in its task.
- launchActivity(LAUNCHING_ACTIVITY);
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
- final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
- .mStacks.size();
-
- // Launch activity on new secondary display.
- // Using custom command here, because normally we add flags Intent#FLAG_ACTIVITY_NEW_TASK
- // and Intent#FLAG_ACTIVITY_MULTIPLE_TASK when launching on some specific display. We don't
- // do it here as we want an existing task to be used.
- final String launchCommand = "am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY)
- + " --display " + newDisplay.mId;
- executeShellCommand(launchCommand);
- mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
-
- // Check that activity is brought to front.
- mAmWmState.assertFocusedActivity("Existing task must be brought to front",
- LAUNCHING_ACTIVITY);
- mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
-
- // Check that activity is on the right display.
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack firstFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity must be moved to the secondary display",
- getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- // Check that task has moved from primary display to secondary.
- final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
- .mStacks.size();
- assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
- stackNumFinal);
- }
-
- /**
- * Tests that when primary display is rotated secondary displays are not affected.
- */
- @Test
- public void testRotationNotAffectingSecondaryScreen() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this)
- .setResizeDisplay(false)
- .build();
- mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
-
- // Launch activity on new secondary display.
- String logSeparator = clearLogcat();
- launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.assertFocusedActivity("Focus must be on secondary display",
- RESIZEABLE_ACTIVITY_NAME);
- final ReportedSizes initialSizes = getLastReportedSizesForActivity(
- RESIZEABLE_ACTIVITY_NAME, logSeparator);
- assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
-
- try (final RotationSession rotationSession = new RotationSession()) {
- // Rotate primary display and check that activity on secondary display is not affected.
- rotateAndCheckSameSizes(rotationSession, RESIZEABLE_ACTIVITY_NAME);
-
- // Launch activity to secondary display when primary one is rotated.
- final int initialRotation = mAmWmState.getWmState().getRotation();
- rotationSession.set((initialRotation + 1) % 4);
-
- logSeparator = clearLogcat();
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
- mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
- mAmWmState.assertFocusedActivity("Focus must be on secondary display",
- TEST_ACTIVITY_NAME);
- final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
- TEST_ACTIVITY_NAME, logSeparator);
- assertEquals(
- "Sizes of secondary display must not change after rotation of primary display",
- initialSizes, testActivitySizes);
- }
- }
-
- private void rotateAndCheckSameSizes(RotationSession rotationSession, String activityName)
- throws Exception {
- for (int rotation = 3; rotation >= 0; --rotation) {
- final String logSeparator = clearLogcat();
- rotationSession.set(rotation);
- final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
- logSeparator);
- assertNull("Sizes must not change after rotation", rotatedSizes);
- }
- }
-
- /**
- * Tests that task affinity does affect what display an activity is launched on but that
- * matching the task component root does.
- */
- @Test
- public void testTaskMatchAcrossDisplays() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
-
- // Check that activity is on the secondary display.
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack firstFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity launched on secondary display must be resumed",
- getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
- mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
- new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
-
- // Check that second activity gets launched on the default display despite
- // the affinity match on the secondary display.
- final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
- DEFAULT_DISPLAY_ID);
- final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
- mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
- assertEquals("Activity launched on default display must be resumed",
- getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
- defaultDisplayFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on primary display",
- defaultDisplayFrontStackId);
-
- executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
- mAmWmState.waitForFocusedStack(frontStackId);
-
- // Check that the third intent is redirected to the first task due to the root
- // component match on the secondary display.
- final ActivityManagerState.ActivityStack secondFrontStack
- = mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity launched on secondary display must be resumed",
- getActivityComponentName(LAUNCHING_ACTIVITY), secondFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
- assertEquals("Focused stack must only contain 1 task",
- 1, secondFrontStack.getTasks().size());
- assertEquals("Focused task must only contain 1 activity",
- 1, secondFrontStack.getTasks().get(0).mActivities.size());
- }
-
- /**
- * Tests that the task affinity search respects the launch display id.
- */
- @Test
- public void testLaunchDisplayAffinityMatch() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).build();
-
- launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
-
- // Check that activity is on the secondary display.
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack firstFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity launched on secondary display must be resumed",
- getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
- executeShellCommand("am start -n "
- + getActivityComponentName(ALT_LAUNCHING_ACTIVITY)
- + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
- + " --display " + newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(ALT_LAUNCHING_ACTIVITY).build());
-
- // Check that second activity gets launched into the affinity matching
- // task on the secondary display
- final int secondFrontStackId =
- mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack secondFrontStack =
- mAmWmState.getAmState().getStackById(secondFrontStackId);
- assertEquals("Activity launched on secondary display must be resumed",
- getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
- secondFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display",
- secondFrontStackId);
- assertEquals("Focused stack must only contain 1 task",
- 1, secondFrontStack.getTasks().size());
- assertEquals("Focused task must contain 2 activities",
- 2, secondFrontStack.getTasks().get(0).mActivities.size());
- }
-
- /**
- * Tests than a new task launched by an activity will end up on that activity's display
- * even if the focused stack is not on that activity's display.
- */
- @Test
- public void testNewTaskSameDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this).setSimulateDisplay(true)
- .build();
-
- launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(BROADCAST_RECEIVER_ACTIVITY).build());
-
- // Check that the first activity is launched onto the secondary display
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack firstFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity launched on secondary display must be resumed",
- getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
- firstFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
-
- executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
- mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
- new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME).build());
-
- // Check that the second activity is launched on the default display
- final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
- final ActivityManagerState.ActivityStack focusedStack
- = mAmWmState.getAmState().getStackById(focusedStackId);
- assertEquals("Activity launched on default display must be resumed",
- getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
- assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
- focusedStack.mDisplayId);
-
- executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
- + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
-
- // Check that the third activity ends up in a new task in the same stack as the
- // first activity
- mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
- new WaitForValidActivityState.Builder(LAUNCHING_ACTIVITY).build());
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
- final ActivityManagerState.ActivityStack secondFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity must be launched on secondary display",
- getActivityComponentName(LAUNCHING_ACTIVITY),
- secondFrontStack.mResumedActivity);
- assertEquals("Secondary display must contain 2 tasks",
- 2, secondFrontStack.getTasks().size());
- }
-
- /**
* Test that display overrides apply correctly and won't be affected by display changes.
* This sets overrides to display size and density, initiates a display changed event by locking
* and unlocking the phone and verifies that overrides are kept.
@@ -1741,401 +120,60 @@
public void testForceDisplayMetrics() throws Exception {
launchHomeActivity();
- // Read initial sizes.
- final ReportedDisplayMetrics originalDisplayMetrics = getDisplayMetrics();
+ try (final DisplayMetricsSession displayMetricsSession = new DisplayMetricsSession()) {
+ // Read initial sizes.
+ final ReportedDisplayMetrics originalDisplayMetrics =
+ displayMetricsSession.getInitialDisplayMetrics();
- // Apply new override values that don't match the physical metrics.
- final int overrideWidth = (int) (originalDisplayMetrics.physicalWidth * 1.5);
- final int overrideHeight = (int) (originalDisplayMetrics.physicalHeight * 1.5);
- executeShellCommand(WM_SIZE + " " + overrideWidth + "x" + overrideHeight);
- final int overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
- executeShellCommand(WM_DENSITY + " " + overrideDensity);
+ // Apply new override values that don't match the physical metrics.
+ final Size overrideSize = new Size(
+ (int) (originalDisplayMetrics.physicalSize.getWidth() * 1.5),
+ (int) (originalDisplayMetrics.physicalSize.getHeight() * 1.5));
+ final Integer overrideDensity = (int) (originalDisplayMetrics.physicalDensity * 1.1);
+ displayMetricsSession.overrideDisplayMetrics(overrideSize, overrideDensity);
- // Check if overrides applied correctly.
- ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
- assertEquals(overrideWidth, displayMetrics.overrideWidth);
- assertEquals(overrideHeight, displayMetrics.overrideHeight);
- assertEquals(overrideDensity, displayMetrics.overrideDensity);
+ // Check if overrides applied correctly.
+ ReportedDisplayMetrics displayMetrics = displayMetricsSession.getDisplayMetrics();
+ assertEquals(overrideSize, displayMetrics.overrideSize);
+ assertEquals(overrideDensity, displayMetrics.overrideDensity);
- // Lock and unlock device. This will cause a DISPLAY_CHANGED event to be triggered and
- // might update the metrics.
- sleepDevice();
- wakeUpAndUnlockDevice();
- mAmWmState.waitForHomeActivityVisible();
+ // Lock and unlock device. This will cause a DISPLAY_CHANGED event to be triggered and
+ // might update the metrics.
+ sleepDevice();
- // Check if overrides are still applied.
- displayMetrics = getDisplayMetrics();
- assertEquals(overrideWidth, displayMetrics.overrideWidth);
- assertEquals(overrideHeight, displayMetrics.overrideHeight);
- assertEquals(overrideDensity, displayMetrics.overrideDensity);
+ wakeUpAndUnlockDevice();
+ mAmWmState.waitForHomeActivityVisible();
- // All overrides will be cleared in tearDown.
- }
-
- /**
- * Tests than an immediate launch after new display creation is handled correctly.
- */
- @Test
- public void testImmediateLaunchOnNewDisplay() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Create new virtual display and immediately launch an activity on it.
- final ActivityDisplay newDisplay = new VirtualDisplayBuilder(this)
- .setLaunchActivity(TEST_ACTIVITY_NAME).build();
-
- // Check that activity is launched and placed correctly.
- mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
- mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
- TEST_ACTIVITY_NAME);
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
- final ActivityManagerState.ActivityStack firstFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals("Activity launched on secondary display must be resumed",
- getActivityComponentName(TEST_ACTIVITY_NAME), firstFrontStack.mResumedActivity);
- mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
- }
-
- /**
- * Tests that turning the primary display off does not affect the activity running
- * on an external secondary display.
- */
- @Test
- public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Launch something on the primary display so we know there is a resumed activity there
- launchActivity(RESIZEABLE_ACTIVITY_NAME);
- waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
- "Activity launched on primary display must be resumed");
-
- final ActivityDisplay newDisplay = createExternalVirtualDisplay(
- true /* showContentWhenLocked */);
-
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
- // Check that the activity is launched onto the external display
- waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
- "Activity launched on external display must be resumed");
-
- setPrimaryDisplayState(false);
-
- // Wait for the fullscreen stack to start sleeping, and then make sure the
- // test activity is still resumed.
- waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
- "Activity launched on primary display must be stopped after turning off");
- waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
- "Activity launched on external display must be resumed");
- }
-
- /**
- * Tests that an activity can be launched on a secondary display while the primary
- * display is off.
- */
- @Test
- public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Launch something on the primary display so we know there is a resumed activity there
- launchActivity(RESIZEABLE_ACTIVITY_NAME);
- waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
- "Activity launched on primary display must be resumed");
-
- setPrimaryDisplayState(false);
-
- // Make sure there is no resumed activity when the primary display is off
- waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
- "Activity launched on primary display must be stopped after turning off");
- assertEquals("Unexpected resumed activity",
- 0, mAmWmState.getAmState().getResumedActivitiesCount());
-
- final ActivityDisplay newDisplay = createExternalVirtualDisplay(
- true /* showContentWhenLocked */);
-
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
- // Check that the test activity is resumed on the external display
- waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
- "Activity launched on external display must be resumed");
- }
-
- /**
- * Tests that turning the secondary display off stops activities running on that display.
- */
- @Test
- public void testExternalDisplayToggleState() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- final ActivityDisplay newDisplay = createExternalVirtualDisplay(
- false /* showContentWhenLocked */);
-
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
- // Check that the test activity is resumed on the external display
- waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
- "Activity launched on external display must be resumed");
-
- mExternalDisplayHelper.turnDisplayOff();
-
- // Check that turning off the external display stops the activity
- waitAndAssertActivityStopped(TEST_ACTIVITY_NAME,
- "Activity launched on external display must be stopped after turning off");
-
- mExternalDisplayHelper.turnDisplayOn();
-
- // Check that turning on the external display resumes the activity
- waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
- "Activity launched on external display must be resumed");
- }
-
- /**
- * Tests that tapping on the primary display after showing the keyguard resumes the
- * activity on the primary display.
- */
- @Test
- public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- // Launch something on the primary display so we know there is a resumed activity there
- launchActivity(RESIZEABLE_ACTIVITY_NAME);
- waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
- "Activity launched on primary display must be resumed");
-
- sleepDevice();
-
- // Make sure there is no resumed activity when the primary display is off
- waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
- "Activity launched on primary display must be stopped after turning off");
- assertEquals("Unexpected resumed activity",
- 0, mAmWmState.getAmState().getResumedActivitiesCount());
-
- final ActivityDisplay newDisplay = createExternalVirtualDisplay(
- true /* showContentWhenLocked */);
-
- launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
-
- // Check that the test activity is resumed on the external display
- waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
- "Activity launched on external display must be resumed");
-
- // Unlock the device and tap on the middle of the primary display
- wakeUpDevice();
- executeShellCommand("wm dismiss-keyguard");
- mAmWmState.waitForKeyguardGone();
- mAmWmState.waitForValidState(new WaitForValidActivityState.Builder(TEST_ACTIVITY_NAME)
- .build());
- final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
- final int width = displayMetrics.getWidth();
- final int height = displayMetrics.getHeight();
- executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
-
- // Check that the activity on the primary display is resumed
- waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
- "Activity launched on primary display must be resumed");
- assertEquals("Unexpected resumed activity",
- 1, mAmWmState.getAmState().getResumedActivitiesCount());
- }
-
- private void waitAndAssertActivityResumed(String activityName, int displayId, String message)
- throws Exception {
- mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
-
- final String fullActivityName = getActivityComponentName(activityName);
- assertEquals(message, fullActivityName, mAmWmState.getAmState().getResumedActivity());
- final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
- ActivityManagerState.ActivityStack firstFrontStack =
- mAmWmState.getAmState().getStackById(frontStackId);
- assertEquals(message, fullActivityName, firstFrontStack.mResumedActivity);
- assertTrue(message,
- mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
- mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
- mAmWmState.assertVisibility(activityName, true /* visible */);
- }
-
- private void waitAndAssertActivityStopped(String activityName, String message)
- throws Exception {
- mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
-
- assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName,
- STATE_STOPPED));
- }
-
- /**
- * Tests that showWhenLocked works on a secondary display.
- */
- public void testSecondaryDisplayShowWhenLocked() throws Exception {
- assumeTrue(supportsMultiDisplay());
-
- try {
- setLockCredential();
-
- launchActivity(TEST_ACTIVITY_NAME);
-
- final ActivityDisplay newDisplay = createExternalVirtualDisplay(
- false /* showContentWhenLocked */);
- launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mId);
-
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
-
- mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
- mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
-
- mAmWmState.computeState(new String[] { SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME });
- assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
- .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED));
- } finally {
- tearDownLockCredentials();
+ // Check if overrides are still applied.
+ displayMetrics = displayMetricsSession.getDisplayMetrics();
+ assertEquals(overrideSize, displayMetrics.overrideSize);
+ assertEquals(overrideDensity, displayMetrics.overrideDensity);
}
}
- /** Get physical and override display metrics from WM. */
- private ReportedDisplayMetrics getDisplayMetrics() throws Exception {
- mDumpLines.clear();
- final String dump = executeShellCommand(WM_SIZE)
- + executeShellCommand(WM_DENSITY);
- mDumpLines.clear();
- Collections.addAll(mDumpLines, dump.split("\\n"));
- return ReportedDisplayMetrics.create(mDumpLines);
- }
+ private static class DisplayMetricsSession implements AutoCloseable {
- private static class ReportedDisplayMetrics {
- private static final Pattern sPhysicalSizePattern =
- Pattern.compile("Physical size: (\\d+)x(\\d+)");
- private static final Pattern sOverrideSizePattern =
- Pattern.compile("Override size: (\\d+)x(\\d+)");
- private static final Pattern sPhysicalDensityPattern =
- Pattern.compile("Physical density: (\\d+)");
- private static final Pattern sOverrideDensityPattern =
- Pattern.compile("Override density: (\\d+)");
+ private final ReportedDisplayMetrics mInitialDisplayMetrics;
- int physicalWidth;
- int physicalHeight;
- int physicalDensity;
-
- boolean sizeOverrideSet;
- int overrideWidth;
- int overrideHeight;
- boolean densityOverrideSet;
- int overrideDensity;
-
- /** Get width that WM operates with. */
- int getWidth() {
- return sizeOverrideSet ? overrideWidth : physicalWidth;
+ DisplayMetricsSession() throws Exception {
+ mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics();
}
- /** Get height that WM operates with. */
- int getHeight() {
- return sizeOverrideSet ? overrideHeight : physicalHeight;
+ ReportedDisplayMetrics getInitialDisplayMetrics() {
+ return mInitialDisplayMetrics;
}
- /** Get density that WM operates with. */
- int getDensity() {
- return densityOverrideSet ? overrideDensity : physicalDensity;
+ ReportedDisplayMetrics getDisplayMetrics() throws Exception {
+ return ReportedDisplayMetrics.getDisplayMetrics();
}
- static ReportedDisplayMetrics create(LinkedList<String> dump) {
- final ReportedDisplayMetrics result = new ReportedDisplayMetrics();
-
- boolean physicalSizeFound = false;
- boolean physicalDensityFound = false;
-
- while (!dump.isEmpty()) {
- final String line = dump.pop().trim();
-
- Matcher matcher = sPhysicalSizePattern.matcher(line);
- if (matcher.matches()) {
- physicalSizeFound = true;
- log(line);
- result.physicalWidth = Integer.parseInt(matcher.group(1));
- result.physicalHeight = Integer.parseInt(matcher.group(2));
- continue;
- }
-
- matcher = sOverrideSizePattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- result.overrideWidth = Integer.parseInt(matcher.group(1));
- result.overrideHeight = Integer.parseInt(matcher.group(2));
- result.sizeOverrideSet = true;
- continue;
- }
-
- matcher = sPhysicalDensityPattern.matcher(line);
- if (matcher.matches()) {
- physicalDensityFound = true;
- log(line);
- result.physicalDensity = Integer.parseInt(matcher.group(1));
- continue;
- }
-
- matcher = sOverrideDensityPattern.matcher(line);
- if (matcher.matches()) {
- log(line);
- result.overrideDensity = Integer.parseInt(matcher.group(1));
- result.densityOverrideSet = true;
- continue;
- }
- }
-
- assertTrue("Physical display size must be reported", physicalSizeFound);
- assertTrue("Physical display density must be reported", physicalDensityFound);
-
- return result;
+ void overrideDisplayMetrics(final Size size, final int density) {
+ mInitialDisplayMetrics.setDisplayMetrics(size, density);
}
- }
- /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
- private void assertMovedToDisplay(String componentName, String logSeparator) throws Exception {
- final ActivityLifecycleCounts lifecycleCounts
- = new ActivityLifecycleCounts(componentName, logSeparator);
- if (lifecycleCounts.mDestroyCount != 0) {
- fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
- + " time(s), wasn't expecting any");
- } else if (lifecycleCounts.mCreateCount != 0) {
- fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount
- + " time(s), wasn't expecting any");
- } else if (lifecycleCounts.mConfigurationChangedCount != 1) {
- fail(componentName + " has received "
- + lifecycleCounts.mConfigurationChangedCount
- + " onConfigurationChanged() calls, expecting " + 1);
- } else if (lifecycleCounts.mMovedToDisplayCount != 1) {
- fail(componentName + " has received "
- + lifecycleCounts.mMovedToDisplayCount
- + " onMovedToDisplay() calls, expecting " + 1);
+ @Override
+ public void close() throws Exception {
+ mInitialDisplayMetrics.restoreDisplayMetrics();
}
}
-
- private static String getResizeVirtualDisplayCommand() {
- return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
- " --es command resize_display";
- }
-
- /**
- * Creates a private virtual display with the external and show with insecure
- * keyguard flags set.
- */
- private ActivityDisplay createExternalVirtualDisplay(boolean showContentWhenLocked)
- throws Exception {
- final List<ActivityDisplay> originalDS = getDisplaysStates();
- final int originalDisplayCount = originalDS.size();
-
- mExternalDisplayHelper = new DisplayHelper();
- mExternalDisplayHelper.createAndWaitForDisplay(true /* external */, showContentWhenLocked);
-
- // Wait for the virtual display to be created and get configurations.
- final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
- assertEquals("New virtual display must be created", originalDisplayCount + 1, ds.size());
-
- // Find the newly added display.
- final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
- return newDisplays.get(0);
- }
-
- /** Turns the primary display on/off by pressing the power key */
- private void setPrimaryDisplayState(boolean wantOn) {
- // Either KeyEvent.KEYCODE_WAKEUP or KeyEvent.KEYCODE_SLEEP
- int keycode = wantOn ? 224 : 223;
- executeShellCommand("input keyevent " + keycode);
- DisplayHelper.waitForDefaultDisplayState(wantOn);
- }
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
new file mode 100644
index 0000000..5ea0d083
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerMultiDisplayTests.java
@@ -0,0 +1,1800 @@
+/*
+ * 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
+ */
+
+package android.server.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+import static android.server.am.ActivityManagerDisplayTestBase.ReportedDisplayMetrics
+ .getDisplayMetrics;
+import static android.server.am.ActivityManagerState.STATE_RESUMED;
+import static android.server.am.ActivityManagerState.STATE_STOPPED;
+import static android.server.am.StateLogger.logE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.ComponentName;
+import android.platform.test.annotations.Presubmit;
+import android.server.am.ActivityManagerState.ActivityDisplay;
+import android.server.am.displayservice.DisplayHelper;
+import android.support.annotation.Nullable;
+import android.support.test.filters.FlakyTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Build/Install/Run:
+ * atest CtsActivityManagerDeviceTestCases:ActivityManagerMultiDisplayTests
+ */
+@FlakyTest(bugId = 71792368, detail = "Might be affecting tests that run after. See bug for details")
+public class ActivityManagerMultiDisplayTests extends ActivityManagerDisplayTestBase {
+ private static final String TEST_ACTIVITY_NAME = "TestActivity";
+ private static final String VIRTUAL_DISPLAY_ACTIVITY = "VirtualDisplayActivity";
+ private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
+ private static final String NON_RESIZEABLE_ACTIVITY_NAME = "NonResizeableActivity";
+ private static final String SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME = "ShowWhenLockedAttrActivity";
+ private static final String SECOND_PACKAGE = "android.server.am.second";
+ private static final String THIRD_PACKAGE = "android.server.am.third";
+ private static final ComponentName SECOND_ACTIVITY = ComponentName.createRelative(
+ SECOND_PACKAGE, ".SecondActivity");
+ private static final ComponentName SECOND_NO_EMBEDDING_ACTIVITY = ComponentName.createRelative(
+ SECOND_PACKAGE, ".SecondActivityNoEmbedding");
+ private static final ComponentName LAUNCH_BROADCAST_RECEIVER = ComponentName.createRelative(
+ SECOND_PACKAGE, ".LaunchBroadcastReceiver");
+ /** See AndroidManifest.xml of appSecondUid. */
+ private static final String LAUNCH_BROADCAST_ACTION =
+ SECOND_PACKAGE + ".LAUNCH_BROADCAST_ACTION";
+ private static final ComponentName THIRD_ACTIVITY = ComponentName.createRelative(
+ THIRD_PACKAGE, ".ThirdActivity");
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ assumeTrue(supportsMultiDisplay());
+ }
+
+ /**
+ * Tests launching an activity on virtual display.
+ */
+ @Presubmit
+ @Test
+ public void testLaunchActivityOnSecondaryDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Launch activity on new secondary display.
+ final String logSeparator = clearLogcat();
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ TEST_ACTIVITY_NAME);
+
+ // Check that activity is on the right display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be on the secondary display and resumed",
+ getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ // Check that activity config corresponds to display config.
+ final ReportedSizes reportedSizes = getLastReportedSizesForActivity(TEST_ACTIVITY_NAME,
+ logSeparator);
+ assertEquals("Activity launched on secondary display must have proper configuration",
+ CUSTOM_DENSITY_DPI, reportedSizes.densityDpi);
+ }
+ }
+
+ /**
+ * Tests launching a non-resizeable activity on virtual display. It should land on the
+ * default display.
+ */
+ @Test
+ public void testLaunchNonResizeableActivityOnSecondaryDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ NON_RESIZEABLE_ACTIVITY_NAME);
+
+ // Check that activity is on the right display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be on the primary display and resumed",
+ getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+ frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+ }
+ }
+
+ /**
+ * Tests launching a non-resizeable activity on virtual display while split-screen is active
+ * on the primary display. It should land on the primary display and dismiss docked stack.
+ */
+ @Test
+ public void testLaunchNonResizeableActivityWithSplitScreen() throws Exception {
+ assumeTrue(supportsSplitScreenMultiWindow());
+
+ // Start launching activity.
+ launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.setLaunchInSplitScreen(true)
+ .createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(NON_RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ NON_RESIZEABLE_ACTIVITY_NAME);
+
+ // Check that activity is on the right display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be on the primary display and resumed",
+ getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+ frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+ mAmWmState.assertDoesNotContainStack("Must not contain docked stack.",
+ WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD);
+ }
+ }
+
+ /**
+ * Tests moving a non-resizeable activity to a virtual display. It should stay on the default
+ * display with no action performed.
+ */
+ @Test
+ public void testMoveNonResizeableActivityToSecondaryDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ // Launch a non-resizeable activity on a primary display.
+ launchActivityInNewTask(NON_RESIZEABLE_ACTIVITY_NAME);
+ // Launch a resizeable activity on new secondary display to create a new stack there.
+ launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+ final int externalFrontStackId = mAmWmState.getAmState()
+ .getFrontStackId(newDisplay.mId);
+
+ // Try to move the non-resizeable activity to new secondary display.
+ moveActivityToStack(NON_RESIZEABLE_ACTIVITY_NAME, externalFrontStackId);
+ mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ RESIZEABLE_ACTIVITY_NAME);
+
+ // Check that activity is in the same stack
+ final int defaultFrontStackId = mAmWmState.getAmState().getFrontStackId(
+ DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack defaultFrontStack =
+ mAmWmState.getAmState().getStackById(defaultFrontStackId);
+ assertEquals("Launched activity must be on the primary display and resumed",
+ getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+ defaultFrontStack.getTopTask().mRealActivity);
+ mAmWmState.assertFocusedStack("Focus must remain on the secondary display",
+ externalFrontStackId);
+ }
+ }
+
+ /**
+ * Tests launching a non-resizeable activity on virtual display from activity there. It should
+ * land on the secondary display based on the resizeability of the root activity of the task.
+ */
+ @Test
+ public void testLaunchNonResizeableActivityFromSecondaryDisplaySameTask() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new simulated display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+ .createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ BROADCAST_RECEIVER_ACTIVITY);
+
+ // Check that launching activity is on the secondary display.
+ int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be on the secondary display and resumed",
+ getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
+ frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
+
+ // Launch non-resizeable activity from secondary display.
+ executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+ + "--ez new_task true --es target_activity " + NON_RESIZEABLE_ACTIVITY_NAME);
+ mAmWmState.computeState(new WaitForValidActivityState(NON_RESIZEABLE_ACTIVITY_NAME));
+
+ // Check that non-resizeable activity is on the secondary display, because of the
+ // resizeable root of the task.
+ frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be on the primary display and resumed",
+ getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+ frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on the primary display", frontStackId);
+ }
+ }
+
+ /**
+ * Tests launching a non-resizeable activity on virtual display from activity there. It should
+ * land on some different suitable display (usually - on the default one).
+ */
+ @Test
+ public void testLaunchNonResizeableActivityFromSecondaryDisplayNewTask() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ LAUNCHING_ACTIVITY);
+
+ // Check that launching activity is on the secondary display.
+ int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be on the secondary display and resumed",
+ getActivityComponentName(LAUNCHING_ACTIVITY),
+ frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on the secondary display", frontStackId);
+
+ // Launch non-resizeable activity from secondary display.
+ getLaunchActivityBuilder().setTargetActivityName(NON_RESIZEABLE_ACTIVITY_NAME)
+ .setNewTask(true).setMultipleTask(true).execute();
+
+ // Check that non-resizeable activity is on the primary display.
+ frontStackId = mAmWmState.getAmState().getFocusedStackId();
+ frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+ assertFalse("Launched activity must be on a different display",
+ newDisplay.mId == frontStack.mDisplayId);
+ assertEquals("Launched activity must be resumed",
+ getActivityComponentName(NON_RESIZEABLE_ACTIVITY_NAME),
+ frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on a just launched activity",
+ frontStackId);
+ }
+ }
+
+ /**
+ * Tests launching an activity on a virtual display without special permission must not be
+ * allowed.
+ */
+ @Test
+ public void testLaunchWithoutPermissionOnVirtualDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ final String logSeparator = clearLogcat();
+
+ // Try to launch an activity and check it security exception was triggered.
+ final String broadcastTarget = "-a " + LAUNCH_BROADCAST_ACTION
+ + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName();
+ final String includeStoppedPackagesFlag = " -f 0x00000020";
+ executeShellCommand("am broadcast " + broadcastTarget
+ + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
+ + " --es package_name " + componentName
+ + " --ei display_id " + newDisplay.mId
+ + includeStoppedPackagesFlag);
+
+ assertSecurityException("LaunchBroadcastReceiver", logSeparator);
+
+ mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+ assertFalse("Restricted activity must not be launched",
+ mAmWmState.getAmState().containsActivity(TEST_ACTIVITY_NAME));
+ }
+ }
+
+ /**
+ * Tests launching an activity on a virtual display without special permission must be allowed
+ * for activities with same UID.
+ */
+ @Test
+ public void testLaunchWithoutPermissionOnVirtualDisplayByOwner() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Try to launch an activity and check it security exception was triggered.
+ final String broadcastTarget = "-a " + componentName + ".LAUNCH_BROADCAST_ACTION"
+ + " -p " + componentName;
+ executeShellCommand("am broadcast " + broadcastTarget
+ + " --ez launch_activity true --es target_activity " + TEST_ACTIVITY_NAME
+ + " --es package_name " + componentName
+ + " --ei display_id " + newDisplay.mId);
+
+ mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
+
+ final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ final ActivityManagerState.ActivityStack focusedStack =
+ mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+ focusedStack.mDisplayId);
+
+ mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
+ TEST_ACTIVITY_NAME);
+ assertEquals("Activity launched by owner must be on external display",
+ externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+ }
+ }
+
+ /**
+ * Tests launching an activity on virtual display and then launching another activity via shell
+ * command and without specifying the display id - the second activity must appear on the
+ * primary display.
+ */
+ @Presubmit
+ @Test
+ public void testConsequentLaunchActivity() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ TEST_ACTIVITY_NAME);
+
+ // Launch second activity without specifying display.
+ launchActivity(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+ // Check that activity is launched in focused stack on primary display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ LAUNCHING_ACTIVITY);
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack frontStack
+ = mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed in front stack",
+ getActivityComponentName(LAUNCHING_ACTIVITY), frontStack.mResumedActivity);
+ assertEquals("Front stack must be on primary display",
+ DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+ }
+ }
+
+ /**
+ * Tests launching an activity on simulated display and then launching another activity from the
+ * first one - it must appear on the secondary display, because it was launched from there.
+ */
+ @FlakyTest(bugId = 71564456)
+ @Presubmit
+ @Test
+ public void testConsequentLaunchActivityFromSecondaryDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new simulated display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+ .createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be resumed",
+ LAUNCHING_ACTIVITY);
+
+ // Launch second activity from app on secondary display without specifying display id.
+ getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
+ mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+ // Check that activity is launched in focused stack on external display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ TEST_ACTIVITY_NAME);
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack frontStack
+ = mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed in front stack",
+ getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+ }
+ }
+
+ /**
+ * Tests launching an activity on virtual display and then launching another activity from the
+ * first one - it must appear on the secondary display, because it was launched from there.
+ */
+ @Test
+ public void testConsequentLaunchActivityFromVirtualDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+ mAmWmState.computeState(LAUNCHING_ACTIVITY);
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be resumed",
+ LAUNCHING_ACTIVITY);
+
+ // Launch second activity from app on secondary display without specifying display id.
+ getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME).execute();
+ mAmWmState.computeState(TEST_ACTIVITY_NAME);
+
+ // Check that activity is launched in focused stack on external display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ TEST_ACTIVITY_NAME);
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState()
+ .getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed in front stack",
+ getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+ }
+ }
+
+ /**
+ * Tests launching an activity on virtual display and then launching another activity from the
+ * first one with specifying the target display - it must appear on the secondary display.
+ */
+ @Test
+ public void testConsequentLaunchActivityFromVirtualDisplayToTargetDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+ mAmWmState.computeState(LAUNCHING_ACTIVITY);
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be resumed",
+ LAUNCHING_ACTIVITY);
+
+ // Launch second activity from app on secondary display specifying same display id.
+ getLaunchActivityBuilder()
+ .setTargetActivity(SECOND_ACTIVITY)
+ .setDisplayId(newDisplay.mId)
+ .execute();
+ mAmWmState.computeState(TEST_ACTIVITY_NAME);
+
+ // Check that activity is launched in focused stack on external display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused", SECOND_ACTIVITY);
+ int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed in front stack",
+ SECOND_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
+
+ // Launch other activity with different uid and check if it has launched successfully.
+ getLaunchActivityBuilder()
+ .setUseBroadcastReceiver(LAUNCH_BROADCAST_RECEIVER, LAUNCH_BROADCAST_ACTION)
+ .setDisplayId(newDisplay.mId)
+ .setTargetActivity(THIRD_ACTIVITY)
+ .execute();
+ mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
+
+ // Check that activity is launched in focused stack on external display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused", THIRD_ACTIVITY);
+ frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed in front stack",
+ THIRD_ACTIVITY.flattenToShortString(), frontStack.mResumedActivity);
+ }
+ }
+
+ /**
+ * Tests launching an activity on virtual display and then launching another activity that
+ * doesn't allow embedding - it should fail with security exception.
+ */
+ @Test
+ public void testConsequentLaunchActivityFromVirtualDisplayNoEmbedding() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+ mAmWmState.computeState(LAUNCHING_ACTIVITY);
+
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be resumed",
+ LAUNCHING_ACTIVITY);
+
+ final String logSeparator = clearLogcat();
+
+ // Launch second activity from app on secondary display specifying same display id.
+ getLaunchActivityBuilder()
+ .setTargetActivity(SECOND_NO_EMBEDDING_ACTIVITY)
+ .setDisplayId(newDisplay.mId)
+ .execute();
+
+ assertSecurityException("ActivityLauncher", logSeparator);
+ }
+ }
+
+ /**
+ * Tests launching an activity to secondary display from activity on primary display.
+ */
+ @Test
+ public void testLaunchActivityFromAppToSecondaryDisplay() throws Exception {
+ // Start launching activity.
+ launchActivity(LAUNCHING_ACTIVITY);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new simulated display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+ .createDisplay();
+
+ // Launch activity on secondary display from the app on primary display.
+ getLaunchActivityBuilder().setTargetActivityName(TEST_ACTIVITY_NAME)
+ .setDisplayId(newDisplay.mId).execute();
+
+ // Check that activity is launched on external display.
+ mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ TEST_ACTIVITY_NAME);
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack frontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed in front stack",
+ getActivityComponentName(TEST_ACTIVITY_NAME), frontStack.mResumedActivity);
+ }
+ }
+
+ /**
+ * Tests launching activities on secondary and then on primary display to see if the stack
+ * visibility is not affected.
+ */
+ @Presubmit
+ @Test
+ public void testLaunchActivitiesAffectsVisibility() throws Exception {
+ // Start launching activity.
+ launchActivity(LAUNCHING_ACTIVITY);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ // Launch activity on primary display and check if it doesn't affect activity on
+ // secondary display.
+ getLaunchActivityBuilder().setTargetActivityName(RESIZEABLE_ACTIVITY_NAME).execute();
+ mAmWmState.waitForValidState(RESIZEABLE_ACTIVITY_NAME);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+ }
+ }
+
+ /**
+ * Test that move-task works when moving between displays.
+ */
+ @Presubmit
+ @Test
+ public void testMoveTaskBetweenDisplays() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+ VIRTUAL_DISPLAY_ACTIVITY);
+ final int defaultDisplayStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+ defaultDisplayStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+ TEST_ACTIVITY_NAME);
+ int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Focused stack must be on secondary display",
+ newDisplay.mId, focusedStack.mDisplayId);
+
+ // Move activity from secondary display to primary.
+ moveActivityToStack(TEST_ACTIVITY_NAME, defaultDisplayStackId);
+ mAmWmState.waitForFocusedStack(defaultDisplayStackId);
+ mAmWmState.assertFocusedActivity("Focus must be on moved activity", TEST_ACTIVITY_NAME);
+ focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Focus must return to primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
+ }
+ }
+
+ /**
+ * Tests launching activities on secondary display and then removing it to see if stack focus
+ * is moved correctly.
+ * This version launches virtual display creator to fullscreen stack in split-screen.
+ */
+ @FlakyTest(bugId = 69573940)
+ @Presubmit
+ @Test
+ public void testStackFocusSwitchOnDisplayRemoved() throws Exception {
+ assumeTrue(supportsSplitScreenMultiWindow());
+
+ // Start launching activity into docked stack.
+ launchActivityInSplitScreenWithRecents(LAUNCHING_ACTIVITY);
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+ tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ }
+
+ /**
+ * Tests launching activities on secondary display and then removing it to see if stack focus
+ * is moved correctly.
+ * This version launches virtual display creator to docked stack in split-screen.
+ */
+ @Test
+ public void testStackFocusSwitchOnDisplayRemoved2() throws Exception {
+ assumeTrue(supportsSplitScreenMultiWindow());
+
+ // Setup split-screen.
+ launchActivitiesInSplitScreen(RESIZEABLE_ACTIVITY_NAME, LAUNCHING_ACTIVITY);
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+ tryCreatingAndRemovingDisplayWithActivity(true /* splitScreen */,
+ WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+ }
+
+ /**
+ * Tests launching activities on secondary display and then removing it to see if stack focus
+ * is moved correctly.
+ * This version works without split-screen.
+ */
+ @Test
+ public void testStackFocusSwitchOnDisplayRemoved3() throws Exception {
+ // Start an activity on default display to determine default stack.
+ launchActivity(BROADCAST_RECEIVER_ACTIVITY);
+ final int focusedStackWindowingMode = mAmWmState.getAmState().getFrontStackWindowingMode(
+ DEFAULT_DISPLAY_ID);
+ // Finish probing activity.
+ executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+ tryCreatingAndRemovingDisplayWithActivity(false /* splitScreen */,
+ focusedStackWindowingMode);
+ }
+
+ /**
+ * Create a virtual display, launch a test activity there, destroy the display and check if test
+ * activity is moved to a stack on the default display.
+ */
+ private void tryCreatingAndRemovingDisplayWithActivity(boolean splitScreen, int windowingMode)
+ throws Exception {
+ String logSeparator;
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession
+ .setPublicDisplay(true)
+ .setLaunchInSplitScreen(splitScreen)
+ .createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ if (splitScreen) {
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+ }
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+ TEST_ACTIVITY_NAME);
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ // Destroy virtual display.
+ logSeparator = clearLogcat();
+ }
+
+ assertActivityLifecycle(TEST_ACTIVITY_NAME, false /* relaunched */, logSeparator);
+ mAmWmState.waitForValidState(TEST_ACTIVITY_NAME, windowingMode, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertSanity();
+ mAmWmState.assertValidBounds(true /* compareTaskAndStackBounds */);
+
+ // Check if the focus is switched back to primary display.
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ mAmWmState.assertFocusedStack(
+ "Default stack on primary display must be focused after display removed",
+ windowingMode, ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertFocusedActivity(
+ "Focus must be switched back to activity on primary display",
+ TEST_ACTIVITY_NAME);
+ }
+
+ /**
+ * Tests launching activities on secondary display and then removing it to see if stack focus
+ * is moved correctly.
+ */
+ @Test
+ public void testStackFocusSwitchOnStackEmptied() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ final int focusedStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+ BROADCAST_RECEIVER_ACTIVITY);
+
+ // Lock the device, so that activity containers will be detached.
+ sleepDevice();
+
+ // Finish activity on secondary display.
+ executeShellCommand(FINISH_ACTIVITY_BROADCAST);
+
+ // Unlock and check if the focus is switched back to primary display.
+ wakeUpAndUnlockDevice();
+ mAmWmState.waitForFocusedStack(focusedStackId);
+ mAmWmState.waitForValidState(VIRTUAL_DISPLAY_ACTIVITY);
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+ VIRTUAL_DISPLAY_ACTIVITY);
+ }
+ }
+
+ /**
+ * Tests that input events on the primary display take focus from the virtual display.
+ */
+ @Test
+ public void testStackFocusSwitchOnTouchEvent() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ mAmWmState.computeState(new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+ mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+ VIRTUAL_DISPLAY_ACTIVITY);
+
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+ mAmWmState.computeState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+ mAmWmState.assertFocusedActivity(
+ "Activity launched on secondary display must be focused",
+ TEST_ACTIVITY_NAME);
+
+ final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
+ final int width = displayMetrics.getSize().getWidth();
+ final int height = displayMetrics.getSize().getHeight();
+ executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
+
+ mAmWmState.computeState(new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+ mAmWmState.assertFocusedActivity("Focus must be switched back to primary display",
+ VIRTUAL_DISPLAY_ACTIVITY);
+ }
+ }
+
+ /** Test that shell is allowed to launch on secondary displays. */
+ @Test
+ public void testPermissionLaunchFromShell() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+ VIRTUAL_DISPLAY_ACTIVITY);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+ defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+ TEST_ACTIVITY_NAME);
+ final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+ focusedStack.mDisplayId);
+
+ // Launch other activity with different uid and check it is launched on dynamic stack on
+ // secondary display.
+ final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+ + " --display " + newDisplay.mId;
+ executeShellCommand(startCmd);
+
+ mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
+ mAmWmState.assertFocusedActivity(
+ "Focus must be on newly launched app", SECOND_ACTIVITY);
+ assertEquals("Activity launched by system must be on external display",
+ externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+ }
+ }
+
+ /** Test that launching from app that is on external display is allowed. */
+ @Test
+ public void testPermissionLaunchFromAppOnSecondary() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new simulated display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+ .createDisplay();
+
+ // Launch activity with different uid on secondary display.
+ final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+ + " --display " + newDisplay.mId;
+ executeShellCommand(startCmd);
+
+ mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
+ mAmWmState.assertFocusedActivity(
+ "Focus must be on newly launched app", SECOND_ACTIVITY);
+ final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+ focusedStack.mDisplayId);
+
+ // Launch another activity with third different uid from app on secondary display and
+ // check it is launched on secondary display.
+ final String targetActivity =
+ " --es target_activity " + THIRD_ACTIVITY.getShortClassName()
+ + " --es package_name " + THIRD_ACTIVITY.getPackageName()
+ + " --ei display_id " + newDisplay.mId;
+ final String includeStoppedPackagesFlag = " -f 0x00000020";
+ executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
+ + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
+ + targetActivity + includeStoppedPackagesFlag);
+
+ mAmWmState.waitForValidState(new WaitForValidActivityState(THIRD_ACTIVITY));
+ mAmWmState.assertFocusedActivity("Focus must be on newly launched app", THIRD_ACTIVITY);
+ assertEquals("Activity launched by app on secondary display must be on that display",
+ externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+ }
+ }
+
+ /** Tests that an activity can launch an activity from a different UID into its own task. */
+ @Test
+ public void testPermissionLaunchMultiUidTask() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+ .createDisplay();
+
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+ // Check that the first activity is launched onto the secondary display
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ ActivityManagerState.ActivityStack frontStack = mAmWmState.getAmState().getStackById(
+ frontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(LAUNCHING_ACTIVITY),
+ frontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ // Launch an activity from a different UID into the first activity's task
+ getLaunchActivityBuilder().setTargetActivity(SECOND_ACTIVITY).execute();
+
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+ frontStack = mAmWmState.getAmState().getStackById(frontStackId);
+ mAmWmState.assertFocusedActivity(
+ "Focus must be on newly launched app", SECOND_ACTIVITY);
+ assertEquals("Secondary display must contain 1 task", 1, frontStack.getTasks().size());
+ }
+ }
+
+ /**
+ * Test that launching from display owner is allowed even when the the display owner
+ * doesn't have anything on the display.
+ */
+ @Test
+ public void testPermissionLaunchFromOwner() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+ VIRTUAL_DISPLAY_ACTIVITY);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
+
+ // Launch other activity with different uid on secondary display.
+ final String startCmd = "am start -n " + SECOND_ACTIVITY.flattenToShortString()
+ + " --display " + newDisplay.mId;
+ executeShellCommand(startCmd);
+
+ mAmWmState.waitForValidState(new WaitForValidActivityState(SECOND_ACTIVITY));
+ mAmWmState.assertFocusedActivity(
+ "Focus must be on newly launched app", SECOND_ACTIVITY);
+ final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+ focusedStack.mDisplayId);
+
+ // Check that owner uid can launch its own activity on secondary display.
+ final String broadcastAction = componentName + ".LAUNCH_BROADCAST_ACTION";
+ executeShellCommand("am broadcast -a " + broadcastAction + " -p " + componentName
+ + " --ez launch_activity true --ez new_task true --ez multiple_task true"
+ + " --ei display_id " + newDisplay.mId);
+
+ mAmWmState.waitForValidState(TEST_ACTIVITY_NAME);
+ mAmWmState.assertFocusedActivity("Focus must be on newly launched app",
+ TEST_ACTIVITY_NAME);
+ assertEquals("Activity launched by owner must be on external display",
+ externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+ }
+ }
+
+ /**
+ * Test that launching from app that is not present on external display and doesn't own it to
+ * that external display is not allowed.
+ */
+ @Test
+ public void testPermissionLaunchFromDifferentApp() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ mAmWmState.assertFocusedActivity("Virtual display activity must be focused",
+ VIRTUAL_DISPLAY_ACTIVITY);
+ final int defaultDisplayFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ ActivityManagerState.ActivityStack focusedStack = mAmWmState.getAmState().getStackById(
+ defaultDisplayFocusedStackId);
+ assertEquals("Focus must remain on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
+
+ // Launch activity on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+ TEST_ACTIVITY_NAME);
+ final int externalFocusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ focusedStack = mAmWmState.getAmState().getStackById(externalFocusedStackId);
+ assertEquals("Focused stack must be on secondary display", newDisplay.mId,
+ focusedStack.mDisplayId);
+
+ final String logSeparator = clearLogcat();
+
+ // Launch other activity with different uid and check security exception is triggered.
+ final String includeStoppedPackagesFlag = " -f 0x00000020";
+ executeShellCommand("am broadcast -a " + LAUNCH_BROADCAST_ACTION
+ + " -p " + LAUNCH_BROADCAST_RECEIVER.getPackageName()
+ + " --ei display_id " + newDisplay.mId
+ + includeStoppedPackagesFlag);
+
+ assertSecurityException("LaunchBroadcastReceiver", logSeparator);
+
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+ mAmWmState.assertFocusedActivity("Focus must be on first activity", TEST_ACTIVITY_NAME);
+ assertEquals("Focused stack must be on secondary display's stack",
+ externalFocusedStackId, mAmWmState.getAmState().getFocusedStackId());
+ }
+ }
+
+ private void assertSecurityException(String component, String logSeparator) throws Exception {
+ int tries = 0;
+ boolean match = false;
+ final Pattern pattern = Pattern.compile(".*SecurityException launching activity.*");
+ while (tries < 5 && !match) {
+ String[] logs = getDeviceLogsForComponent(component, logSeparator);
+ for (String line : logs) {
+ Matcher m = pattern.matcher(line);
+ if (m.matches()) {
+ match = true;
+ break;
+ }
+ }
+ tries++;
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ }
+ }
+
+ assertTrue("Expected exception not found", match);
+ }
+
+ /**
+ * Test that only private virtual display can show content with insecure keyguard.
+ */
+ @Test
+ public void testFlagShowWithInsecureKeyguardOnPublicVirtualDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Try to create new show-with-insecure-keyguard public virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession
+ .setPublicDisplay(true)
+ .setCanShowWithInsecureKeyguard(true)
+ .setMustBeCreated(false)
+ .createDisplay();
+
+ // Check that the display is not created.
+ assertNull(newDisplay);
+ }
+ }
+
+ /**
+ * Test that all activities that were on the private display are destroyed on display removal.
+ */
+ @FlakyTest(bugId = 63404575)
+ @Presubmit
+ @Test
+ public void testContentDestroyOnDisplayRemoved() throws Exception {
+ String logSeparator;
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new private virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ // Launch activities on new secondary display.
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true /* visible */);
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ TEST_ACTIVITY_NAME);
+ launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ RESIZEABLE_ACTIVITY_NAME);
+
+ // Destroy the display and check if activities are removed from system.
+ logSeparator = clearLogcat();
+ }
+
+ final String activityName1 = ActivityManagerTestBase.getActivityComponentName(
+ TEST_ACTIVITY_NAME);
+ final String activityName2 = ActivityManagerTestBase.getActivityComponentName(
+ RESIZEABLE_ACTIVITY_NAME);
+ final String windowName1 = ActivityManagerTestBase.getWindowName(TEST_ACTIVITY_NAME);
+ final String windowName2 = ActivityManagerTestBase.getWindowName(RESIZEABLE_ACTIVITY_NAME);
+ mAmWmState.waitForWithAmState(
+ (state) -> !state.containsActivity(activityName1)
+ && !state.containsActivity(activityName2),
+ "Waiting for activity to be removed");
+ mAmWmState.waitForWithWmState(
+ (state) -> !state.containsWindow(windowName1)
+ && !state.containsWindow(windowName2),
+ "Waiting for activity window to be gone");
+
+ // Check AM state.
+ assertFalse("Activity from removed display must be destroyed",
+ mAmWmState.getAmState().containsActivity(activityName1));
+ assertFalse("Activity from removed display must be destroyed",
+ mAmWmState.getAmState().containsActivity(activityName2));
+ // Check WM state.
+ assertFalse("Activity windows from removed display must be destroyed",
+ mAmWmState.getWmState().containsWindow(windowName1));
+ assertFalse("Activity windows from removed display must be destroyed",
+ mAmWmState.getWmState().containsWindow(windowName2));
+ // Check activity logs.
+ assertActivityDestroyed(TEST_ACTIVITY_NAME, logSeparator);
+ assertActivityDestroyed(RESIZEABLE_ACTIVITY_NAME, logSeparator);
+ }
+
+ /**
+ * Test that the update of display metrics updates all its content.
+ */
+ @Presubmit
+ @Test
+ public void testDisplayResize() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ // Launch a resizeable activity on new secondary display.
+ final String initialLogSeparator = clearLogcat();
+ launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ RESIZEABLE_ACTIVITY_NAME);
+
+ // Grab reported sizes and compute new with slight size change.
+ final ReportedSizes initialSize = getLastReportedSizesForActivity(
+ RESIZEABLE_ACTIVITY_NAME,
+ initialLogSeparator);
+
+ // Resize the docked stack, so that activity with virtual display will also be resized.
+ final String logSeparator = clearLogcat();
+ executeShellCommand(getResizeVirtualDisplayCommand());
+
+ mAmWmState.waitForWithAmState(amState -> {
+ try {
+ return readConfigChangeNumber(RESIZEABLE_ACTIVITY_NAME, logSeparator) == 1
+ && amState.hasActivityState(RESIZEABLE_ACTIVITY_NAME, STATE_RESUMED);
+ } catch (Exception e) {
+ logE("Error waiting for valid state: " + e.getMessage());
+ return false;
+ }
+ }, "Wait for the configuration change to happen and for activity to be resumed.");
+
+ mAmWmState.computeState(false /* compareTaskAndStackBounds */,
+ new WaitForValidActivityState(RESIZEABLE_ACTIVITY_NAME),
+ new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true);
+ mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true);
+
+ // Check if activity in virtual display was resized properly.
+ assertRelaunchOrConfigChanged(RESIZEABLE_ACTIVITY_NAME, 0 /* numRelaunch */,
+ 1 /* numConfigChange */, logSeparator);
+
+ final ReportedSizes updatedSize = getLastReportedSizesForActivity(
+ RESIZEABLE_ACTIVITY_NAME,
+ logSeparator);
+ assertTrue(updatedSize.widthDp <= initialSize.widthDp);
+ assertTrue(updatedSize.heightDp <= initialSize.heightDp);
+ assertTrue(updatedSize.displayWidth == initialSize.displayWidth / 2);
+ assertTrue(updatedSize.displayHeight == initialSize.displayHeight / 2);
+ }
+ }
+
+ /** Read the number of configuration changes sent to activity from logs. */
+ private int readConfigChangeNumber(String activityName, String logSeparator) throws Exception {
+ return (new ActivityLifecycleCounts(activityName, logSeparator)).mConfigurationChangedCount;
+ }
+
+ /**
+ * Tests that when an activity is launched with displayId specified and there is an existing
+ * matching task on some other display - that task will moved to the target display.
+ */
+ @Test
+ public void testMoveToDisplayOnLaunch() throws Exception {
+ // Launch activity with unique affinity, so it will the only one in its task.
+ launchActivity(LAUNCHING_ACTIVITY);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+ // Launch something to that display so that a new stack is created. We need this to be
+ // able to compare task numbers in stacks later.
+ launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+
+ final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+ .mStacks.size();
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final int taskNumOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
+ .getTasks().size();
+
+ // Launch activity on new secondary display.
+ // Using custom command here, because normally we add flags
+ // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
+ // when launching on some specific display. We don't do it here as we want an existing
+ // task to be used.
+ final String launchCommand = "am start -n " + getActivityComponentName(
+ LAUNCHING_ACTIVITY)
+ + " --display " + newDisplay.mId;
+ executeShellCommand(launchCommand);
+ mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
+
+ // Check that activity is brought to front.
+ mAmWmState.assertFocusedActivity("Existing task must be brought to front",
+ LAUNCHING_ACTIVITY);
+ mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
+
+ // Check that activity is on the right display.
+ final ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity must be moved to the secondary display",
+ getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ // Check that task has moved from primary display to secondary.
+ final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+ .mStacks.size();
+ assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
+ stackNumFinal);
+ final int taskNumFinalOnSecondary = mAmWmState.getAmState().getStackById(frontStackId)
+ .getTasks().size();
+ assertEquals("Task number in stack on external display must be incremented.",
+ taskNumOnSecondary + 1, taskNumFinalOnSecondary);
+ }
+ }
+
+ /**
+ * Tests that when an activity is launched with displayId specified and there is an existing
+ * matching task on some other display - that task will moved to the target display.
+ */
+ @Test
+ public void testMoveToEmptyDisplayOnLaunch() throws Exception {
+ // Launch activity with unique affinity, so it will the only one in its task.
+ launchActivity(LAUNCHING_ACTIVITY);
+
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ final int stackNum = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+ .mStacks.size();
+
+ // Launch activity on new secondary display.
+ // Using custom command here, because normally we add flags
+ // {@link Intent#FLAG_ACTIVITY_NEW_TASK} and {@link Intent#FLAG_ACTIVITY_MULTIPLE_TASK}
+ // when launching on some specific display. We don't do it here as we want an existing
+ // task to be used.
+ final String launchCommand = "am start -n " + getActivityComponentName(
+ LAUNCHING_ACTIVITY)
+ + " --display " + newDisplay.mId;
+ executeShellCommand(launchCommand);
+ mAmWmState.waitForActivityState(LAUNCHING_ACTIVITY, STATE_RESUMED);
+
+ // Check that activity is brought to front.
+ mAmWmState.assertFocusedActivity("Existing task must be brought to front",
+ LAUNCHING_ACTIVITY);
+ mAmWmState.assertResumedActivity("Existing task must be resumed", LAUNCHING_ACTIVITY);
+
+ // Check that activity is on the right display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity must be moved to the secondary display",
+ getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ // Check that task has moved from primary display to secondary.
+ final int stackNumFinal = mAmWmState.getAmState().getDisplay(DEFAULT_DISPLAY_ID)
+ .mStacks.size();
+ assertEquals("Stack number in default stack must be decremented.", stackNum - 1,
+ stackNumFinal);
+ }
+ }
+
+ /**
+ * Tests that when primary display is rotated secondary displays are not affected.
+ */
+ @Test
+ public void testRotationNotAffectingSecondaryScreen() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display.
+ final ActivityDisplay newDisplay = virtualDisplaySession.setResizeDisplay(false)
+ .createDisplay();
+ mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
+
+ // Launch activity on new secondary display.
+ String logSeparator = clearLogcat();
+ launchActivityOnDisplay(RESIZEABLE_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+ RESIZEABLE_ACTIVITY_NAME);
+ final ReportedSizes initialSizes = getLastReportedSizesForActivity(
+ RESIZEABLE_ACTIVITY_NAME, logSeparator);
+ assertNotNull("Test activity must have reported initial sizes on launch", initialSizes);
+
+ try (final RotationSession rotationSession = new RotationSession()) {
+ // Rotate primary display and check that activity on secondary display is not
+ // affected.
+
+ rotateAndCheckSameSizes(rotationSession, RESIZEABLE_ACTIVITY_NAME);
+
+ // Launch activity to secondary display when primary one is rotated.
+ final int initialRotation = mAmWmState.getWmState().getRotation();
+ rotationSession.set((initialRotation + 1) % 4);
+
+ logSeparator = clearLogcat();
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+ mAmWmState.assertFocusedActivity("Focus must be on secondary display",
+ TEST_ACTIVITY_NAME);
+ final ReportedSizes testActivitySizes = getLastReportedSizesForActivity(
+ TEST_ACTIVITY_NAME, logSeparator);
+ assertEquals(
+ "Sizes of secondary display must not change after rotation of primary "
+ + "display",
+ initialSizes, testActivitySizes);
+ }
+ }
+ }
+
+ private void rotateAndCheckSameSizes(RotationSession rotationSession, String activityName)
+ throws Exception {
+ for (int rotation = 3; rotation >= 0; --rotation) {
+ final String logSeparator = clearLogcat();
+ rotationSession.set(rotation);
+ final ReportedSizes rotatedSizes = getLastReportedSizesForActivity(activityName,
+ logSeparator);
+ assertNull("Sizes must not change after rotation", rotatedSizes);
+ }
+ }
+
+ /**
+ * Tests that task affinity does affect what display an activity is launched on but that
+ * matching the task component root does.
+ */
+ @Test
+ public void testTaskMatchAcrossDisplays() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+ // Check that activity is on the secondary display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ executeShellCommand("am start -n " + getActivityComponentName(ALT_LAUNCHING_ACTIVITY));
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+ // Check that second activity gets launched on the default display despite
+ // the affinity match on the secondary display.
+ final int defaultDisplayFrontStackId = mAmWmState.getAmState().getFrontStackId(
+ DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack defaultDisplayFrontStack =
+ mAmWmState.getAmState().getStackById(defaultDisplayFrontStackId);
+ assertEquals("Activity launched on default display must be resumed",
+ getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+ defaultDisplayFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on primary display",
+ defaultDisplayFrontStackId);
+
+ executeShellCommand("am start -n " + getActivityComponentName(LAUNCHING_ACTIVITY));
+ mAmWmState.waitForFocusedStack(frontStackId);
+
+ // Check that the third intent is redirected to the first task due to the root
+ // component match on the secondary display.
+ final ActivityManagerState.ActivityStack secondFrontStack
+ = mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(LAUNCHING_ACTIVITY),
+ secondFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on primary display", frontStackId);
+ assertEquals("Focused stack must only contain 1 task",
+ 1, secondFrontStack.getTasks().size());
+ assertEquals("Focused task must only contain 1 activity",
+ 1, secondFrontStack.getTasks().get(0).mActivities.size());
+ }
+ }
+
+ /**
+ * Tests that the task affinity search respects the launch display id.
+ */
+ @Test
+ public void testLaunchDisplayAffinityMatch() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay newDisplay = virtualDisplaySession.createDisplay();
+
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, newDisplay.mId);
+
+ // Check that activity is on the secondary display.
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(LAUNCHING_ACTIVITY), firstFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ // We don't want FLAG_ACTIVITY_MULTIPLE_TASK, so we can't use launchActivityOnDisplay
+ executeShellCommand("am start -n "
+ + getActivityComponentName(ALT_LAUNCHING_ACTIVITY)
+ + " -f 0x10000000" // FLAG_ACTIVITY_NEW_TASK
+ + " --display " + newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+ // Check that second activity gets launched into the affinity matching
+ // task on the secondary display
+ final int secondFrontStackId =
+ mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack secondFrontStack =
+ mAmWmState.getAmState().getStackById(secondFrontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+ secondFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display",
+ secondFrontStackId);
+ assertEquals("Focused stack must only contain 1 task",
+ 1, secondFrontStack.getTasks().size());
+ assertEquals("Focused task must contain 2 activities",
+ 2, secondFrontStack.getTasks().get(0).mActivities.size());
+ }
+ }
+
+ /**
+ * Tests than a new task launched by an activity will end up on that activity's display
+ * even if the focused stack is not on that activity's display.
+ */
+ @Test
+ public void testNewTaskSameDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ final ActivityDisplay newDisplay = virtualDisplaySession.setSimulateDisplay(true)
+ .createDisplay();
+
+ launchActivityOnDisplay(BROADCAST_RECEIVER_ACTIVITY, newDisplay.mId);
+ mAmWmState.computeState(new WaitForValidActivityState(BROADCAST_RECEIVER_ACTIVITY));
+
+ // Check that the first activity is launched onto the secondary display
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(BROADCAST_RECEIVER_ACTIVITY),
+ firstFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+
+ executeShellCommand("am start -n " + getActivityComponentName(TEST_ACTIVITY_NAME));
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+
+ // Check that the second activity is launched on the default display
+ final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ final ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Activity launched on default display must be resumed",
+ getActivityComponentName(TEST_ACTIVITY_NAME), focusedStack.mResumedActivity);
+ assertEquals("Focus must be on primary display", DEFAULT_DISPLAY_ID,
+ focusedStack.mDisplayId);
+
+ executeShellCommand("am broadcast -a trigger_broadcast --ez launch_activity true "
+ + "--ez new_task true --es target_activity " + LAUNCHING_ACTIVITY);
+
+ // Check that the third activity ends up in a new task in the same stack as the
+ // first activity
+ mAmWmState.waitForValidState(false /* compareTaskAndStackBounds */, componentName,
+ new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+ final ActivityManagerState.ActivityStack secondFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity must be launched on secondary display",
+ getActivityComponentName(LAUNCHING_ACTIVITY),
+ secondFrontStack.mResumedActivity);
+ assertEquals("Secondary display must contain 2 tasks",
+ 2, secondFrontStack.getTasks().size());
+ }
+ }
+
+ /**
+ * Tests than an immediate launch after new display creation is handled correctly.
+ */
+ @Test
+ public void testImmediateLaunchOnNewDisplay() throws Exception {
+ try (final VirtualDisplaySession virtualDisplaySession = new VirtualDisplaySession()) {
+ // Create new virtual display and immediately launch an activity on it.
+ final ActivityDisplay newDisplay = virtualDisplaySession
+ .setLaunchActivity(TEST_ACTIVITY_NAME)
+ .createDisplay();
+
+ // Check that activity is launched and placed correctly.
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_RESUMED);
+ mAmWmState.assertResumedActivity("Test activity must be launched on a new display",
+ TEST_ACTIVITY_NAME);
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(newDisplay.mId);
+ final ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Activity launched on secondary display must be resumed",
+ getActivityComponentName(TEST_ACTIVITY_NAME), firstFrontStack.mResumedActivity);
+ mAmWmState.assertFocusedStack("Focus must be on secondary display", frontStackId);
+ }
+ }
+
+ /**
+ * Tests that turning the primary display off does not affect the activity running
+ * on an external secondary display.
+ */
+ @Test
+ public void testExternalDisplayActivityTurnPrimaryOff() throws Exception {
+ // Launch something on the primary display so we know there is a resumed activity there
+ launchActivity(RESIZEABLE_ACTIVITY_NAME);
+ waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+ "Activity launched on primary display must be resumed");
+
+ try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
+ final PrimaryDisplayStateSession displayStateSession =
+ new PrimaryDisplayStateSession()) {
+ final ActivityDisplay newDisplay =
+ externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+ // Check that the activity is launched onto the external display
+ waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+
+ displayStateSession.turnScreenOff();
+
+ // Wait for the fullscreen stack to start sleeping, and then make sure the
+ // test activity is still resumed.
+ waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+ "Activity launched on primary display must be stopped after turning off");
+ waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+ }
+ }
+
+ /**
+ * Tests that an activity can be launched on a secondary display while the primary
+ * display is off.
+ */
+ @Test
+ public void testLaunchExternalDisplayActivityWhilePrimaryOff() throws Exception {
+ // Launch something on the primary display so we know there is a resumed activity there
+ launchActivity(RESIZEABLE_ACTIVITY_NAME);
+ waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+ "Activity launched on primary display must be resumed");
+
+ try (final PrimaryDisplayStateSession displayStateSession =
+ new PrimaryDisplayStateSession();
+ final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+ displayStateSession.turnScreenOff();
+
+ // Make sure there is no resumed activity when the primary display is off
+ waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+ "Activity launched on primary display must be stopped after turning off");
+ assertEquals("Unexpected resumed activity",
+ 0, mAmWmState.getAmState().getResumedActivitiesCount());
+
+ final ActivityDisplay newDisplay =
+ externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+ // Check that the test activity is resumed on the external display
+ waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+ }
+ }
+
+ /**
+ * Tests that turning the secondary display off stops activities running on that display.
+ */
+ @Test
+ public void testExternalDisplayToggleState() throws Exception {
+ try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+ final ActivityDisplay newDisplay =
+ externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+ // Check that the test activity is resumed on the external display
+ waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+
+ externalDisplaySession.turnDisplayOff();
+
+ // Check that turning off the external display stops the activity
+ waitAndAssertActivityStopped(TEST_ACTIVITY_NAME,
+ "Activity launched on external display must be stopped after turning off");
+
+ externalDisplaySession.turnDisplayOn();
+
+ // Check that turning on the external display resumes the activity
+ waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+ }
+ }
+
+ /**
+ * Tests that tapping on the primary display after showing the keyguard resumes the
+ * activity on the primary display.
+ */
+ @Test
+ public void testStackFocusSwitchOnTouchEventAfterKeyguard() throws Exception {
+ // Launch something on the primary display so we know there is a resumed activity there
+ launchActivity(RESIZEABLE_ACTIVITY_NAME);
+ waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+ "Activity launched on primary display must be resumed");
+
+ sleepDevice();
+
+ // Make sure there is no resumed activity when the primary display is off
+ waitAndAssertActivityStopped(RESIZEABLE_ACTIVITY_NAME,
+ "Activity launched on primary display must be stopped after turning off");
+ assertEquals("Unexpected resumed activity",
+ 0, mAmWmState.getAmState().getResumedActivitiesCount());
+
+ try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession()) {
+ final ActivityDisplay newDisplay =
+ externalDisplaySession.createVirtualDisplay(true /* showContentWhenLocked */);
+
+ launchActivityOnDisplay(TEST_ACTIVITY_NAME, newDisplay.mId);
+
+ // Check that the test activity is resumed on the external display
+ waitAndAssertActivityResumed(TEST_ACTIVITY_NAME, newDisplay.mId,
+ "Activity launched on external display must be resumed");
+
+ // Unlock the device and tap on the middle of the primary display
+ wakeUpDevice();
+ executeShellCommand("wm dismiss-keyguard");
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.waitForValidState(new WaitForValidActivityState(TEST_ACTIVITY_NAME));
+ final ReportedDisplayMetrics displayMetrics = getDisplayMetrics();
+ final int width = displayMetrics.getSize().getWidth();
+ final int height = displayMetrics.getSize().getHeight();
+ executeShellCommand("input tap " + (width / 2) + " " + (height / 2));
+
+ // Check that the activity on the primary display is resumed
+ waitAndAssertActivityResumed(RESIZEABLE_ACTIVITY_NAME, DEFAULT_DISPLAY_ID,
+ "Activity launched on primary display must be resumed");
+ assertEquals("Unexpected resumed activity",
+ 1, mAmWmState.getAmState().getResumedActivitiesCount());
+ }
+ }
+
+ private void waitAndAssertActivityResumed(String activityName, int displayId, String message)
+ throws Exception {
+ mAmWmState.waitForActivityState(activityName, STATE_RESUMED);
+
+ final String fullActivityName = getActivityComponentName(activityName);
+ assertEquals(message, fullActivityName, mAmWmState.getAmState().getResumedActivity());
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(displayId);
+ ActivityManagerState.ActivityStack firstFrontStack =
+ mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals(message, fullActivityName, firstFrontStack.mResumedActivity);
+ assertTrue(message,
+ mAmWmState.getAmState().hasActivityState(activityName, STATE_RESUMED));
+ mAmWmState.assertFocusedStack("Focus must be on external display", frontStackId);
+ mAmWmState.assertVisibility(activityName, true /* visible */);
+ }
+
+ private void waitAndAssertActivityStopped(String activityName, String message)
+ throws Exception {
+ mAmWmState.waitForActivityState(activityName, STATE_STOPPED);
+
+ assertTrue(message, mAmWmState.getAmState().hasActivityState(activityName,
+ STATE_STOPPED));
+ }
+
+ /**
+ * Tests that showWhenLocked works on a secondary display.
+ */
+ public void testSecondaryDisplayShowWhenLocked() throws Exception {
+ try (final ExternalDisplaySession externalDisplaySession = new ExternalDisplaySession();
+ final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+
+ launchActivity(TEST_ACTIVITY_NAME);
+
+ final ActivityDisplay newDisplay =
+ externalDisplaySession.createVirtualDisplay(false /* showContentWhenLocked */);
+ launchActivityOnDisplay(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, newDisplay.mId);
+
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+
+ mAmWmState.waitForActivityState(TEST_ACTIVITY_NAME, STATE_STOPPED);
+ mAmWmState.waitForActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED);
+
+ mAmWmState.computeState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME);
+ assertTrue("Expected resumed activity on secondary display", mAmWmState.getAmState()
+ .hasActivityState(SHOW_WHEN_LOCKED_ATTR_ACTIVITY_NAME, STATE_RESUMED));
+ }
+ }
+
+ /** Assert that component received onMovedToDisplay and onConfigurationChanged callbacks. */
+ private void assertMovedToDisplay(String componentName, String logSeparator) throws Exception {
+ final ActivityLifecycleCounts lifecycleCounts
+ = new ActivityLifecycleCounts(componentName, logSeparator);
+ if (lifecycleCounts.mDestroyCount != 0) {
+ fail(componentName + " has been destroyed " + lifecycleCounts.mDestroyCount
+ + " time(s), wasn't expecting any");
+ } else if (lifecycleCounts.mCreateCount != 0) {
+ fail(componentName + " has been (re)created " + lifecycleCounts.mCreateCount
+ + " time(s), wasn't expecting any");
+ } else if (lifecycleCounts.mConfigurationChangedCount != 1) {
+ fail(componentName + " has received "
+ + lifecycleCounts.mConfigurationChangedCount
+ + " onConfigurationChanged() calls, expecting " + 1);
+ } else if (lifecycleCounts.mMovedToDisplayCount != 1) {
+ fail(componentName + " has received "
+ + lifecycleCounts.mMovedToDisplayCount
+ + " onMovedToDisplay() calls, expecting " + 1);
+ }
+ }
+
+ private static String getResizeVirtualDisplayCommand() {
+ return getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY) + " -f 0x20000000" +
+ " --es command resize_display";
+ }
+
+ private class ExternalDisplaySession implements AutoCloseable {
+
+ @Nullable
+ private DisplayHelper mExternalDisplayHelper;
+
+ /**
+ * Creates a private virtual display with the external and show with insecure
+ * keyguard flags set.
+ */
+ ActivityDisplay createVirtualDisplay(boolean showContentWhenLocked)
+ throws Exception {
+ final List<ActivityDisplay> originalDS = getDisplaysStates();
+ final int originalDisplayCount = originalDS.size();
+
+ mExternalDisplayHelper = new DisplayHelper();
+ mExternalDisplayHelper.createAndWaitForDisplay(true /* external */,
+ showContentWhenLocked);
+
+ // Wait for the virtual display to be created and get configurations.
+ final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
+ assertEquals("New virtual display must be created", originalDisplayCount + 1,
+ ds.size());
+
+ // Find the newly added display.
+ final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
+ return newDisplays.get(0);
+ }
+
+ void turnDisplayOff() {
+ if (mExternalDisplayHelper == null) {
+ new RuntimeException("No external display created");
+ }
+ mExternalDisplayHelper.turnDisplayOff();
+ }
+
+ void turnDisplayOn() {
+ if (mExternalDisplayHelper == null) {
+ new RuntimeException("No external display created");
+ }
+ mExternalDisplayHelper.turnDisplayOn();
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (mExternalDisplayHelper != null) {
+ mExternalDisplayHelper.releaseDisplay();
+ mExternalDisplayHelper = null;
+ }
+ }
+ }
+
+ private static class PrimaryDisplayStateSession implements AutoCloseable {
+
+ void turnScreenOff() {
+ setPrimaryDisplayState(false);
+ }
+
+ @Override
+ public void close() throws Exception {
+ setPrimaryDisplayState(true);
+ }
+
+ /** Turns the primary display on/off by pressing the power key */
+ private void setPrimaryDisplayState(boolean wantOn) {
+ // Either KeyEvent.KEYCODE_WAKEUP or KeyEvent.KEYCODE_SLEEP
+ int keycode = wantOn ? 224 : 223;
+ executeShellCommand("input keyevent " + keycode);
+ DisplayHelper.waitForDefaultDisplayState(wantOn);
+ }
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
index 5ba794d..f5a6a22 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerPinnedStackTests.java
@@ -1237,6 +1237,7 @@
@Presubmit
@Test
+ @FlakyTest(bugId = 71792368)
public void testEnterPictureInPictureDiscardSavedPositionOnFinish() throws Exception {
if (!supportsPip()) return;
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
index 06fd6bf..3187ca7 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerTransitionSelectionTests.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.assertEquals;
import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
import org.junit.Test;
@@ -98,6 +99,7 @@
false /*slowStop*/, TRANSIT_TASK_OPEN);
}
+ @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_NeitherWallpaper() throws Exception {
testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -156,6 +158,7 @@
// Test task close -- bottom task top activity slow in stopping
// These simulate the case where the bottom activity is resumed
// before AM receives its activitiyStopped
+ @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_NeitherWallpaper_SlowStop() throws Exception {
testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -201,6 +204,7 @@
TRANSIT_TASK_CLOSE);
}
+ @FlakyTest(bugId = 71792333)
@Test
public void testCloseTask_BottomWallpaper_Translucent() throws Exception {
testCloseTaskTranslucent(true /*bottomWallpaper*/, false /*topWallpaper*/,
@@ -262,7 +266,7 @@
}
executeShellCommand(bottomStartCmd);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
+ mAmWmState.computeState(new WaitForValidActivityState(BOTTOM_ACTIVITY_NAME));
final String topActivityName = topTranslucent ?
TRANSLUCENT_TOP_ACTIVITY_NAME : TOP_ACTIVITY_NAME;
@@ -280,9 +284,9 @@
Thread.sleep(5000);
if (testOpen) {
- mAmWmState.computeState(new WaitForValidActivityState.Builder(topActivityName).build());
+ mAmWmState.computeState(new WaitForValidActivityState(topActivityName));
} else {
- mAmWmState.computeState(new WaitForValidActivityState.Builder(BOTTOM_ACTIVITY_NAME).build());
+ mAmWmState.computeState(new WaitForValidActivityState(BOTTOM_ACTIVITY_NAME));
}
assertEquals("Picked wrong transition", expectedTransit,
diff --git a/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
new file mode 100644
index 0000000..3c0a584
--- /dev/null
+++ b/tests/framework/base/activitymanager/src/android/server/am/ActivityManagerVrDisplayTests.java
@@ -0,0 +1,229 @@
+/*
+ * 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
+ */
+
+package android.server.am;
+
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assume.assumeTrue;
+
+import android.server.am.ActivityManagerState.ActivityDisplay;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+/**
+ * Build/Install/Run:
+ * atest CtsActivityManagerDeviceTestCases:ActivityManagerVrDisplayTests
+ */
+public class ActivityManagerVrDisplayTests extends ActivityManagerDisplayTestBase {
+ private static final String RESIZEABLE_ACTIVITY_NAME = "ResizeableActivity";
+ private static final String VR_TEST_ACTIVITY_NAME = "VrTestActivity";
+ private static final int VR_VIRTUAL_DISPLAY_WIDTH = 700;
+ private static final int VR_VIRTUAL_DISPLAY_HEIGHT = 900;
+ private static final int VR_VIRTUAL_DISPLAY_DPI = 320;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ assumeTrue(supportsVrMode());
+ }
+
+ private static class VrModeSession implements AutoCloseable {
+
+ void enablePersistentVrMode() throws Exception {
+ executeShellCommand("setprop vr_virtualdisplay true");
+ executeShellCommand("vr set-persistent-vr-mode-enabled true");
+ }
+
+ @Override
+ public void close() throws Exception {
+ executeShellCommand("vr set-persistent-vr-mode-enabled false");
+ executeShellCommand("setprop vr_virtualdisplay false");
+ }
+ }
+
+ /**
+ * Tests that any new activity launch in Vr mode is in Vr display.
+ */
+ @Test
+ public void testVrActivityLaunch() throws Exception {
+ assumeTrue(supportsMultiDisplay());
+
+ try (final VrModeSession vrModeSession = new VrModeSession()) {
+ // Put the device in persistent vr mode.
+ vrModeSession.enablePersistentVrMode();
+
+ // Launch the VR activity.
+ launchActivity(VR_TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+ mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+ // Launch the non-VR 2D activity and check where it ends up.
+ launchActivity(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+ // Ensure that the subsequent activity is visible
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+ // Check that activity is launched in focused stack on primary display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ LAUNCHING_ACTIVITY);
+ final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ final ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Launched activity must be resumed in focused stack",
+ getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
+
+ // Check if the launch activity is in Vr virtual display id.
+ final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+ final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+ VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
+ assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+ // Check if the focused activity is on this virtual stack.
+ assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+ focusedStack.mDisplayId);
+ }
+ }
+
+ /**
+ * Tests that any activity already present is re-launched in Vr display in vr mode.
+ */
+ @Test
+ public void testVrActivityReLaunch() throws Exception {
+ assumeTrue(supportsMultiDisplay());
+
+ // Launch a 2D activity.
+ launchActivity(LAUNCHING_ACTIVITY);
+
+ try (final VrModeSession vrModeSession = new VrModeSession()) {
+ // Put the device in persistent vr mode.
+ vrModeSession.enablePersistentVrMode();
+
+ // Launch the VR activity.
+ launchActivity(VR_TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+ mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+ // Re-launch the non-VR 2D activity and check where it ends up.
+ launchActivity(LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(LAUNCHING_ACTIVITY));
+
+ // Ensure that the subsequent activity is visible
+ mAmWmState.assertVisibility(LAUNCHING_ACTIVITY, true /* visible */);
+
+ // Check that activity is launched in focused stack on primary display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ LAUNCHING_ACTIVITY);
+ final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ final ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Launched activity must be resumed in focused stack",
+ getActivityComponentName(LAUNCHING_ACTIVITY), focusedStack.mResumedActivity);
+
+ // Check if the launch activity is in Vr virtual display id.
+ final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+ final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+ VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT, VR_VIRTUAL_DISPLAY_DPI);
+ assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+ // Check if the focused activity is on this virtual stack.
+ assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+ focusedStack.mDisplayId);
+ }
+ }
+
+ /**
+ * Tests that any new activity launch post Vr mode is in the main display.
+ */
+ @Test
+ public void testActivityLaunchPostVr() throws Exception {
+ assumeTrue(supportsMultiDisplay());
+
+ try (final VrModeSession vrModeSession = new VrModeSession()) {
+ // Put the device in persistent vr mode.
+ vrModeSession.enablePersistentVrMode();
+
+ // Launch the VR activity.
+ launchActivity(VR_TEST_ACTIVITY_NAME);
+ mAmWmState.computeState(new WaitForValidActivityState(VR_TEST_ACTIVITY_NAME));
+ mAmWmState.assertVisibility(VR_TEST_ACTIVITY_NAME, true /* visible */);
+
+ // Launch the non-VR 2D activity and check where it ends up.
+ launchActivity(ALT_LAUNCHING_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(ALT_LAUNCHING_ACTIVITY));
+
+ // Ensure that the subsequent activity is visible
+ mAmWmState.assertVisibility(ALT_LAUNCHING_ACTIVITY, true /* visible */);
+
+ // Check that activity is launched in focused stack on primary display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ ALT_LAUNCHING_ACTIVITY);
+ final int focusedStackId = mAmWmState.getAmState().getFocusedStackId();
+ final ActivityManagerState.ActivityStack focusedStack
+ = mAmWmState.getAmState().getStackById(focusedStackId);
+ assertEquals("Launched activity must be resumed in focused stack",
+ getActivityComponentName(ALT_LAUNCHING_ACTIVITY),
+ focusedStack.mResumedActivity);
+
+ // Check if the launch activity is in Vr virtual display id.
+ final List<ActivityDisplay> reportedDisplays = getDisplaysStates();
+ final ActivityDisplay vrDisplay = getDisplayState(reportedDisplays,
+ VR_VIRTUAL_DISPLAY_WIDTH, VR_VIRTUAL_DISPLAY_HEIGHT,
+ VR_VIRTUAL_DISPLAY_DPI);
+ assertNotNull("Vr mode should have a virtual display", vrDisplay);
+
+ // Check if the focused activity is on this virtual stack.
+ assertEquals("Launch in Vr mode should be in virtual stack", vrDisplay.mId,
+ focusedStack.mDisplayId);
+
+ }
+
+ // There isn't a direct launch of activity which can take an user out of persistent VR mode.
+ // This sleep is to account for that delay and let device settle once it comes out of VR
+ // mode.
+ try {
+ Thread.sleep(2000);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ // Launch the non-VR 2D activity and check where it ends up.
+ launchActivity(RESIZEABLE_ACTIVITY_NAME);
+ mAmWmState.computeState(new WaitForValidActivityState(RESIZEABLE_ACTIVITY_NAME));
+
+ // Ensure that the subsequent activity is visible
+ mAmWmState.assertVisibility(RESIZEABLE_ACTIVITY_NAME, true /* visible */);
+
+ // Check that activity is launched in focused stack on primary display.
+ mAmWmState.assertFocusedActivity("Launched activity must be focused",
+ RESIZEABLE_ACTIVITY_NAME);
+ final int frontStackId = mAmWmState.getAmState().getFrontStackId(DEFAULT_DISPLAY_ID);
+ final ActivityManagerState.ActivityStack frontStack
+ = mAmWmState.getAmState().getStackById(frontStackId);
+ assertEquals("Launched activity must be resumed in front stack",
+ getActivityComponentName(RESIZEABLE_ACTIVITY_NAME), frontStack.mResumedActivity);
+ assertEquals("Front stack must be on primary display",
+ DEFAULT_DISPLAY_ID, frontStack.mDisplayId);
+ }
+}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
index d6c61a4..fd8ed2f 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/AnimationBackgroundTests.java
@@ -23,6 +23,8 @@
import org.junit.Test;
+import static android.server.am.ActivityAndWindowManagersState.DEFAULT_DISPLAY_ID;
+
/**
* Build/Install/Run:
* atest CtsActivityManagerDeviceTestCases:AnimationBackgroundTests
@@ -31,7 +33,7 @@
@Test
public void testAnimationBackground_duringAnimation() throws Exception {
- launchActivity(LAUNCHING_ACTIVITY);
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY_ID);
getLaunchActivityBuilder()
.setTargetActivityName("AnimationTestActivity")
.setWaitForLaunched(false)
@@ -49,7 +51,7 @@
@Test
public void testAnimationBackground_gone() throws Exception {
- launchActivity(LAUNCHING_ACTIVITY);
+ launchActivityOnDisplay(LAUNCHING_ACTIVITY, DEFAULT_DISPLAY_ID);
getLaunchActivityBuilder().setTargetActivityName("AnimationTestActivity").execute();
mAmWmState.computeState(new WaitForValidActivityState.Builder("AnimationTestActivity").build());
assertFalse("window animation background needs to be gone", mAmWmState.getWmState()
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
index 55c8ebf..ce22ae7 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardLockedTests.java
@@ -44,164 +44,182 @@
super.setUp();
assumeTrue(isHandheld());
-
- setLockCredential();
- }
-
- @After
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
- tearDownLockCredentials();
}
@Test
public void testLockAndUnlock() throws Exception {
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- unlockDeviceWithCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.assertKeyguardGone();
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ lockCredentialSession.unlockDeviceWithCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ }
}
@Test
public void testDismissKeyguard() throws Exception {
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- launchActivity("DismissKeyguardActivity");
- enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.assertKeyguardGone();
- mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivity("DismissKeyguardActivity");
+ lockCredentialSession.enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+ }
}
@Test
public void testDismissKeyguard_whileOccluded() throws Exception {
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
- mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
- launchActivity("DismissKeyguardActivity");
- enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.assertKeyguardGone();
- mAmWmState.assertVisibility("DismissKeyguardActivity", true);
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ launchActivity("DismissKeyguardActivity");
+ lockCredentialSession.enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertVisibility("DismissKeyguardActivity", true);
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, false);
+ }
}
@Test
public void testDismissKeyguard_fromShowWhenLocked_notAllowed() throws Exception {
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
- mAmWmState.computeState(new WaitForValidActivityState.Builder( SHOW_WHEN_LOCKED_ACTIVITY ).build());
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
- executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
- enterAndConfirmLockCredential();
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ executeShellCommand("am broadcast -a trigger_broadcast --ez dismissKeyguard true");
+ lockCredentialSession.enterAndConfirmLockCredential();
- // Make sure we stay on Keyguard.
- mAmWmState.assertKeyguardShowingAndOccluded();
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ // Make sure we stay on Keyguard.
+ mAmWmState.assertKeyguardShowingAndOccluded();
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ }
}
@Test
public void testDismissKeyguardActivity_method() throws Exception {
- final String logSeparator = clearLogcat();
- gotoKeyguard();
- mAmWmState.computeState();
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- launchActivity("DismissKeyguardMethodActivity");
- enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.computeState(new WaitForValidActivityState.Builder( "DismissKeyguardMethodActivity").build());
- mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
- assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- assertOnDismissSucceededInLogcat(logSeparator);
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ final String logSeparator = clearLogcat();
+ gotoKeyguard();
+ mAmWmState.computeState();
+ assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+ launchActivity("DismissKeyguardMethodActivity");
+ lockCredentialSession.enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.computeState(new WaitForValidActivityState("DismissKeyguardMethodActivity"));
+ mAmWmState.assertVisibility("DismissKeyguardMethodActivity", true);
+ assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+ assertOnDismissSucceededInLogcat(logSeparator);
+ }
}
@Test
public void testDismissKeyguardActivity_method_cancelled() throws Exception {
- final String logSeparator = clearLogcat();
- gotoKeyguard();
- mAmWmState.computeState();
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- launchActivity("DismissKeyguardMethodActivity");
- pressBackButton();
- assertOnDismissCancelledInLogcat(logSeparator);
- mAmWmState.computeState();
- mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- unlockDeviceWithCredential();
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ final String logSeparator = clearLogcat();
+ gotoKeyguard();
+ mAmWmState.computeState();
+ assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+ launchActivity("DismissKeyguardMethodActivity");
+ pressBackButton();
+ assertOnDismissCancelledInLogcat(logSeparator);
+ mAmWmState.computeState();
+ mAmWmState.assertVisibility("DismissKeyguardMethodActivity", false);
+ assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+ lockCredentialSession.unlockDeviceWithCredential();
+ }
}
@Test
public void testEnterPipOverKeyguard() throws Exception {
assumeTrue(supportsPip());
- // Go to the keyguard
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ // Go to the keyguard
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- // Enter PiP on an activity on top of the keyguard, and ensure that it prompts the user for
- // their credentials and does not enter picture-in-picture yet
- launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
- executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.waitForKeyguardShowingAndOccluded();
- mAmWmState.assertKeyguardShowingAndOccluded();
- mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
- WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
+ // Enter PiP on an activity on top of the keyguard, and ensure that it prompts the user
+ // for their credentials and does not enter picture-in-picture yet
+ launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
+ executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+ mAmWmState.waitForKeyguardShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
+ mAmWmState.assertDoesNotContainStack("Must not contain pinned stack.",
+ WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
- // Enter the credentials and ensure that the activity actually entered picture-in-picture
- enterAndConfirmLockCredential();
- mAmWmState.waitForKeyguardGone();
- mAmWmState.assertKeyguardGone();
- mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD);
+ // Enter the credentials and ensure that the activity actually entered picture-in
+ // -picture
+ lockCredentialSession.enterAndConfirmLockCredential();
+ mAmWmState.waitForKeyguardGone();
+ mAmWmState.assertKeyguardGone();
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
+ }
}
@Test
public void testShowWhenLockedActivityAndPipActivity() throws Exception {
assumeTrue(supportsPip());
- launchActivity(PIP_ACTIVITY);
- executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
- mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD);
- mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ launchActivity(PIP_ACTIVITY);
+ executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+ mAmWmState.computeState(new WaitForValidActivityState(PIP_ACTIVITY));
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertVisibility(PIP_ACTIVITY, true);
- launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
- mAmWmState.computeState(
- new WaitForValidActivityState.Builder(SHOW_WHEN_LOCKED_ACTIVITY).build());
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ launchActivity(SHOW_WHEN_LOCKED_ACTIVITY);
+ mAmWmState.computeState(new WaitForValidActivityState(SHOW_WHEN_LOCKED_ACTIVITY));
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndOccluded();
- mAmWmState.assertKeyguardShowingAndOccluded();
- mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
- mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndOccluded();
+ mAmWmState.assertKeyguardShowingAndOccluded();
+ mAmWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true);
+ mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+ }
}
@Test
public void testShowWhenLockedPipActivity() throws Exception {
assumeTrue(supportsPip());
- launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
- executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
- mAmWmState.computeState(new WaitForValidActivityState.Builder(PIP_ACTIVITY).build());
- mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
- ACTIVITY_TYPE_STANDARD);
- mAmWmState.assertVisibility(PIP_ACTIVITY, true);
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ launchActivity(PIP_ACTIVITY, EXTRA_SHOW_OVER_KEYGUARD, "true");
+ executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_ENTER_PIP);
+ mAmWmState.computeState(new WaitForValidActivityState(PIP_ACTIVITY));
+ mAmWmState.assertContainsStack("Must contain pinned stack.", WINDOWING_MODE_PINNED,
+ ACTIVITY_TYPE_STANDARD);
+ mAmWmState.assertVisibility(PIP_ACTIVITY, true);
- gotoKeyguard();
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertKeyguardShowingAndNotOccluded();
- mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+ gotoKeyguard();
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertKeyguardShowingAndNotOccluded();
+ mAmWmState.assertVisibility(PIP_ACTIVITY, false);
+ }
}
}
diff --git a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
index 2c7c554..830615d 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/KeyguardTests.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeTrue;
+import android.platform.test.annotations.Presubmit;
import android.server.am.WindowManagerState.WindowState;
import org.junit.After;
@@ -50,14 +51,6 @@
setLockDisabled(false);
}
- @After
- @Override
- public void tearDown() throws Exception {
- super.tearDown();
-
- tearDownLockCredentials();
- }
-
@Test
public void testKeyguardHidesActivity() throws Exception {
launchActivity("TestActivity");
@@ -177,6 +170,7 @@
* Test that showWhenLocked activity is fullscreen when shown over keyguard
*/
@Test
+ @Presubmit
public void testShowWhenLockedActivityWhileSplit() throws Exception {
assumeTrue(supportsSplitScreenMultiWindow());
@@ -251,6 +245,7 @@
mAmWmState.assertVisibility("TurnScreenOnDismissKeyguardActivity", true);
assertFalse(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
assertOnDismissSucceededInLogcat(logSeparator);
+ assertTrue(isDisplayOn());
}
@Test
@@ -327,16 +322,18 @@
public void testDismissKeyguardAttrActivity_method_turnScreenOn_withSecureKeyguard() throws Exception {
final String activityName = "TurnScreenOnAttrDismissKeyguardActivity";
- setLockCredential();
- sleepDevice();
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
+ sleepDevice();
- mAmWmState.computeState();
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- launchActivity(activityName);
- mAmWmState.waitForKeyguardShowingAndNotOccluded();
- mAmWmState.assertVisibility(activityName, false);
- assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
- assertTrue(isDisplayOn());
+ mAmWmState.computeState();
+ assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+ launchActivity(activityName);
+ mAmWmState.waitForKeyguardShowingAndNotOccluded();
+ mAmWmState.assertVisibility(activityName, false);
+ assertTrue(mAmWmState.getAmState().getKeyguardControllerState().keyguardShowing);
+ assertTrue(isDisplayOn());
+ }
}
@Test
diff --git a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
index 982f30d..1e39db6 100644
--- a/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
+++ b/tests/framework/base/activitymanager/src/android/server/am/lifecycle/ActivityLifecycleKeyguardTests.java
@@ -25,22 +25,18 @@
@Override
public void setUp() throws Exception {
super.setUp();
- setLockCredential();
gotoKeyguard();
}
- @After
- @Override
- public void tearDown() throws Exception {
- tearDownLockCredentials();
- super.tearDown();
- }
-
@Test
public void testSingleLaunch() throws Exception {
- final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
- waitAndAssertActivityStates(state(activity, STOPPED));
+ try (final LockCredentialSession lockCredentialSession = new LockCredentialSession()) {
+ lockCredentialSession.setLockCredential();
- LifecycleVerifier.assertLaunchAndStopSequence(FirstActivity.class, getLifecycleLog());
+ final Activity activity = mFirstActivityTestRule.launchActivity(new Intent());
+ waitAndAssertActivityStates(state(activity, STOPPED));
+
+ LifecycleVerifier.assertLaunchAndStopSequence(FirstActivity.class, getLifecycleLog());
+ }
}
}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
index ab3e374..7367875 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityAndWindowManagersState.java
@@ -248,6 +248,28 @@
} while (retriesLeft-- > 0);
}
+ /**
+ * Ensures all exiting windows have been removed.
+ */
+ void waitForAllExitingWindows() {
+ int retriesLeft = 5;
+ do {
+ mWmState.computeState();
+ if (mWmState.containsExitingWindow()) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ log(e.toString());
+ // Well I guess we are not waiting...
+ }
+ } else {
+ break;
+ }
+ } while (retriesLeft-- > 0);
+
+ assertFalse(mWmState.containsExitingWindow());
+ }
+
void waitForAllStoppedActivities() throws Exception {
int retriesLeft = 5;
do {
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
index 7fe707a..16b0c0a 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/ActivityManagerTestBase.java
@@ -129,8 +129,6 @@
protected ActivityManager mAm;
protected UiDevice mDevice;
- private boolean mLockCredentialsSet;
-
private boolean mLockDisabled;
@Deprecated
@@ -274,7 +272,6 @@
wakeUpAndUnlockDevice();
pressHomeButton();
removeStacksWithActivityTypes(ALL_ACTIVITY_TYPE_BUT_HOME);
- mLockCredentialsSet = false;
mLockDisabled = isLockDisabled();
}
@@ -718,36 +715,52 @@
mDevice.pressMenu();
}
- protected void unlockDeviceWithCredential() throws Exception {
- mDevice.pressMenu();
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- //ignored
- }
- enterAndConfirmLockCredential();
- }
-
- protected void enterAndConfirmLockCredential() throws Exception {
- mDevice.waitForIdle(3000);
-
- runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL);
- mDevice.pressEnter();
- }
-
protected void gotoKeyguard() throws Exception {
sleepDevice();
wakeUpDevice();
mAmWmState.waitForKeyguardShowingAndNotOccluded();
}
- protected void setLockCredential() {
- mLockCredentialsSet = true;
- runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
- }
+ protected class LockCredentialSession implements AutoCloseable {
+ private boolean mLockCredentialSet;
- protected void removeLockCredential() {
- runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
+ public LockCredentialSession() {
+ mLockCredentialSet = false;
+ }
+
+ public void setLockCredential() {
+ mLockCredentialSet = true;
+ runCommandAndPrintOutput("locksettings set-pin " + LOCK_CREDENTIAL);
+ }
+
+ public void unlockDeviceWithCredential() throws Exception {
+ mDevice.pressMenu();
+ enterAndConfirmLockCredential();
+ }
+
+ public void enterAndConfirmLockCredential() throws Exception {
+ mDevice.waitForIdle(3000);
+
+ runCommandAndPrintOutput("input text " + LOCK_CREDENTIAL);
+ mDevice.pressEnter();
+ }
+
+ private void removeLockCredential() {
+ runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
+ mLockCredentialSet = false;
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (mLockCredentialSet) {
+ removeLockCredential();
+ // Dismiss active keyguard after credential is cleared, so keyguard doesn't ask for
+ // the stale credential.
+ pressBackButton();
+ sleepDevice();
+ wakeUpAndUnlockDevice();
+ }
+ }
}
/**
@@ -815,7 +828,7 @@
return INVALID_DEVICE_ROTATION;
}
- protected String runCommandAndPrintOutput(String command) {
+ protected static String runCommandAndPrintOutput(String command) {
final String output = executeShellCommand(command);
log(output);
return output;
@@ -1492,17 +1505,4 @@
}
}
}
-
- protected void tearDownLockCredentials() throws Exception {
- if (!mLockCredentialsSet) {
- return;
- }
-
- removeLockCredential();
- // Dismiss active keyguard after credential is cleared, so
- // keyguard doesn't ask for the stale credential.
- pressBackButton();
- sleepDevice();
- wakeUpAndUnlockDevice();
- }
}
diff --git a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
index 140f9a2..1eb855e 100644
--- a/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
+++ b/tests/framework/base/activitymanager/util/src/android/server/am/WindowManagerState.java
@@ -332,6 +332,16 @@
}
}
+ public boolean containsExitingWindow() {
+ for (WindowState ws : mWindowStates) {
+ if (ws.isExitingWindow()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
public WindowState getWindowByPackageName(String packageName, int windowType) {
for (WindowState ws : mWindowStates) {
final String name = ws.getName();
@@ -711,7 +721,7 @@
DisplayInfoProto infoProto = proto.displayInfo;
if (infoProto != null) {
mDisplayRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
- mAppRect.set(0, 0, infoProto.logicalWidth, infoProto.logicalHeight);
+ mAppRect.set(0, 0, infoProto.appWidth, infoProto.appHeight);
}
final DisplayFramesProto displayFramesProto = proto.displayFrames;
if (displayFramesProto != null) {
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
index 16a90d6..79c35f1 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java
@@ -16,7 +16,7 @@
package com.android.cts.mockime;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static com.android.cts.mockime.MockImeSession.MOCK_IME_SETTINGS_FILE;
@@ -226,7 +226,19 @@
final int windowFlags = mSettings.getWindowFlags(0);
final int windowFlagsMask = mSettings.getWindowFlagsMask(0);
if (windowFlags != 0 || windowFlagsMask != 0) {
+ final int prevFlags = getWindow().getWindow().getAttributes().flags;
getWindow().getWindow().setFlags(windowFlags, windowFlagsMask);
+ // For some reasons, seems that we need to post another requestLayout() when
+ // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS bit is changed.
+ // TODO: Investigate the reason.
+ if ((windowFlagsMask & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
+ final boolean hadFlag = (prevFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ final boolean hasFlag = (windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+ if (hadFlag != hasFlag) {
+ final View decorView = getWindow().getWindow().getDecorView();
+ decorView.post(() -> decorView.requestLayout());
+ }
+ }
}
if (mSettings.hasNavigationBarColor()) {
@@ -294,24 +306,42 @@
this.addOnLayoutChangeListener(mLayoutListener);
}
+ private void updateBottomPaddingIfNecessary(int newPaddingBottom) {
+ if (getPaddingBottom() != newPaddingBottom) {
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), newPaddingBottom);
+ }
+ }
+
@Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
- final int windowFlags = mSettings.getWindowFlags(0);
- if ((windowFlags & FLAG_LAYOUT_IN_OVERSCAN) == 0) {
+ if (insets.isConsumed()
+ || (getSystemUiVisibility() & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0) {
+ // In this case we are not interested in consuming NavBar region.
+ // Make sure that the bottom padding is empty.
+ updateBottomPaddingIfNecessary(0);
return insets;
}
- final int insetBottom = insets.getSystemWindowInsetBottom();
+ // In some cases the bottom system window inset is not a navigation bar. Wear devices
+ // that have bottom chin are examples. For now, assume that it's a navigation bar if it
+ // has the same height as the root window's stable bottom inset.
+ final WindowInsets rootWindowInsets = getRootWindowInsets();
+ if (rootWindowInsets != null && (rootWindowInsets.getStableInsetBottom() !=
+ insets.getSystemWindowInsetBottom())) {
+ // This is probably not a NavBar.
+ updateBottomPaddingIfNecessary(0);
+ return insets;
+ }
- // Somehow immediately calling setPadding doesn't properly update the layout.
- // TODO: Figure out why we have to delay this task.
- post(() -> setPadding(0, 0, 0, insetBottom));
-
- return insets.replaceSystemWindowInsets(
- insets.getSystemWindowInsetLeft(),
- insets.getSystemWindowInsetTop(),
- insets.getSystemWindowInsetRight(),
- 0 /* bottom */);
+ final int possibleNavBarHeight = insets.getSystemWindowInsetBottom();
+ updateBottomPaddingIfNecessary(possibleNavBarHeight);
+ return possibleNavBarHeight <= 0
+ ? insets
+ : insets.replaceSystemWindowInsets(
+ insets.getSystemWindowInsetLeft(),
+ insets.getSystemWindowInsetTop(),
+ insets.getSystemWindowInsetRight(),
+ 0 /* bottom */);
}
@Override
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
index 3a9a05e..cccc279 100644
--- a/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/NavigationBarColorTest.java
@@ -22,6 +22,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.inputmethod.cts.util.LightNavigationBarVerifier.expectLightNavigationBarNotSupported;
+import static android.view.inputmethod.cts.util.LightNavigationBarVerifier.expectLightNavigationBarSupported;
import static android.view.inputmethod.cts.util.NavigationBarColorVerifier.expectNavigationBarColorNotSupported;
import static android.view.inputmethod.cts.util.NavigationBarColorVerifier.expectNavigationBarColorSupported;
import static android.view.inputmethod.cts.util.TestUtils.getOnMainSync;
@@ -213,17 +214,15 @@
private ImeSettings.Builder imeSettingForFloatingIme(@ColorInt int navigationBarColor,
boolean lightNavigationBar) {
final ImeSettings.Builder builder = new ImeSettings.Builder();
- // Currently un-setting FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS does nothing for IME windows.
- // TODO: Fix this anomaly
builder.setWindowFlags(0, FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- // Although the document says that Window#setNavigationBarColor() requires
- // FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS to work, currently it's not true for IME windows.
- // TODO: Fix this anomaly
+ // As documented, Window#setNavigationBarColor() is actually ignored when the IME window
+ // does not have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS. We are calling setNavigationBarColor()
+ // to ensure it.
builder.setNavigationBarColor(navigationBarColor);
if (lightNavigationBar) {
- // Although the document says that Window#setNavigationBarColor() requires
- // SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR to work, currently it's not true for IME windows.
- // TODO: Fix this anomaly
+ // As documented, SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is actually ignored when the IME
+ // window does not have FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS. We set this flag just to
+ // ensure it.
builder.setInputViewSystemUiVisibility(SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
return builder;
@@ -274,9 +273,8 @@
public void testSetNavigationBarColor() throws Exception {
final NavigationBarInfo info = NavigationBarInfo.getInstance();
- // Currently Window#setNavigationBarColor() is ignored for IME windows.
- // TODO: Support Window#setNavigationBarColor() for IME windows (Bug 25706186)
- expectNavigationBarColorNotSupported(color ->
+ // Make sure that Window#setNavigationBarColor() works for IMEs.
+ expectNavigationBarColorSupported(color ->
getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, false),
Color.BLACK, false, info.getBottomNavigationBerHeight(),
DimmingTestMode.NO_DIMMING_DIALOG));
@@ -302,16 +300,16 @@
assumeTrue("This test does not make sense if light navigation bar is not supported"
+ " even for typical Activity", info.supportsLightNavigationBar());
- // Currently SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is ignored for IME windows.
- // TODO: Support SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR for IME windows (Bug 69002467)
- expectLightNavigationBarNotSupported((color, lightMode) ->
+ // Make sure that SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR works for IMEs (Bug 69002467).
+ expectLightNavigationBarSupported((color, lightMode) ->
getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, lightMode),
Color.BLACK, false, info.getBottomNavigationBerHeight(),
DimmingTestMode.NO_DIMMING_DIALOG));
- // Currently there is no way for IMEs to opt-out dark/light navigation bar mode.
- // TODO: Allows IMEs to opt out dark/light navigation bar mode (Bug 69111208).
- expectLightNavigationBarNotSupported((color, lightMode) ->
+ // Make sure that IMEs can opt-out navigation bar custom rendering, including
+ // SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, by un-setting FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag
+ // so that it can be controlled by the target application instead (Bug 69111208).
+ expectLightNavigationBarSupported((color, lightMode) ->
getNavigationBarBitmap(imeSettingForFloatingIme(Color.BLACK, false),
color, lightMode, info.getBottomNavigationBerHeight(),
DimmingTestMode.NO_DIMMING_DIALOG));
@@ -325,9 +323,9 @@
+ " light navigation bar for typical Activities",
info.supportsDimmingWindowLightNavigationBarOverride());
- // Currently SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR is ignored for IME windows.
- // TODO: Support SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR for IME windows (Bug 69002467)
- expectLightNavigationBarNotSupported((color, lightMode) ->
+ // Make sure that SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR works for IMEs, even if a dimming
+ // window is shown behind the IME window.
+ expectLightNavigationBarSupported((color, lightMode) ->
getNavigationBarBitmap(imeSettingForSolidNavigationBar(color, lightMode),
Color.BLACK, false, info.getBottomNavigationBerHeight(),
DimmingTestMode.DIMMING_DIALOG_BEHIND_IME));
diff --git a/tests/libcore/wycheproof-bc/AndroidTest.xml b/tests/libcore/wycheproof-bc/AndroidTest.xml
index e5280a4..2e92706 100644
--- a/tests/libcore/wycheproof-bc/AndroidTest.xml
+++ b/tests/libcore/wycheproof-bc/AndroidTest.xml
@@ -31,5 +31,6 @@
value="com.android.cts.core.runner.ExpectationBasedFilter" />
<option name="core-expectation" value="/knownfailures.txt" />
<option name="runtime-hint" value="10m"/>
+ <option name="test-timeout" value="600000" />
</test>
</configuration>
diff --git a/tests/tests/appwidget/AndroidTest.xml b/tests/tests/appwidget/AndroidTest.xml
index 778a9316..cc4fe42 100644
--- a/tests/tests/appwidget/AndroidTest.xml
+++ b/tests/tests/appwidget/AndroidTest.xml
@@ -22,6 +22,26 @@
<option name="test-file-name" value="CtsAppWidgetLauncher3.apk" />
<option name="test-file-name" value="CtsAppWidgetTestCases.apk" />
</target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="mkdir -p /data/local/tmp/cts/widgetprovider/" />
+ <option name="teardown-command" value="rm -rf /data/local/tmp/cts/widgetprovider"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsAppWidgetProvider1.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider1.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsAppWidgetProvider2.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider2.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsAppWidgetProvider3.apk->/data/local/tmp/cts/widgetprovider/CtsAppWidgetProvider3.apk" />
+ </target_preparer>
+
+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.appwidget.cts" />
<option name="runtime-hint" value="9m30s" />
diff --git a/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
index 954a81b..786b2eb 100644
--- a/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
+++ b/tests/tests/appwidget/common/src/android/appwidget/cts/common/Constants.java
@@ -27,4 +27,6 @@
public static final String EXTRA_PACKAGE = "PACKAGE_NAME";
public static final String EXTRA_REQUEST = "REQUEST";
+ public static final String ACTION_APPLY_OVERRIDE =
+ "android.appwidget.cts.widgetprovider.APPLY_OVERRIDE";
}
diff --git a/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java
new file mode 100644
index 0000000..3c29746
--- /dev/null
+++ b/tests/tests/appwidget/packages/src/android/appwidget/cts/packages/SimpleProvider.java
@@ -0,0 +1,25 @@
+package android.appwidget.cts.packages;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.appwidget.cts.common.Constants;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+
+public class SimpleProvider extends AppWidgetProvider {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ super.onReceive(context, intent);
+
+ if (Constants.ACTION_APPLY_OVERRIDE.equals(intent.getAction())) {
+ String request = intent.getStringExtra(Constants.EXTRA_REQUEST);
+ AppWidgetManager.getInstance(context).updateAppWidgetProviderInfo(
+ new ComponentName(context, SimpleProvider.class),
+ request);
+ setResultCode(Activity.RESULT_OK);
+ }
+ }
+}
diff --git a/tests/tests/appwidget/packages/widgetprovider/Android.mk b/tests/tests/appwidget/packages/widgetprovider/Android.mk
new file mode 100644
index 0000000..733a4a8
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/Android.mk
@@ -0,0 +1,78 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider1
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+ $(LOCAL_PATH)/AndroidManifestV1.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider2
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+ $(LOCAL_PATH)/AndroidManifestV2.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
+
+#-----------------------------------------------------------
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetProvider3
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, ../src) \
+ $(call all-java-files-under, ../../common/src)
+LOCAL_FULL_LIBS_MANIFEST_FILES := \
+ $(LOCAL_PATH)/AndroidManifestV3.xml
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml
new file mode 100644
index 0000000..77dfc5b
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?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 package="android.appwidget.cts.widgetprovider" >
+
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml
new file mode 100644
index 0000000..ea23176
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV1.xml
@@ -0,0 +1,32 @@
+<?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" >
+
+ <application>
+ <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/widget_no_config" />
+ <meta-data android:name="my_custom_info"
+ android:resource="@xml/widget_config" />
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml
new file mode 100644
index 0000000..e46e160
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV2.xml
@@ -0,0 +1,32 @@
+<?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" >
+
+ <application>
+ <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/widget_no_config" />
+ <meta-data android:name="my_custom_info"
+ android:resource="@xml/widget_config_no_resize" />
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml
new file mode 100644
index 0000000..6da740d
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/AndroidManifestV3.xml
@@ -0,0 +1,30 @@
+<?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" >
+
+ <application>
+ <receiver android:name="android.appwidget.cts.packages.SimpleProvider">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+ <action android:name="android.appwidget.cts.widgetprovider.APPLY_OVERRIDE" />
+ </intent-filter>
+ <meta-data android:name="android.appwidget.provider"
+ android:resource="@xml/widget_no_config" />
+ </receiver>
+ </application>
+</manifest>
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml b/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml
new file mode 100644
index 0000000..b9db450
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/layout/simple_widget.xml
@@ -0,0 +1,22 @@
+<?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.
+ -->
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#ff333333"
+ android:elevation="3dp" />
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml
new file mode 100644
index 0000000..f45c476
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<appwidget-provider
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="180dp"
+ android:minHeight="110dp"
+ android:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/simple_widget"
+ android:resizeMode="horizontal|vertical"
+ android:configure="android.appwidget.cts.widgetprovider.ExampleAppWidgetConfigure"
+ android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml
new file mode 100644
index 0000000..428a780
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_config_no_resize.xml
@@ -0,0 +1,26 @@
+<?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.
+ -->
+<appwidget-provider
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="180dp"
+ android:minHeight="110dp"
+ android:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/simple_widget"
+ android:resizeMode="none"
+ android:configure="android.appwidget.cts.widgetprovider.ExampleAppWidgetConfigure"
+ android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml
new file mode 100644
index 0000000..0dacd1c
--- /dev/null
+++ b/tests/tests/appwidget/packages/widgetprovider/res/xml/widget_no_config.xml
@@ -0,0 +1,25 @@
+<?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.
+ -->
+<appwidget-provider
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:minWidth="180dp"
+ android:minHeight="110dp"
+ android:updatePeriodMillis="86400000"
+ android:initialLayout="@layout/simple_widget"
+ android:resizeMode="horizontal|vertical"
+ android:widgetCategory="home_screen">
+</appwidget-provider>
\ No newline at end of file
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index acafd7d..c0870f2 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -86,10 +86,6 @@
public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// By default we should get only providers for the current user.
List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
@@ -98,10 +94,6 @@
}
public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We ask only for providers for the current user.
List<AppWidgetProviderInfo> providers = getAppWidgetManager()
.getInstalledProvidersForProfile(Process.myUserHandle());
@@ -111,10 +103,6 @@
}
public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We ask only for providers for all current user's profiles
UserManager userManager = (UserManager) getInstrumentation()
.getTargetContext().getSystemService(Context.USER_SERVICE);
@@ -135,10 +123,6 @@
}
public void testBindAppWidget() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// Create a host and start listening.
AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
host.deleteHost();
@@ -176,9 +160,6 @@
}
public void testGetAppWidgetIdsForHost() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
AppWidgetHost host1 = new AppWidgetHost(getInstrumentation().getTargetContext(), 1);
AppWidgetHost host2 = new AppWidgetHost(getInstrumentation().getTargetContext(), 2);
@@ -208,10 +189,6 @@
}
public void testAppWidgetProviderCallbacks() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
AtomicInteger invocationCounter = new AtomicInteger();
// Set a mock to intercept provider callbacks.
@@ -319,10 +296,6 @@
}
public void testTwoAppWidgetProviderCallbacks() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
AtomicInteger invocationCounter = new AtomicInteger();
// Set a mock to intercept first provider callbacks.
@@ -414,10 +387,6 @@
}
public void testGetAppWidgetIdsForProvider() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -465,10 +434,6 @@
}
public void testGetAppWidgetInfo() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -521,10 +486,6 @@
}
public void testGetAppWidgetOptions() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -570,10 +531,6 @@
}
public void testDeleteHost() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -615,10 +572,6 @@
}
public void testDeleteHosts() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -675,10 +628,6 @@
}
public void testOnProvidersChanged() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -741,10 +690,6 @@
}
public void testUpdateAppWidgetViaComponentName() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -835,10 +780,6 @@
}
public void testUpdateAppWidgetViaWidgetId() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -909,10 +850,6 @@
}
public void testUpdateAppWidgetViaWidgetIds() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -1005,10 +942,6 @@
}
public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -1083,10 +1016,6 @@
}
public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -1197,10 +1126,6 @@
}
public void testCollectionWidgets() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
-
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -1310,9 +1235,6 @@
}
public void testWidgetFeaturesParsed() throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
assertEquals(0, getFirstAppWidgetProviderInfo().widgetFeatures);
String packageName = getInstrumentation().getTargetContext().getPackageName();
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
index fb0dbd1..b98973a 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTestCase.java
@@ -16,6 +16,8 @@
package android.appwidget.cts;
+import static org.junit.Assume.assumeTrue;
+
import android.appwidget.AppWidgetProviderInfo;
import android.appwidget.cts.provider.FirstAppWidgetProvider;
import android.appwidget.cts.provider.SecondAppWidgetProvider;
@@ -36,6 +38,12 @@
private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
"android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ assumeTrue(hasAppWidgets());
+ }
+
public boolean hasAppWidgets() {
return getInstrumentation().getTargetContext().getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
index 019fb66..4f6c657 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
@@ -52,9 +52,6 @@
}
private void runPinWidgetTest(final String launcherPkg) throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
setLauncher(launcherPkg + "/" + LAUNCHER_CLASS);
Context context = getInstrumentation().getContext();
@@ -109,9 +106,6 @@
public void verifyIsRequestPinAppWidgetSupported(String launcherPkg, boolean expectedSupport)
throws Exception {
- if (!hasAppWidgets()) {
- return;
- }
setLauncher(launcherPkg + "/" + LAUNCHER_CLASS);
Context context = getInstrumentation().getContext();
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java b/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java
new file mode 100644
index 0000000..2ec8cf7
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/UpdateProviderInfoTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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 android.appwidget.cts;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.cts.common.Constants;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Process;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class UpdateProviderInfoTest extends AppWidgetTestCase {
+
+ private static final String PROVIDER_PACKAGE = "android.appwidget.cts.widgetprovider";
+ private static final String PROVIDER_CLASS = "android.appwidget.cts.packages.SimpleProvider";
+
+ private static final String APK_PATH = "data/local/tmp/cts/widgetprovider/";
+ private static final String APK_V1 = APK_PATH + "CtsAppWidgetProvider1.apk";
+ private static final String APK_V2 = APK_PATH + "CtsAppWidgetProvider2.apk";
+ private static final String APK_V3 = APK_PATH + "CtsAppWidgetProvider3.apk";
+
+ private static final String EXTRA_CUSTOM_INFO = "my_custom_info";
+
+ private static final int HOST_ID = 42;
+
+ private static final int RETRY_COUNT = 3;
+
+ private CountDownLatch mProviderChangeNotifier;
+ AppWidgetHost mHost;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ uninstallProvider();
+ createHost();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ uninstallProvider();
+
+ if (mHost != null) {
+ mHost.deleteHost();
+ }
+ }
+
+ public void testInfoOverrides() throws Throwable {
+ // On first install the provider does not have any config activity.
+ installApk(APK_V1);
+ assertNull(getProviderInfo().configure);
+
+ // The provider info is updated
+ updateInfo(EXTRA_CUSTOM_INFO);
+ assertNotNull(getProviderInfo().configure);
+
+ // The provider info is updated
+ updateInfo(null);
+ assertNull(getProviderInfo().configure);
+ }
+
+ public void testOverridesPersistedOnUpdate() throws Exception {
+ installApk(APK_V1);
+ assertNull(getProviderInfo().configure);
+
+ updateInfo(EXTRA_CUSTOM_INFO);
+ assertNotNull(getProviderInfo().configure);
+ assertEquals((AppWidgetProviderInfo.RESIZE_BOTH & getProviderInfo().resizeMode),
+ AppWidgetProviderInfo.RESIZE_BOTH);
+
+ // Apk updated, the info is also updated
+ installApk(APK_V2);
+ assertNotNull(getProviderInfo().configure);
+ assertEquals((AppWidgetProviderInfo.RESIZE_BOTH & getProviderInfo().resizeMode), 0);
+
+ // The provider info is reverted
+ updateInfo(null);
+ assertNull(getProviderInfo().configure);
+ }
+
+ public void testOverrideClearedWhenMissingInfo() throws Exception {
+ installApk(APK_V1);
+ assertNull(getProviderInfo().configure);
+
+ updateInfo(EXTRA_CUSTOM_INFO);
+ assertNotNull(getProviderInfo().configure);
+
+ // V3 does not have the custom info definition
+ installApk(APK_V3);
+ assertNull(getProviderInfo().configure);
+ }
+
+ private void createHost() throws Exception {
+ try {
+ runTestOnUiThread(() -> {
+ mHost = new AppWidgetHost(getInstrumentation().getTargetContext(), HOST_ID) {
+
+ @Override
+ protected void onProvidersChanged() {
+ super.onProvidersChanged();
+
+ if (mProviderChangeNotifier != null) {
+ mProviderChangeNotifier.countDown();
+ }
+ }
+ };
+ mHost.startListening();
+ });
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+
+ private void updateInfo(String key) throws Exception {
+ mProviderChangeNotifier = new CountDownLatch(1);
+ Intent intent = new Intent(Constants.ACTION_APPLY_OVERRIDE)
+ .setComponent(new ComponentName(PROVIDER_PACKAGE, PROVIDER_CLASS))
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ .putExtra(Constants.EXTRA_REQUEST, key);
+ getInstrumentation().getTargetContext().sendBroadcast(intent);
+
+ // Wait until the app widget manager is notified
+ mProviderChangeNotifier.await();
+ }
+
+ private void uninstallProvider() throws Exception {
+ runShellCommand("pm uninstall " + PROVIDER_PACKAGE);
+ }
+
+ private void installApk(String path) throws Exception {
+ mProviderChangeNotifier = new CountDownLatch(1);
+ runShellCommand("pm install -r -d " + path);
+
+ // Wait until the app widget manager is notified
+ mProviderChangeNotifier.await();
+ }
+
+ private AppWidgetProviderInfo getProviderInfo() throws Exception {
+ for (int i = 0; i < RETRY_COUNT; i++) {
+ mProviderChangeNotifier = new CountDownLatch(1);
+ List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(getInstrumentation()
+ .getTargetContext()).getInstalledProvidersForPackage(
+ PROVIDER_PACKAGE, Process.myUserHandle());
+
+ if (providers != null && !providers.isEmpty()) {
+ return providers.get(0);
+ }
+
+ // Sometimes it could take time for the info to appear after the apk is just installed
+ mProviderChangeNotifier.await(2, TimeUnit.SECONDS);
+ }
+ return null;
+ }
+}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java
new file mode 100644
index 0000000..372f22c
--- /dev/null
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/ApnDatabaseTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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 android.carrierapi.cts;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.provider.Telephony.Carriers;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+/**
+ * Build, install and run the tests by running the commands below:
+ * make cts -j64
+ * cts-tradefed run cts -m CtsCarrierApiTestCases --test android.carrierapi.cts.ApnDatabaseTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class ApnDatabaseTest {
+ private static final String TAG = "ApnDatabaseTest";
+
+ private ContentResolver mContentResolver;
+
+ private static final String NAME = "carrierName";
+ private static final String APN = "apn";
+ private static final String PROXY = "proxy";
+ private static final String PORT = "port";
+ private static final String MMSC = "mmsc";
+ private static final String MMSPROXY = "mmsproxy";
+ private static final String MMSPORT = "mmsport";
+ private static final String NUMERIC = "numeric";
+ private static final String USER = "user";
+ private static final String PASSWORD = "password";
+ private static final String AUTH_TYPE = "auth_type";
+ private static final String TYPE = "type";
+ private static final String PROTOCOL = "protocol";
+ private static final String ROAMING_PROTOCOL = "roaming_protocol";
+ private static final String CARRIER_ENABLED = "true";
+ private static final String NETWORK_TYPE_BITMASK = "0";
+ private static final String BEARER = "0";
+
+ private static final Map<String, String> APN_MAP = new HashMap<String,String>() {{
+ put(Carriers.NAME, NAME);
+ put(Carriers.APN, APN);
+ put(Carriers.PROXY, PROXY);
+ put(Carriers.PORT, PORT);
+ put(Carriers.MMSC, MMSC);
+ put(Carriers.MMSPROXY, MMSPROXY);
+ put(Carriers.MMSPORT, MMSPORT);
+ put(Carriers.NUMERIC, NUMERIC);
+ put(Carriers.USER, USER);
+ put(Carriers.PASSWORD, PASSWORD);
+ put(Carriers.AUTH_TYPE, AUTH_TYPE);
+ put(Carriers.TYPE, TYPE);
+ put(Carriers.PROTOCOL, PROTOCOL);
+ put(Carriers.ROAMING_PROTOCOL, ROAMING_PROTOCOL);
+ put(Carriers.CARRIER_ENABLED, CARRIER_ENABLED);
+ put(Carriers.NETWORK_TYPE_BITMASK, NETWORK_TYPE_BITMASK);
+ put(Carriers.BEARER, BEARER);
+ }};
+
+ // Faked network type bitmask and its compatible bearer bitmask.
+ private static final int NETWORK_TYPE_BITMASK_NUMBER = 1 << (13 - 1);
+ private static final int RIL_RADIO_TECHNOLOGY_BITMASK_NUMBER = 1 << (14 - 1);
+
+ @Before
+ public void setUp() throws Exception {
+ mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
+ }
+
+ private void failMessage() {
+ fail("This test requires a SIM card with carrier privilege rule on it.\n" +
+ "Visit https://source.android.com/devices/tech/config/uicc.html");
+ }
+
+ /**
+ * Test inserting, querying, updating and deleting values in carriers table.
+ * Verify that the inserted values match the result of the query and are deleted.
+ */
+ @Test
+ public void testValidCase() {
+ Uri uri = Carriers.CONTENT_URI;
+ // CONTENT_URI = Uri.parse("content://telephony/carriers");
+ // Create A set of column_name/value pairs to add to the database.
+ ContentValues contentValues = makeDefaultContentValues();
+
+ try {
+ // Insert the value into database.
+ Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+ Uri newUri = mContentResolver.insert(uri, contentValues);
+ assertNotNull("Failed to insert to table", newUri);
+
+ // Get the values in table.
+ final String selection = Carriers.NUMERIC + "=?";
+ String[] selectionArgs = { NUMERIC };
+ String[] apnProjection = APN_MAP.keySet().toArray(new String[APN_MAP.size()]);
+ Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(apnProjection)
+ + "\ntestInsertCarriers selection: " + selection
+ + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ Cursor cursor = mContentResolver.query(
+ uri, apnProjection, selection, selectionArgs, null);
+
+ // Verify that the inserted value match the results of the query
+ assertNotNull("Failed to query the table", cursor);
+ assertEquals("Unexpected number of APNs returned by cursor",
+ 1, cursor.getCount());
+ cursor.moveToFirst();
+ for (Map.Entry<String, String> entry: APN_MAP.entrySet()) {
+ assertEquals(
+ "Unexpected value returned by cursor",
+ cursor.getString(cursor.getColumnIndex(entry.getKey())), entry.getValue());
+ }
+
+ // update the apn
+ final String newApn = "newapn";
+ Log.d(TAG, "Update the APN field to: " + newApn);
+ contentValues.put(Carriers.APN, newApn);
+ final int updateCount = mContentResolver.update(uri, contentValues, selection,
+ selectionArgs);
+ assertEquals("Unexpected number of rows updated", 1, updateCount);
+
+ // Verify the updated value
+ cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null);
+ assertNotNull("Failed to query the table", cursor);
+ assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+ cursor.moveToFirst();
+ assertEquals("Unexpected value returned by cursor",
+ cursor.getString(cursor.getColumnIndex(Carriers.APN)), newApn);
+
+ // delete test content
+ final String selectionToDelete = Carriers.NUMERIC + "=?";
+ String[] selectionArgsToDelete = { NUMERIC };
+ Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+ + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ int numRowsDeleted = mContentResolver.delete(
+ uri, selectionToDelete, selectionArgsToDelete);
+ assertEquals("Unexpected number of rows deleted",1, numRowsDeleted);
+
+ // verify that deleted values are gone
+ cursor = mContentResolver.query(uri, apnProjection, selection, selectionArgs, null);
+ assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
+
+ @Test
+ public void testQueryConflictCase() {
+ String invalidColumn = "random";
+ Uri uri = Carriers.CONTENT_URI;
+ // CONTENT_URI = Uri.parse("content://telephony/carriers");
+ // Create A set of column_name/value pairs to add to the database.
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Carriers.NAME, NAME);
+ contentValues.put(Carriers.APN, APN);
+ contentValues.put(Carriers.PORT, PORT);
+ contentValues.put(Carriers.PROTOCOL, PROTOCOL);
+ contentValues.put(Carriers.NUMERIC, NUMERIC);
+
+ try {
+ // Insert the value into database.
+ Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+ Uri newUri = mContentResolver.insert(uri, contentValues);
+ assertNotNull("Failed to insert to table", newUri);
+
+ // Try to get the value with invalid selection
+ final String[] testProjection =
+ {
+ Carriers.NAME,
+ Carriers.APN,
+ Carriers.PORT,
+ Carriers.PROTOCOL,
+ Carriers.NUMERIC,
+ };
+ final String selection = invalidColumn + "=?";
+ String[] selectionArgs = { invalidColumn };
+ Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
+ + "\ntestInsertCarriers selection: " + selection
+ + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ Cursor cursor = mContentResolver.query(
+ uri, testProjection, selection, selectionArgs, null);
+ assertNull("Failed to query the table",cursor);
+
+ // delete test content
+ final String selectionToDelete = Carriers.NAME + "=?";
+ String[] selectionArgsToDelete = { NAME };
+ Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+ + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ int numRowsDeleted = mContentResolver.delete(
+ uri, selectionToDelete, selectionArgsToDelete);
+ assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+
+ // verify that deleted values are gone
+ cursor = mContentResolver.query(
+ uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
+ assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
+
+ @Test
+ public void testUpdateConflictCase() {
+ Uri uri = Carriers.CONTENT_URI;
+ // CONTENT_URI = Uri.parse("content://telephony/carriers");
+ // Create A set of column_name/value pairs to add to the database.
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Carriers.NAME, NAME);
+ contentValues.put(Carriers.APN, APN);
+ contentValues.put(Carriers.PORT, PORT);
+ contentValues.put(Carriers.PROTOCOL, PROTOCOL);
+ contentValues.put(Carriers.NUMERIC, NUMERIC);
+
+ try {
+ // Insert the value into database.
+ Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+ Uri newUri = mContentResolver.insert(uri, contentValues);
+ assertNotNull("Failed to insert to table", newUri);
+
+ // Try to get the value with invalid selection
+ final String[] testProjection =
+ {
+ Carriers.NAME,
+ Carriers.APN,
+ Carriers.PORT,
+ Carriers.PROTOCOL,
+ Carriers.NUMERIC,
+ };
+ String selection = Carriers.NAME + "=?";
+ String[] selectionArgs = { NAME };
+ Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
+ + "\ntestInsertCarriers selection: " + selection
+ + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ Cursor cursor = mContentResolver.query(
+ uri, testProjection, selection, selectionArgs, null);
+ assertEquals("Unexpected number of APNs returned by cursor",
+ 1, cursor.getCount());
+
+ // Update the table with invalid column
+ String invalidColumn = "random";
+ contentValues.put(invalidColumn, invalidColumn);
+ try {
+ mContentResolver.update(uri, contentValues, selection, selectionArgs);
+ fail();
+ } catch (SQLiteException e) {
+ // Expected: If there's no such a column, an exception will be thrown and the
+ // Activity Manager will kill this process shortly.
+ }
+
+ // delete test content
+ final String selectionToDelete = Carriers.NAME + "=?";
+ String[] selectionArgsToDelete = { NAME };
+ Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+ + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ int numRowsDeleted = mContentResolver.delete(
+ uri, selectionToDelete, selectionArgsToDelete);
+ assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+
+ // verify that deleted values are gone
+ cursor = mContentResolver.query(
+ uri, testProjection, selectionToDelete, selectionArgsToDelete, null);
+ assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
+
+ @Test
+ public void testDeleteConflictCase() {
+ String invalidColumn = "random";
+ Uri uri = Carriers.CONTENT_URI;
+ // CONTENT_URI = Uri.parse("content://telephony/carriers");
+ // Create A set of column_name/value pairs to add to the database.
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(Carriers.NAME, NAME);
+ contentValues.put(Carriers.APN, APN);
+ contentValues.put(Carriers.PORT, PORT);
+ contentValues.put(Carriers.PROTOCOL, PROTOCOL);
+ contentValues.put(Carriers.NUMERIC, NUMERIC);
+
+ try {
+ // Insert the value into database.
+ Log.d(TAG, "testInsertCarriers Inserting contentValues: " + contentValues.toString());
+ Uri newUri = mContentResolver.insert(uri, contentValues);
+ assertNotNull("Failed to insert to table", newUri);
+
+ // Get the values in table.
+ final String[] testProjection =
+ {
+ Carriers.NAME,
+ Carriers.APN,
+ Carriers.PORT,
+ Carriers.PROTOCOL,
+ Carriers.NUMERIC,
+ };
+ String selection = Carriers.NAME + "=?";
+ String[] selectionArgs = { NAME };
+ Log.d(TAG, "testInsertCarriers query projection: " + Arrays.toString(testProjection)
+ + "\ntestInsertCarriers selection: " + selection
+ + "\ntestInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ Cursor cursor = mContentResolver.query(
+ uri, testProjection, selection, selectionArgs, null);
+ assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+
+ // try to delete with invalid selection
+ String selectionToDelete = invalidColumn + "=?";
+ String[] selectionArgsToDelete = { invalidColumn };
+ Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+ + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+
+ try {
+ mContentResolver.delete(uri, selectionToDelete, selectionArgsToDelete);
+ fail();
+ } catch (SQLiteException e) {
+ // Expected: If there's no such a column, an exception will be thrown and the
+ // Activity Manager will kill this process shortly.
+ }
+
+ // verify that deleted value is still there
+ selection = Carriers.NAME + "=?";
+ selectionArgs[0] = NAME;
+ cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+ assertEquals("Unexpected number of APNs returned by cursor", 1, cursor.getCount());
+
+ // delete test content
+ selectionToDelete = Carriers.NAME + "=?";
+ selectionArgsToDelete[0] = NAME;
+ Log.d(TAG, "testInsertCarriers deleting selection: " + selectionToDelete
+ + "testInsertCarriers selectionArgs: " + Arrays.toString(selectionArgs));
+ int numRowsDeleted = mContentResolver.delete(
+ uri, selectionToDelete, selectionArgsToDelete);
+ assertEquals("Unexpected number of rows deleted", 1, numRowsDeleted);
+
+ // verify that deleted values are gone
+ cursor = mContentResolver.query(uri, testProjection, selection, selectionArgs, null);
+ assertEquals("Unexpected number of rows deleted", 0, cursor.getCount());
+ } catch (SecurityException e) {
+ failMessage();
+ }
+ }
+
+ private ContentValues makeDefaultContentValues() {
+ ContentValues contentValues = new ContentValues();
+ for (Map.Entry<String, String> entry: APN_MAP.entrySet()) {
+ contentValues.put(entry.getKey(), entry.getValue());
+ }
+ return contentValues;
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index e340d00..1fa78d3 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -31,7 +31,8 @@
ctstestrunner \
services.core \
junit \
- truth-prebuilt
+ truth-prebuilt \
+ accountaccesslib
LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v4
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index b191a11..eb6fecb 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -29,6 +29,7 @@
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.content.cts.permission.TEST_GRANTED" />
<!-- Used for PackageManager test, don't delete this INTERNET permission -->
@@ -288,6 +289,16 @@
android:process=":providerProcess">
</provider>
+ <activity android:name="com.android.cts.content.StubActivity"/>
+
+ <service android:name="com.android.cts.content.SyncService">
+ <intent-filter>
+ <action android:name="android.content.SyncAdapter"/>
+ </intent-filter>
+ <meta-data android:name="android.content.SyncAdapter"
+ android:resource="@xml/accountaccesssyncadapter" />
+ </service>
+
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml
index 5cab0ac..1fd262f 100644
--- a/tests/tests/content/AndroidTest.xml
+++ b/tests/tests/content/AndroidTest.xml
@@ -31,6 +31,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsContentTestCases.apk" />
+ <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
similarity index 81%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
index 0979e96..09523ba 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/Android.mk
@@ -24,11 +24,14 @@
android-support-test \
ctstestrunner \
ub-uiautomator \
- compatibility-device-util
+ compatibility-device-util \
+ accountaccesslib
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessSameCertTestCases
+LOCAL_PACKAGE_NAME := CtsSyncAccountAccessOtherCertTestCases
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
@@ -36,4 +39,4 @@
LOCAL_DEX_PREOPT := false
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidManifest.xml
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml
new file mode 100644
index 0000000..cc6da02
--- /dev/null
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS tests of sync adapters with different certs as the authenticator">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsSyncAccountAccessOtherCertTestCases.apk" />
+ <option name="test-file-name" value="CtsSyncAccountAccessStubs.apk" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.cts.content" />
+ <option name="runtime-hint" value="30s" />
+ </test>
+</configuration>
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
similarity index 88%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
rename to tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index e732f81..95bf60c 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -16,16 +16,18 @@
package com.android.cts.content;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
-import android.content.ContentProviderClient;
+import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SyncRequest;
-import android.content.SyncResult;
-import android.content.cts.FlakyTestRule;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.ConnectivityManager;
@@ -47,11 +49,9 @@
import org.junit.runner.RunWith;
import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import com.android.compatibility.common.util.SystemUtil;
@@ -63,7 +63,9 @@
private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec
- private static final String PERMISSION_REQUESTED = "Permission Requested";
+ private static final Pattern PERMISSION_REQUESTED = Pattern.compile(
+ "Permission Requested|Permission requested");
+ private static final Pattern ALLOW_SYNC = Pattern.compile("ALLOW|Allow");
public static final String TOKEN_TYPE_REMOVE_ACCOUNTS = "TOKEN_TYPE_REMOVE_ACCOUNTS";
@Rule
@@ -81,9 +83,8 @@
@Test
public void testAccountAccess_otherCertAsAuthenticatorCanNotSeeAccount() throws Exception {
- if (!hasDataConnection() || !hasNotificationSupport()) {
- return;
- }
+ assumeTrue(hasDataConnection());
+ assumeTrue(hasNotificationSupport());
Intent intent = new Intent(getContext(), StubActivity.class);
Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
@@ -99,11 +100,7 @@
waitForSyncManagerAccountChangeUpdate();
try {
- CountDownLatch latch = new CountDownLatch(1);
-
- SyncAdapter.setOnPerformSyncDelegate((Account account, Bundle extras,
- String authority, ContentProviderClient provider, SyncResult syncResult)
- -> latch.countDown());
+ AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
@@ -118,7 +115,8 @@
.build();
ContentResolver.requestSync(request);
- assertFalse(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
+ verify(adapter, timeout(SYNC_TIMEOUT_MILLIS).times(0)).onPerformSync(any(), any(),
+ any(), any(), any());
UiDevice uiDevice = getUiDevice();
if (isWatch()) {
@@ -132,14 +130,15 @@
uiDevice.findObject(By.text(PERMISSION_REQUESTED)).click();
}
- uiDevice.wait(Until.hasObject(By.text("ALLOW")),
+ uiDevice.wait(Until.hasObject(By.text(ALLOW_SYNC)),
UI_TIMEOUT_MILLIS);
- uiDevice.findObject(By.text("ALLOW")).click();
+ uiDevice.findObject(By.text(ALLOW_SYNC)).click();
ContentResolver.requestSync(request);
- assertTrue(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
+ verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
+ any());
} finally {
// Ask the differently signed authenticator to drop all accounts
accountManager.getAuthToken(addedAccount, TOKEN_TYPE_REMOVE_ACCOUNTS,
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/Android.mk b/tests/tests/content/SyncAccountAccessStubs/Android.mk
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/Android.mk
rename to tests/tests/content/SyncAccountAccessStubs/Android.mk
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/AndroidManifest.xml b/tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/AndroidManifest.xml
rename to tests/tests/content/SyncAccountAccessStubs/AndroidManifest.xml
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/res/xml/authenticator.xml b/tests/tests/content/SyncAccountAccessStubs/res/xml/authenticator.xml
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/res/xml/authenticator.xml
rename to tests/tests/content/SyncAccountAccessStubs/res/xml/authenticator.xml
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
rename to tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubAuthenticator.java
diff --git a/hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java b/tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
similarity index 100%
rename from hostsidetests/content/test-apps/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
rename to tests/tests/content/SyncAccountAccessStubs/src/com/android/cts/stub/StubProvider.java
diff --git a/tests/tests/content/lib/Android.mk b/tests/tests/content/lib/Android.mk
new file mode 100644
index 0000000..9aaa6ac
--- /dev/null
+++ b/tests/tests/content/lib/Android.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk b/tests/tests/content/lib/accountaccess/Android.mk
similarity index 60%
copy from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
copy to tests/tests/content/lib/accountaccess/Android.mk
index 0979e96..559dc90 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/Android.mk
+++ b/tests/tests/content/lib/accountaccess/Android.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
+# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,28 +11,17 @@
# 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)
+LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-support-test \
- ctstestrunner \
- ub-uiautomator \
- compatibility-device-util
-
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_PACKAGE_NAME := CtsSyncAccountAccessSameCertTestCases
+LOCAL_MODULE := accountaccesslib
-LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
-LOCAL_PROGUARD_ENABLED := disabled
-
-LOCAL_DEX_PREOPT := false
-
-include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
similarity index 97%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
index e5664f1..2608b97 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/FlakyTestRule.java
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/FlakyTestRule.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
*/
-package android.content.cts;
+package com.android.cts.content;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/StubActivity.java
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/StubActivity.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/StubActivity.java
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
similarity index 74%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
index e93a070..19fb8ee 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncAdapter.java
+++ b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncAdapter.java
@@ -16,6 +16,9 @@
package com.android.cts.content;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import android.accounts.Account;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
@@ -25,31 +28,33 @@
public class SyncAdapter extends AbstractThreadedSyncAdapter {
private static final Object sLock = new Object();
+ private static AbstractThreadedSyncAdapter sDelegate;
- private static OnPerformSyncDelegate sOnPerformSyncDelegate;
+ public static AbstractThreadedSyncAdapter setNewDelegate() {
+ AbstractThreadedSyncAdapter delegate = mock(AbstractThreadedSyncAdapter.class);
- public interface OnPerformSyncDelegate {
- void onPerformSync(Account account, Bundle extras, String authority,
- ContentProviderClient provider, SyncResult syncResult);
- }
-
- public static void setOnPerformSyncDelegate(OnPerformSyncDelegate delegate) {
synchronized (sLock) {
- sOnPerformSyncDelegate = delegate;
+ sDelegate = delegate;
}
+
+ return delegate;
}
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
}
+ private AbstractThreadedSyncAdapter getCopyOfDelegate() {
+ synchronized (sLock) {
+ return sDelegate;
+ }
+ }
+
@Override
public void onPerformSync(Account account, Bundle extras, String authority,
ContentProviderClient provider, SyncResult syncResult) {
- OnPerformSyncDelegate delegate;
- synchronized (sLock) {
- delegate = sOnPerformSyncDelegate;
- }
+ AbstractThreadedSyncAdapter delegate = getCopyOfDelegate();
+
if (delegate != null) {
delegate.onPerformSync(account, extras, authority, provider, syncResult);
}
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java b/tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
similarity index 100%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/SyncService.java
rename to tests/tests/content/lib/accountaccess/src/com.android.cts.content/SyncService.java
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml b/tests/tests/content/res/xml/accountaccesssyncadapter.xml
similarity index 100%
copy from hostsidetests/content/test-apps/CtsSyncAccountAccessOtherCertTests/res/xml/syncadapter.xml
copy to tests/tests/content/res/xml/accountaccesssyncadapter.xml
diff --git a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
similarity index 86%
rename from hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
rename to tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
index bfdd072..72785b5 100644
--- a/hostsidetests/content/test-apps/CtsSyncAccountAccessSameCertTests/src/com/android/cts/content/CtsSyncAccountAccessSameCertTestCases.java
+++ b/tests/tests/content/src/android/content/cts/AccountAccessSameCertTest.java
@@ -14,20 +14,21 @@
* limitations under the License.
*/
-package com.android.cts.content;
+package android.content.cts;
-import static junit.framework.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.Activity;
-import android.content.ContentProviderClient;
+import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SyncRequest;
-import android.content.SyncResult;
-import android.content.cts.FlakyTestRule;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
@@ -36,6 +37,12 @@
import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.cts.content.FlakyTestRule;
+import com.android.cts.content.StubActivity;
+import com.android.cts.content.SyncAdapter;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -44,16 +51,12 @@
import org.junit.runner.RunWith;
import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import com.android.compatibility.common.util.SystemUtil;
/**
* Tests whether a sync adapter can access accounts.
*/
@RunWith(AndroidJUnit4.class)
-public class CtsSyncAccountAccessSameCertTestCases {
+public class AccountAccessSameCertTest {
private static final long SYNC_TIMEOUT_MILLIS = 20000; // 20 sec
@Rule
@@ -71,9 +74,8 @@
@Test
public void testAccountAccess_sameCertAsAuthenticatorCanSeeAccount() throws Exception {
- if (!hasDataConnection() || !hasNotificationSupport()) {
- return;
- }
+ assumeTrue(hasDataConnection());
+ assumeTrue(hasNotificationSupport());
Intent intent = new Intent(getContext(), StubActivity.class);
Activity activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent);
@@ -89,11 +91,7 @@
waitForSyncManagerAccountChangeUpdate();
try {
- CountDownLatch latch = new CountDownLatch(1);
-
- SyncAdapter.setOnPerformSyncDelegate((Account account, Bundle extras,
- String authority, ContentProviderClient provider, SyncResult syncResult)
- -> latch.countDown());
+ AbstractThreadedSyncAdapter adapter = SyncAdapter.setNewDelegate();
Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, true);
@@ -108,7 +106,8 @@
.build();
ContentResolver.requestSync(request);
- assertTrue(latch.await(SYNC_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS));
+ verify(adapter, timeout(SYNC_TIMEOUT_MILLIS)).onPerformSync(any(), any(), any(), any(),
+ any());
} finally {
accountManager.removeAccount(addedAccount, activity, null, null);
activity.finish();
diff --git a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
index 7a97434..e17d0de 100644
--- a/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
+++ b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
@@ -28,13 +28,9 @@
import android.os.Process;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.AttributeSet;
import com.android.compatibility.common.util.SystemUtil;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -204,30 +200,11 @@
*/
@SmallTest
public void testCantSetModeForSelf() {
- boolean gotToTest = false;
try {
- Method setMode = mAppOps.getClass().getMethod("setMode", int.class, int.class,
- String.class, int.class);
- int writeSmsOp = mAppOps.getClass().getField("OP_WRITE_SMS").getInt(mAppOps);
- gotToTest = true;
- setMode.invoke(mAppOps, writeSmsOp, mMyUid, mOpPackageName, AppOpsManager.MODE_ALLOWED);
+ int writeSmsOp = AppOpsManager.permissionToOpCode("android.permission.WRITE_SMS");
+ mAppOps.setMode(writeSmsOp, mMyUid, mOpPackageName, AppOpsManager.MODE_ALLOWED);
fail("Was able to set mode for self");
- } catch (NoSuchFieldException e) {
- throw new AssertionError("Unable to find OP_WRITE_SMS", e);
- } catch (NoSuchMethodException e) {
- throw new AssertionError("Unable to find setMode method", e);
- } catch (InvocationTargetException e) {
- if (!gotToTest) {
- throw new AssertionError("Whoops", e);
- }
- // If we got to the test, we want it to have thrown a security exception.
- // We need to look inside of the wrapper exception to see.
- Throwable t = e.getCause();
- if (!(t instanceof SecurityException)) {
- throw new AssertionError("Did not throw SecurityException", e);
- }
- } catch (IllegalAccessException e) {
- throw new AssertionError("Whoops", e);
+ } catch (SecurityException expected) {
}
}
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index c95898a..c72c8c1 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -183,15 +183,23 @@
<protected-broadcast
android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.BROWSE_CONNECTION_STATE_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.FOLDER_LIST" />
+ <protected-broadcast
+ android:name="android.bluetooth.avrcp-controller.profile.action.TRACK_EVENT" />
+ <protected-broadcast
android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
+ android:name="android.bluetooth.input.profile.action.IDLE_TIME_CHANGED" />
+ <protected-broadcast
android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
<protected-broadcast
- android:name="android.bluetooth.inputhost.profile.action.CONNECTION_STATE_CHANGED" />
+ android:name="android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast
android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" />
@@ -299,6 +307,8 @@
<protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
<protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
+ <protected-broadcast android:name="com.android.server.stats.action.TRIGGER_COLLECTION" />
+
<protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
<protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
<protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
@@ -318,6 +328,7 @@
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
<protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
<protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
<protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
@@ -396,6 +407,7 @@
<protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
<protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
<protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+ <protected-broadcast android:name="android.app.action.MANAGED_USER_CREATED" />
<!-- Added in N -->
<protected-broadcast android:name="android.intent.action.ANR" />
@@ -474,7 +486,6 @@
<protected-broadcast android:name="android.content.jobscheduler.JOB_DEADLINE_EXPIRED" />
<protected-broadcast android:name="android.intent.action.ACTION_UNSOL_RESPONSE_OEM_HOOK_RAW" />
<protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_SUPL" />
- <protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
<protected-broadcast android:name="android.os.action.LIGHT_DEVICE_IDLE_MODE_CHANGED" />
<protected-broadcast android:name="android.os.storage.action.VOLUME_STATE_CHANGED" />
<protected-broadcast android:name="android.os.storage.action.DISK_SCANNED" />
@@ -493,6 +504,8 @@
<protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_CHANGED" />
<protected-broadcast android:name="android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED" />
<protected-broadcast android:name="android.os.action.ACTION_EFFECTS_SUPPRESSOR_CHANGED" />
+ <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" />
+ <protected-broadcast android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" />
<protected-broadcast android:name="android.permission.GET_APP_GRANTED_URI_PERMISSIONS" />
<protected-broadcast android:name="android.permission.CLEAR_APP_GRANTED_URI_PERMISSIONS" />
@@ -1024,7 +1037,7 @@
<eat-comment />
<!-- Used for permissions that are associated with accessing
- camera or capturing images/video from the device. -->
+ body or environmental sensors. -->
<permission-group android:name="android.permission-group.SENSORS"
android:icon="@drawable/perm_group_sensors"
android:label="@string/permgrouplab_sensors"
@@ -1607,6 +1620,11 @@
<permission android:name="android.permission.ACCESS_PDB_STATE"
android:protectionLevel="signature" />
+ <!-- Allows testing if a passwords is forbidden by the admins.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.TEST_BLACKLISTED_PASSWORD"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows system update service to notify device owner about pending updates.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
@@ -1779,6 +1797,15 @@
<permission android:name="android.permission.ALLOCATE_AGGRESSIVE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide
+ Allows an application to use reserved disk space.
+ <p>Not for use by third-party applications. Should only be requested by
+ apps that provide core system functionality, to ensure system stability
+ when disk is otherwise completely full.
+ -->
+ <permission android:name="android.permission.USE_RESERVED_DISK"
+ android:protectionLevel="signature|privileged" />
+
<!-- ================================== -->
<!-- Permissions for screenlock -->
<!-- ================================== -->
@@ -2046,6 +2073,11 @@
<eat-comment />
<!-- Allows an application to install a shortcut in Launcher.
+ <p>In Android O (API level 26) and higher, the <code>INSTALL_SHORTCUT</code> broadcast no
+ longer has any effect on your app because it's a private, implicit
+ broadcast. Instead, you should create an app shortcut by using the
+ {@link android.content.pm.ShortcutManager#requestPinShortcut requestPinShortcut()}
+ method from the {@link android.content.pm.ShortcutManager} class.
<p>Protection level: normal
-->
<permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -2767,8 +2799,9 @@
android:protectionLevel="signature|appop" />
<!-- Allows an application to request deleting packages. Apps
- targeting APIs greater than 25 must hold this permission in
- order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE}.
+ targeting APIs {@link android.os.Build.VERSION_CODES#P} or greater must hold this
+ permission in order to use {@link android.content.Intent#ACTION_UNINSTALL_PACKAGE} or
+ {@link android.content.pm.PackageInstaller#uninstall}.
<p>Protection level: normal
-->
<permission android:name="android.permission.REQUEST_DELETE_PACKAGES"
@@ -3481,6 +3514,7 @@
@hide -->
<permission android:name="android.permission.LOCAL_MAC_ADDRESS"
android:protectionLevel="signature|privileged" />
+ <uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS"/>
<!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
@hide -->
@@ -3509,11 +3543,19 @@
@hide -->
<permission android:name="android.permission.ACCESS_INSTANT_APPS"
android:protectionLevel="signature|installer|verifier" />
+ <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
<!-- Allows the holder to view the instant applications on the device.
@hide -->
<permission android:name="android.permission.VIEW_INSTANT_APPS"
- android:protectionLevel="signature|preinstalled" />
+ android:protectionLevel="signature|preinstalled" />
+
+ <!-- Allows the holder to manage whether the system can bind to services
+ provided by instant apps. This permission is intended to protect
+ test/development fucntionality and should be used only in such cases.
+ @hide -->
+ <permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"
+ android:protectionLevel="signature" />
<!-- Allows receiving the usage of media resource e.g. video/audio codec and
graphic memory.
@@ -3608,6 +3650,11 @@
<permission android:name="android.permission.READ_RUNTIME_PROFILES"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to turn on / off quiet mode.
+ @hide <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MODIFY_QUIET_MODE"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
@@ -3665,7 +3712,7 @@
</activity-alias>
<activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
android:targetActivity="com.android.internal.app.IntentForwarderActivity"
- android:icon="@drawable/ic_corp_icon"
+ android:icon="@drawable/ic_corp_badge"
android:exported="true"
android:label="@string/managed_profile_label">
</activity-alias>
@@ -3960,6 +4007,14 @@
<service android:name="com.android.server.timezone.TimeZoneUpdateIdler"
android:permission="android.permission.BIND_JOB_SERVICE" >
</service>
- </application>
+
+ <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+
+ <service android:name="com.android.server.display.BrightnessIdleJob"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+ </service>
+</application>
</manifest>
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
index 3f4aea2..c0b4253 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
@@ -88,32 +88,46 @@
public void testVoicemailsTable() throws Exception {
final String[] VOICEMAILS_PROJECTION = new String[] {
- Voicemails._ID, Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION,
- Voicemails.IS_READ, Voicemails.SOURCE_PACKAGE, Voicemails.SOURCE_DATA,
- Voicemails.HAS_CONTENT, Voicemails.MIME_TYPE, Voicemails.TRANSCRIPTION,
+ Voicemails._ID,
+ Voicemails.NUMBER,
+ Voicemails.DATE,
+ Voicemails.DURATION,
+ Voicemails.NEW,
+ Voicemails.IS_READ,
+ Voicemails.SOURCE_PACKAGE,
+ Voicemails.SOURCE_DATA,
+ Voicemails.HAS_CONTENT,
+ Voicemails.MIME_TYPE,
+ Voicemails.TRANSCRIPTION,
Voicemails.PHONE_ACCOUNT_COMPONENT_NAME,
- Voicemails.PHONE_ACCOUNT_ID, Voicemails.DIRTY, Voicemails.DELETED,
- Voicemails.LAST_MODIFIED, Voicemails.BACKED_UP, Voicemails.RESTORED,
- Voicemails.ARCHIVED, Voicemails.IS_OMTP_VOICEMAIL};
+ Voicemails.PHONE_ACCOUNT_ID,
+ Voicemails.DIRTY,
+ Voicemails.DELETED,
+ Voicemails.LAST_MODIFIED,
+ Voicemails.BACKED_UP,
+ Voicemails.RESTORED,
+ Voicemails.ARCHIVED,
+ Voicemails.IS_OMTP_VOICEMAIL};
final int ID_INDEX = 0;
final int NUMBER_INDEX = 1;
final int DATE_INDEX = 2;
final int DURATION_INDEX = 3;
- final int IS_READ_INDEX = 4;
- final int SOURCE_PACKAGE_INDEX = 5;
- final int SOURCE_DATA_INDEX = 6;
- final int HAS_CONTENT_INDEX = 7;
- final int MIME_TYPE_INDEX = 8;
- final int TRANSCRIPTION_INDEX = 9;
- final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 10;
- final int PHONE_ACCOUNT_ID_INDEX = 11;
- final int DIRTY_INDEX = 12;
- final int DELETED_INDEX = 13;
- final int LAST_MODIFIED_INDEX = 14;
- final int BACKED_UP_INDEX = 15;
- final int RESTORED_INDEX = 16;
- final int ARCHIVED_INDEX = 17;
- final int IS_OMTP_VOICEMAIL_INDEX = 18;
+ final int NEW_INDEX = 4;
+ final int IS_READ_INDEX = 5;
+ final int SOURCE_PACKAGE_INDEX = 6;
+ final int SOURCE_DATA_INDEX = 7;
+ final int HAS_CONTENT_INDEX = 8;
+ final int MIME_TYPE_INDEX = 9;
+ final int TRANSCRIPTION_INDEX = 10;
+ final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 11;
+ final int PHONE_ACCOUNT_ID_INDEX = 12;
+ final int DIRTY_INDEX = 13;
+ final int DELETED_INDEX = 14;
+ final int LAST_MODIFIED_INDEX = 15;
+ final int BACKED_UP_INDEX = 16;
+ final int RESTORED_INDEX = 17;
+ final int ARCHIVED_INDEX = 18;
+ final int IS_OMTP_VOICEMAIL_INDEX = 19;
String insertCallsNumber = "0123456789";
long insertCallsDuration = 120;
@@ -131,6 +145,7 @@
value.put(Voicemails.NUMBER, insertCallsNumber);
value.put(Voicemails.DATE, insertDate);
value.put(Voicemails.DURATION, insertCallsDuration);
+ value.put(Voicemails.NEW, 0);
// Source package is expected to be inserted by the provider, if not set.
value.put(Voicemails.SOURCE_DATA, insertSourceData);
value.put(Voicemails.MIME_TYPE, insertMimeType);
@@ -158,6 +173,7 @@
assertEquals(mSourcePackageName, cursor.getString(SOURCE_PACKAGE_INDEX));
assertEquals(insertSourceData, cursor.getString(SOURCE_DATA_INDEX));
assertEquals(insertMimeType, cursor.getString(MIME_TYPE_INDEX));
+ assertEquals(0, cursor.getInt(NEW_INDEX));
assertEquals(0, cursor.getInt(IS_READ_INDEX));
assertEquals(1, cursor.getInt(HAS_CONTENT_INDEX));
assertEquals("foo", cursor.getString(TRANSCRIPTION_INDEX));
@@ -179,6 +195,7 @@
value.put(Voicemails.DATE, updateDate);
value.put(Voicemails.DURATION, updateCallsDuration);
value.put(Voicemails.SOURCE_DATA, updateSourceData);
+ value.put(Voicemails.NEW, 1);
value.put(Voicemails.DIRTY, 1);
value.put(Voicemails.DELETED, 1);
value.put(Voicemails.BACKED_UP, 1);
@@ -196,6 +213,7 @@
assertEquals(updateDate, cursor.getLong(DATE_INDEX));
assertEquals(updateCallsDuration, cursor.getLong(DURATION_INDEX));
assertEquals(updateSourceData, cursor.getString(SOURCE_DATA_INDEX));
+ assertEquals(1, cursor.getInt(NEW_INDEX));
assertEquals(1, cursor.getInt(DIRTY_INDEX));
assertEquals(1, cursor.getInt(DELETED_INDEX));
assertEquals(1, cursor.getInt(BACKED_UP_INDEX));
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
index 7cdfb55..0843eaf 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewDataDirTest.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.test.AndroidTestCase;
+import android.webkit.CookieManager;
import android.webkit.WebView;
import com.android.compatibility.common.util.NullWebViewUtils;
@@ -26,6 +27,10 @@
public class WebViewDataDirTest extends AndroidTestCase {
private static final long REMOTE_TIMEOUT_MS = 5000;
+ private static final String ALTERNATE_DIR_NAME = "test";
+ private static final String COOKIE_URL = "https://www.example.com/";
+ private static final String COOKIE_VALUE = "foo=main";
+ private static final String SET_COOKIE_PARAMS = "; Max-Age=86400";
static class TestDisableThenUseImpl extends TestProcessClient.TestRunnable {
@Override
@@ -68,4 +73,115 @@
process.run(TestUseThenDisableImpl.class, REMOTE_TIMEOUT_MS);
}
}
+
+ static class TestUseThenChangeDirImpl extends TestProcessClient.TestRunnable {
+ @Override
+ public void run(Context ctx) {
+ new WebView(ctx);
+ try {
+ WebView.setDataDirectorySuffix("test");
+ fail("didn't throw IllegalStateException");
+ } catch (IllegalStateException e) {}
+ }
+ }
+
+ public void testUseThenChangeDir() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+
+ try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+ process.run(TestUseThenChangeDirImpl.class, REMOTE_TIMEOUT_MS);
+ }
+ }
+
+ static class TestInvalidDirImpl extends TestProcessClient.TestRunnable {
+ @Override
+ public void run(Context ctx) {
+ try {
+ WebView.setDataDirectorySuffix("no/path/separators");
+ fail("didn't throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {}
+ }
+ }
+
+ public void testInvalidDir() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+
+ try (TestProcessClient process = TestProcessClient.createProcessA(getContext())) {
+ process.run(TestInvalidDirImpl.class, REMOTE_TIMEOUT_MS);
+ }
+ }
+
+ static class WebViewInDefaultDir extends TestProcessClient.TestRunnable {
+ @Override
+ public void run(Context ctx) {
+ new WebView(ctx);
+ }
+ }
+
+ static class TestDefaultDirDisallowed extends TestProcessClient.TestRunnable {
+ @Override
+ public void run(Context ctx) {
+ try {
+ new WebView(ctx);
+ fail("didn't throw RuntimeException");
+ } catch (RuntimeException e) {}
+ }
+ }
+
+ public void testSameDirTwoProcesses() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+
+ try (TestProcessClient processA = TestProcessClient.createProcessA(getContext());
+ TestProcessClient processB = TestProcessClient.createProcessB(getContext())) {
+ processA.run(WebViewInDefaultDir.class, REMOTE_TIMEOUT_MS);
+ processB.run(TestDefaultDirDisallowed.class, REMOTE_TIMEOUT_MS);
+ }
+ }
+
+ static class SetCookieInDefaultDir extends TestProcessClient.TestRunnable {
+ @Override
+ public void run(Context ctx) {
+ CookieManager cm = CookieManager.getInstance();
+ cm.setCookie(COOKIE_URL, COOKIE_VALUE + SET_COOKIE_PARAMS);
+ cm.flush();
+ }
+ }
+
+ static class TestCookieInDefaultDir extends TestProcessClient.TestRunnable {
+ @Override
+ public void run(Context ctx) {
+ CookieManager cm = CookieManager.getInstance();
+ String cookie = cm.getCookie(COOKIE_URL);
+ assertEquals("wrong cookie in default cookie jar", COOKIE_VALUE, cookie);
+ }
+ }
+
+ static class TestCookieInAlternateDir extends TestProcessClient.TestRunnable {
+ @Override
+ public void run(Context ctx) {
+ WebView.setDataDirectorySuffix(ALTERNATE_DIR_NAME);
+ CookieManager cm = CookieManager.getInstance();
+ String cookie = cm.getCookie(COOKIE_URL);
+ assertNull("cookie leaked to alternate cookie jar", cookie);
+ }
+ }
+
+ public void testCookieJarsSeparate() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+
+ try (TestProcessClient processA = TestProcessClient.createProcessA(getContext());
+ TestProcessClient processB = TestProcessClient.createProcessB(getContext())) {
+ processA.run(SetCookieInDefaultDir.class, REMOTE_TIMEOUT_MS);
+ processA.run(TestCookieInDefaultDir.class, REMOTE_TIMEOUT_MS);
+ processB.run(TestCookieInAlternateDir.class, REMOTE_TIMEOUT_MS);
+ }
+ }
}
diff --git a/tools/cts-api-coverage/proto/testsuite.proto b/tools/cts-api-coverage/proto/testsuite.proto
index 45a8a71..a0adb6f 100644
--- a/tools/cts-api-coverage/proto/testsuite.proto
+++ b/tools/cts-api-coverage/proto/testsuite.proto
@@ -23,6 +23,37 @@
// [END java_declaration]
// [START messages]
+message Option {
+ string name = 1;
+ string key = 2;
+ string value =3;
+}
+
+message ConfigMetadata {
+ string module_name = 1;
+ string component = 2;
+ repeated Option options = 3;
+
+ message TargetPreparer {
+ string test_class = 1;
+ repeated Option options = 2;
+ }
+ repeated TargetPreparer target_preparers = 4;
+
+ message TestClass {
+ string test_class = 1;
+ string package = 2;
+ repeated Option options = 3;
+ }
+ repeated TestClass test_classes = 5;
+}
+
+// target File Metadata for e.g. config, apk, jar, exe, so
+message FileMetadata {
+ string description = 1;
+ ConfigMetadata config_metadata = 2;
+}
+
// An entry in a Test Suire Release messages: cts, etc.
message Entry {
// Entry ID
@@ -50,9 +81,11 @@
string parent_id = 6;
// Relative path
string relative_path = 7;
+
+ FileMetadata file_metadata = 8;
}
-// A Test Suite Release: cts, etc.
+// Test Suite Release: cts, etc.
message TestSuiteContent {
// Entry ID
string id = 1;
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
index a1a6923..3c88938 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/GTestApiReport.java
@@ -128,7 +128,7 @@
for (File testConfigFile : testConfigFiles) {
XMLReader xmlReader = XMLReaderFactory.createXMLReader();
- TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler();
+ TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
xmlReader.setContentHandler(testModuleXmlHandler);
FileReader fileReader = null;
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
index 85423b7..8a17153 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestModuleConfigHandler.java
@@ -16,6 +16,8 @@
package com.android.cts.apicoverage;
+import com.android.cts.apicoverage.TestSuiteProto.*;
+
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
@@ -25,25 +27,65 @@
* TestModule.xml.
*/
class TestModuleConfigHandler extends DefaultHandler {
- private String mTestClassName;
- private String mModuleName;
- private Boolean inTestEle = false;
+ private static final String CONFIGURATION_TAG = "configuration";
+ private static final String DESCRIPTION_TAG = "description";
+ private static final String OPTION_TAG = "option";
+ private static final String TARGET_PREPARER_TAG = "target_preparer";
+ private static final String TEST_TAG = "test";
+ private static final String CLASS_TAG = "class";
+ private static final String NAME_TAG = "name";
+ private static final String KEY_TAG = "key";
+ private static final String VALUE_TAG = "value";
+ private static final String MODULE_NAME_TAG = "module-name";
+ private static final String GTEST_CLASS_TAG = "com.android.tradefed.testtype.GTest";
+
+ private FileMetadata.Builder mFileMetadata;
+ private ConfigMetadata.Builder mConfigMetadata;
+ private ConfigMetadata.TestClass.Builder mTestCase;
+ private ConfigMetadata.TargetPreparer.Builder mTargetPreparer;
+ private String mModuleName = null;
+
+ TestModuleConfigHandler(String configFileName) {
+ mFileMetadata = FileMetadata.newBuilder();
+ mConfigMetadata = ConfigMetadata.newBuilder();
+ mTestCase = null;
+ mTargetPreparer = null;
+ // Default Module Name is the Config File Name
+ mModuleName = configFileName.replaceAll(".config$", "");
+ }
@Override
public void startElement(String uri, String localName, String name, Attributes attributes)
throws SAXException {
super.startElement(uri, localName, name, attributes);
- if ("test".equalsIgnoreCase(localName)) {
- mTestClassName = attributes.getValue("class");
- inTestEle = true;
- } else if ("option".equalsIgnoreCase(localName)) {
- if (inTestEle) {
- String optName = attributes.getValue("name");
- if ("module-name".equalsIgnoreCase(optName)) {
- mModuleName = attributes.getValue("value");
+ if (CONFIGURATION_TAG.equalsIgnoreCase(localName)) {
+ if (null != attributes.getValue(DESCRIPTION_TAG)) {
+ mFileMetadata.setDescription(attributes.getValue(DESCRIPTION_TAG));
+ } else {
+ mFileMetadata.setDescription("WARNING: no description.");
+ }
+ } else if (TEST_TAG.equalsIgnoreCase(localName)) {
+ mTestCase = ConfigMetadata.TestClass.newBuilder();
+ mTestCase.setTestClass(attributes.getValue(CLASS_TAG));
+ } else if (TARGET_PREPARER_TAG.equalsIgnoreCase(localName)) {
+ mTargetPreparer = ConfigMetadata.TargetPreparer.newBuilder();
+ mTargetPreparer.setTestClass(attributes.getValue(CLASS_TAG));
+ } else if (OPTION_TAG.equalsIgnoreCase(localName)) {
+ Option.Builder option = Option.newBuilder();
+ option.setName(attributes.getValue(NAME_TAG));
+ option.setValue(attributes.getValue(VALUE_TAG));
+ String keyStr = attributes.getValue(KEY_TAG);
+ if (null != keyStr) {
+ option.setKey(keyStr);
+ }
+ if (null != mTestCase) {
+ mTestCase.addOptions(option);
+ if (GTEST_CLASS_TAG.equalsIgnoreCase(option.getName())) {
+ mModuleName = option.getValue();
}
- //System.out.println(String.format("%s: %s, %s, %s", localName, name, optName, attributes.getValue("value")));
+ } else if (null != mTargetPreparer) {
+ mTargetPreparer.addOptions(option);
}
}
}
@@ -51,8 +93,14 @@
@Override
public void endElement(String uri, String localName, String name) throws SAXException {
super.endElement(uri, localName, name);
- if ("test".equalsIgnoreCase(localName)) {
- inTestEle = false;
+ if (TEST_TAG.equalsIgnoreCase(localName)) {
+ mConfigMetadata.addTestClasses(mTestCase);
+ mTestCase = null;
+ } else if (TARGET_PREPARER_TAG.equalsIgnoreCase(localName)) {
+ mConfigMetadata.addTargetPreparers(mTargetPreparer);
+ mTargetPreparer = null;
+ } else if (CONFIGURATION_TAG.equalsIgnoreCase(localName)) {
+ mFileMetadata.setConfigMetadata(mConfigMetadata);
}
}
@@ -61,6 +109,11 @@
}
public String getTestClassName() {
- return mTestClassName;
+ //return the 1st Test Class
+ return mFileMetadata.getConfigMetadata().getTestClassesList().get(0).getTestClass();
+ }
+
+ public FileMetadata getFileMetadata() {
+ return mFileMetadata.build();
}
}
diff --git a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
index e9ab1be..044acdc 100644
--- a/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
+++ b/tools/cts-api-coverage/src/com/android/cts/apicoverage/TestSuiteContentReport.java
@@ -17,12 +17,15 @@
package com.android.cts.apicoverage;
-import com.android.cts.apicoverage.TestSuiteProto.Entry;
-import com.android.cts.apicoverage.TestSuiteProto.Entry.EntryType;
-import com.android.cts.apicoverage.TestSuiteProto.TestSuiteContent;
+import com.android.cts.apicoverage.TestSuiteProto.*;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.XMLReaderFactory;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -38,6 +41,31 @@
import java.util.List;
class TestSuiteContentReport {
+ // configuration option
+ private static final String NOT_SHARDABLE_TAG = "not-shardable";
+ // test class option
+ private static final String RUNTIME_HIT_TAG = "runtime-hint";
+ // com.android.tradefed.testtype.AndroidJUnitTest option
+ private static final String PACKAGE_TAG = "package";
+ // com.android.compatibility.common.tradefed.testtype.JarHostTest option
+ private static final String JAR_NAME_TAG = "jar";
+ // com.android.tradefed.testtype.GTest option
+ private static final String NATIVE_TEST_DEVICE_PATH_TAG = "native-test-device-path";
+ private static final String MODULE_TAG = "module-name";
+
+ private static final String SUITE_API_INSTALLER_TAG = "com.android.tradefed.targetprep.suite.SuiteApkInstaller";
+ private static final String JAR_HOST_TEST_TAG = "com.android.compatibility.common.tradefed.testtype.JarHostTest";
+ // com.android.tradefed.targetprep.suite.SuiteApkInstaller option
+ private static final String TEST_FILE_NAME_TAG = "test-file-name";
+ // com.android.compatibility.common.tradefed.targetprep.FilePusher option
+ private static final String PUSH_TAG = "push";
+
+ // Target File Extensions
+ private static final String CONFIG_EXT_TAG = ".config";
+ private static final String JAR_EXT_TAG = ".jar";
+ private static final String APK_EXT_TAG = ".apk";
+ private static final String SO_EXT_TAG = ".so";
+
private static void printUsage() {
System.out.println("Usage: test-suite-content-report [OPTION]...");
System.out.println();
@@ -73,6 +101,42 @@
return testSuiteContent.build();
}
+ // Parse a file
+ private static FileMetadata parseFileMetadata(Entry.Builder fEntry, File file)
+ throws Exception {
+ if (file.getName().endsWith(CONFIG_EXT_TAG)) {
+ fEntry.setType(Entry.EntryType.CONFIG);
+ return parseConfigFile(file);
+ } else if (file.getName().endsWith(APK_EXT_TAG)) {
+ fEntry.setType(Entry.EntryType.APK);
+ } else if (file.getName().endsWith(JAR_EXT_TAG)) {
+ fEntry.setType(Entry.EntryType.JAR);
+ } else if (file.getName().endsWith(SO_EXT_TAG)) {
+ fEntry.setType(Entry.EntryType.SO);
+ } else {
+ // Just file in general
+ fEntry.setType(Entry.EntryType.FILE);
+ }
+ return null;
+ }
+
+ private static FileMetadata parseConfigFile(File file)
+ throws Exception {
+ XMLReader xmlReader = XMLReaderFactory.createXMLReader();
+ TestModuleConfigHandler testModuleXmlHandler = new TestModuleConfigHandler(file.getName());
+ xmlReader.setContentHandler(testModuleXmlHandler);
+ FileReader fileReader = null;
+ try {
+ fileReader = new FileReader(file);
+ xmlReader.parse(new InputSource(fileReader));
+ return testModuleXmlHandler.getFileMetadata();
+ } finally {
+ if (null != fileReader) {
+ fileReader.close();
+ }
+ }
+ }
+
// Parse a folder to add all entries
private static Entry.Builder parseFolder(TestSuiteContent.Builder testSuiteContent, String fPath, String rPath)
throws IOException, NoSuchAlgorithmException {
@@ -94,10 +158,20 @@
fileEntry.setId(getId(fileRelativePath));
fileEntry.setName(file.getName());
fileEntry.setSize(file.length());
- fileEntry.setType(EntryType.FILE);
fileEntry.setContentId(getFileContentId(file));
fileEntry.setRelativePath(fileRelativePath);
fileEntry.setParentId(folderId);
+ try {
+ FileMetadata fMetadata = parseFileMetadata(fileEntry, file);
+ if (null != fMetadata) {
+ fileEntry.setFileMetadata(fMetadata);
+ }
+ } catch (Exception ex) {
+ System.err.println(
+ String.format("Cannot parse %s",
+ file.getAbsolutePath()));
+ ex.printStackTrace();
+ }
testSuiteContent.addFileEntries(fileEntry);
entryList.add(fileEntry.build());
folderSize += file.length();
@@ -113,7 +187,7 @@
folderEntry.setId(folderId);
folderEntry.setName(folderRelativePath);
folderEntry.setSize(folderSize);
- folderEntry.setType(EntryType.FOLDER);
+ folderEntry.setType(Entry.EntryType.FOLDER);
folderEntry.setContentId(getFolderContentId(folderEntry, entryList));
folderEntry.setRelativePath(folderRelativePath);
return folderEntry;
@@ -173,13 +247,68 @@
// Iterates though all test suite content and prints them.
static void Print(TestSuiteContent tsContent) {
- System.out.printf("no,name,size,relativePath,id,cid,pid\n");
+ //Header
+ System.out.printf("no,type,name,size,relative path,id,content id,parent id,description,test class");
+ // test class header
+ System.out.printf(",%s,%s,%s,%s,%s",
+ RUNTIME_HIT_TAG, PACKAGE_TAG, JAR_NAME_TAG, NATIVE_TEST_DEVICE_PATH_TAG, MODULE_TAG);
+ // target preparer header
+ System.out.printf(",%s,%s\n",
+ TEST_FILE_NAME_TAG, PUSH_TAG);
+
int i = 1;
- for(Entry entry: tsContent.getFileEntriesList()) {
- System.out.printf("%d,%s,%d,%s,%s,%s,%s\n", i++,
- entry.getName(), entry.getSize(),
- entry.getRelativePath(),
- entry.getId(), entry.getContentId(),entry.getParentId());
+ for (Entry entry: tsContent.getFileEntriesList()) {
+ System.out.printf("%d,%s,%s,%d,%s,%s,%s,%s",
+ i++, entry.getType(), entry.getName(), entry.getSize(),
+ entry.getRelativePath(), entry.getId(), entry.getContentId(),
+ entry.getParentId());
+ if (Entry.EntryType.CONFIG == entry.getType()) {
+ ConfigMetadata config = entry.getFileMetadata().getConfigMetadata();
+ System.out.printf(",%s", entry.getFileMetadata().getDescription());
+ List<Option> optList;
+ List<ConfigMetadata.TestClass> testClassesList = config.getTestClassesList();
+ String rtHit = "";
+ String pkg = "";
+ String jar = "";
+ String ntdPath = "";
+ String module = "";
+
+ for (ConfigMetadata.TestClass tClass : testClassesList) {
+ System.out.printf(",%s", tClass.getTestClass());
+ optList = tClass.getOptionsList();
+ for (Option opt : optList) {
+ if (RUNTIME_HIT_TAG.equalsIgnoreCase(opt.getName())) {
+ rtHit = rtHit + opt.getValue() + " ";
+ } else if (PACKAGE_TAG.equalsIgnoreCase(opt.getName())) {
+ pkg = pkg + opt.getValue() + " ";
+ } else if (JAR_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+ jar = jar + opt.getValue() + " ";
+ } else if (NATIVE_TEST_DEVICE_PATH_TAG.equalsIgnoreCase(opt.getName())) {
+ ntdPath = ntdPath + opt.getValue() + " ";
+ } else if (MODULE_TAG.equalsIgnoreCase(opt.getName())) {
+ module = module + opt.getValue() + " ";
+ }
+ }
+ }
+ System.out.printf(",%s,%s,%s,%s,%s", rtHit.trim(), pkg.trim(),
+ jar.trim(), module.trim(), ntdPath.trim());
+
+ List<ConfigMetadata.TargetPreparer> tPrepList = config.getTargetPreparersList();
+ String testFile = "";
+ String pushList = "";
+ for (ConfigMetadata.TargetPreparer tPrep : tPrepList) {
+ optList = tPrep.getOptionsList();
+ for (Option opt : optList) {
+ if (TEST_FILE_NAME_TAG.equalsIgnoreCase(opt.getName())) {
+ testFile = testFile + opt.getValue() + " ";
+ } else if (PUSH_TAG.equalsIgnoreCase(opt.getName())) {
+ pushList = pushList + opt.getValue() + " ";
+ }
+ }
+ }
+ System.out.printf(",%s,%s", testFile.trim(), pushList.trim());
+ }
+ System.out.printf("\n");
}
}
diff --git a/tools/cts-tradefed/Android.mk b/tools/cts-tradefed/Android.mk
index 953ef63..e6c4750 100644
--- a/tools/cts-tradefed/Android.mk
+++ b/tools/cts-tradefed/Android.mk
@@ -22,6 +22,7 @@
LOCAL_JAVA_RESOURCE_DIRS += ../../common/host-side/tradefed/res
LOCAL_MODULE := cts-tradefed-harness
LOCAL_JAVA_LIBRARIES += tradefed compatibility-host-util
+LOCAL_STATIC_JAVA_LIBRARIES := google-api-java-client-min-repackaged
include $(BUILD_HOST_JAVA_LIBRARY)
include $(CLEAR_VARS)
diff --git a/tools/cts-tradefed/res/config/cts-preconditions.xml b/tools/cts-tradefed/res/config/cts-preconditions.xml
index 21215df..0f0f9b7 100644
--- a/tools/cts-tradefed/res/config/cts-preconditions.xml
+++ b/tools/cts-tradefed/res/config/cts-preconditions.xml
@@ -21,7 +21,11 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
<option name="target" value="host" />
- <option name="config-filename" value="cts"/>
+ <!-- the name under which to find the configuration -->
+ <option name="config-filename" value="cts" />
+ <option name="extract-from-resource" value="true" />
+ <!-- the name of the resource inside the jar -->
+ <option name="dynamic-resource-name" value="cts-tradefed" />
</target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.StayAwakePreparer" />
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
index 512d8bd..5d5df59 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/tradefed/CtsTradefedTest.java
@@ -46,7 +46,9 @@
@Override
protected void tearDown() throws Exception {
- System.setProperty(PROPERTY_NAME, mOriginalProperty);
+ if (mOriginalProperty != null) {
+ System.setProperty(PROPERTY_NAME, mOriginalProperty);
+ }
super.tearDown();
}
diff --git a/tools/vm-tests-tf/src/util/build/D8BuildStep.java b/tools/vm-tests-tf/src/util/build/D8BuildStep.java
index cd97dba..1e2c281 100644
--- a/tools/vm-tests-tf/src/util/build/D8BuildStep.java
+++ b/tools/vm-tests-tf/src/util/build/D8BuildStep.java
@@ -19,7 +19,7 @@
import com.android.tools.r8.CompilationMode;
import com.android.tools.r8.D8;
import com.android.tools.r8.D8Command;
-import com.android.tools.r8.utils.OutputMode;
+import com.android.tools.r8.OutputMode;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -37,8 +37,7 @@
D8Command.builder()
.setMode(CompilationMode.DEBUG)
.setMinApiLevel(1000)
- .setEnableDesugaring(false)
- .setOutputMode(OutputMode.Indexed);
+ .setEnableDesugaring(false);
}
@Override
@@ -46,7 +45,7 @@
if (super.build()) {
try {
- builder.setOutputPath(Paths.get(outputFile.fileName.getAbsolutePath()));
+ builder.setOutput(Paths.get(outputFile.fileName.getAbsolutePath()), OutputMode.DexIndexed);
Files.find(
Paths.get(inputFile.fileName.getAbsolutePath()),
1000,