Merge "Camera: reduce number of buffers allocated in PerformanceTest" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 41f9dc3..975ac47 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -194,6 +194,7 @@
CtsSecurityTestCases \
CtsSignatureTestCases \
CtsSpeechTestCases \
+ CtsSystemUiTestCases \
CtsTelecomTestCases \
CtsTelecomTestCases2 \
CtsTelephonyTestCases \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index d0d5ff0..85e5b734 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1398,6 +1398,13 @@
</intent-filter>
</activity-alias>
+ <activity android:name=".managedprovisioning.AuthenticationBoundKeyTestActivity">
+ <intent-filter>
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
<activity android:name=".managedprovisioning.ByodFlowTestActivity"
android:launchMode="singleTask"
android:label="@string/provisioning_byod">
@@ -1425,6 +1432,8 @@
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_KEYGUARD_DISABLED_FEATURES" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_LOCKNOW" />
<action android:name="com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
@@ -1452,12 +1461,19 @@
<activity android:name=".managedprovisioning.CrossProfileTestActivity">
<intent-filter>
- <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE" />
<!-- We need to have at least one activity listening to this intent in the parent
to test if it is forwarded from the managed profile to the parent -->
<action android:name="android.provider.MediaStore.RECORD_SOUND" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_PERSONAL" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_WORK" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <category android:name="android.intent.category.DEFAULT"></category>
+ <data android:scheme="http" android:host="com.android.cts.verifier" />
+ </intent-filter>
</activity>
<activity android:name=".managedprovisioning.WorkStatusTestActivity">
@@ -1574,6 +1590,18 @@
android:value="android.software.live_tv" />
</activity>
+ <activity android:name=".tv.AppLinkTestActivity"
+ android:label="@string/tv_app_link_test"
+ android:launchMode="singleTask">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_tv" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.live_tv" />
+ </activity>
+
<activity android:name=".screenpinning.ScreenPinningTestActivity"
android:label="@string/screen_pinning_test">
<intent-filter>
diff --git a/apps/CtsVerifier/jni/verifier/Android.mk b/apps/CtsVerifier/jni/verifier/Android.mk
index 6bdf71c..4840e62 100644
--- a/apps/CtsVerifier/jni/verifier/Android.mk
+++ b/apps/CtsVerifier/jni/verifier/Android.mk
@@ -30,6 +30,6 @@
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_LDLIBS := -llog
+LOCAL_SHARED_LIBRARIES := liblog
include $(BUILD_SHARED_LIBRARY)
diff --git a/apps/CtsVerifier/res/drawable/app_link_img.png b/apps/CtsVerifier/res/drawable/app_link_img.png
new file mode 100644
index 0000000..851fc6f
--- /dev/null
+++ b/apps/CtsVerifier/res/drawable/app_link_img.png
Binary files differ
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 77566a3..68f9d82 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1278,6 +1278,23 @@
<string name="provisioning_byod_no_audio_capture_resolver">No audio capture app present. Skip test.</string>
<string name="provisioning_byod_capture_media_error">Error while capturing media from managed profile.</string>
+ <string name="provisioning_byod_auth_bound_key">Autentication-boud keys</string>
+ <string name="provisioning_byod_auth_bound_key_info">
+ This test verifies keystore cryptographic keys can be bound to device credentials.
+ These keys should only be available if there was a recent enough authentication.
+ </string>
+ <string name="provisioning_byod_auth_bound_key_instruction">
+ This test verifies keystore cryptographic keys can be bound to device lockscreen challenge or fingerprints (if available).
+ These keys should only be available if there was a recent enough authentication. \n
+
+ 1. Press "Set up" to open Security settings. Create a lockscreen password and if available, enroll a fingerprint.\n
+ 2. Go through the list of tests.\n
+ 3. Mark the overall test pass or fail.\n
+ 4. Once the set of tests are completed, remove the lockscreen challenge.
+ </string>
+ <string name="provisioning_byod_auth_bound_key_set_up">Set up</string>
+ <string name="provisioning_byod_lockscreen_bound_key">Lockscreen-bound key test</string>
+ <string name="provisioning_byod_fingerprint_bound_key">Fingerprint-bound key test</string>
<!-- Strings for DeskClock -->
<string name="deskclock_tests">Alarms and Timers Tests</string>
<string name="deskclock_tests_info">
@@ -1436,12 +1453,13 @@
<string name="provisioning_byod_profile_visible">Profile-aware accounts settings</string>
<string name="provisioning_byod_admin_visible">Profile-aware device administrator settings</string>
<string name="provisioning_byod_workapps_visible">Badged work apps visible in Launcher</string>
- <string name="provisioning_byod_cross_profile">Open app cross profiles</string>
- <string name="provisioning_byod_cross_profile_app_personal">
- You selected the CTS Verifier option.
- </string>
+ <string name="provisioning_byod_cross_profile_from_personal">Open app cross profiles from the personal side</string>
+ <string name="provisioning_byod_cross_profile_from_work">Open app cross profiles from the work side</string>
+ <string name="provisioning_app_linking">App links from the work side</string>
+ <string name="provisioning_byod_cross_profile_app_personal">You selected the personal option.</string>
<string name="provisioning_byod_cross_profile_app_work">You selected the Work option.</string>
- <string name="provisioning_byod_cross_profile_instruction">
+ <string name="provisioning_byod_cross_profile_app_ctsverifier"> You selected the ctsverifier option </string>
+ <string name="provisioning_byod_cross_profile_from_personal_instruction">
Please press the Go button to start an action.\n
\n
You should be asked to choose either \"CTS Verifier\" or \"Work\" to complete the action.
@@ -1449,6 +1467,25 @@
\n
Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
</string>
+ <string name="provisioning_byod_cross_profile_from_work_instruction">
+ Please press the Go button to start an action.\n
+ \n
+ You should be asked to choose either \"CTS Verifier\" or \"Personal\" to complete the action.
+ Pressing either should bring up a page stating your choice.\n
+ \n
+ Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
+ </string>
+ <string name="provisioning_byod_app_linking_instruction">
+ Please press the Go button to start an action.\n
+ \n
+ You should be asked to choose either \"CTS Verifier\" or \"Personal\" to complete the action.\n
+ - If you choose \"CTS Verifier\", you should see a page stating your chose \"CTS Verifier\".\n
+ - If you choose \"Personal\", you should be presented with another dialog between \"CTS Verifier\"
+ and some other apps. In this case, you should choose \"CTS verifier\".\n
+ You should then see a page stating you chose \"Personal\".\n
+ \n
+ Verify that you are prompted with the above choices and both options work as intended. Then mark this test accordingly.
+ </string>
<string name="provisioning_byod_keyguard_disabled_features">Keyguard disabled features</string>
<string name="provisioning_byod_keyguard_disabled_features_info">
This test exercises Keyguard Disabled Features. Follow instructions above.
@@ -1640,6 +1677,7 @@
<string name="provisioning_byod_send_share_intent">Send share intent</string>
<string name="provisioning_byod_cannot_resolve_beam_activity">Cannot find beam activity</string>
+ <string name="test_failed_cannot_start_intent">Cannot start the given intent.</string>
<string name="provisioning_byod_no_activity">Cannot communicate with activity in the work profile.</string>
<string name="provisioning_byod_delete_profile">Initiate deletion of work profile.</string>
<string name="provisioning_byod_profile_deleted">Work profile deleted.</string>
@@ -1875,7 +1913,6 @@
Do you see the programs named \"Dummy Program\" and their descriptions
"Dummy Program Description" in the EPG?
</string>
- <string name="tv_input_discover_test_yes">Yes</string>
<string name="tv_parental_control_test">TV app parental controls test</string>
<string name="tv_parental_control_test_info">
@@ -1964,6 +2001,24 @@
The playback position should be moved to the next position.
</string>
+ <string name="tv_app_link_test">TV app app-link test</string>
+ <string name="tv_app_link_test_info">
+ Verify that the bundled TV app supports linking to channel apps. If TV input service provides
+ links for its specific channels, TV app should show the links in a proper format.
+ </string>
+ <string name="tv_app_link_test_select_app_link">
+ Select the \"Launch TV app\" button, then check if you can see a menu with \"Cts App-Link Text\"
+ text in red background. If you see the link, select it to follow the link.
+ </string>
+ <string name="tv_app_link_test_verify_link_clicked">
+ The app-link must have been clicked and the activity should be changed correctly.
+ </string>
+ <string name="tv_input_link_test_verify_link_interface">
+ Do you see the app-link card similar to the image on the left?\n
+ 1) You should see the poster art image, but the color could be differ.\n
+ 2) You should see the text \"Cts App-Link Text\".\n
+ </string>
+
<string name="overlay_view_text">Overlay View Dummy Text</string>
<string name="fake_rating">Fake</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
index 789effa..91b8d93 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/DialogTestListActivity.java
@@ -23,6 +23,7 @@
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
@@ -41,6 +42,7 @@
* Instructions are shown on top of the screen and a test preparation button is provided.
*/
public abstract class DialogTestListActivity extends PassFailButtons.TestListActivity {
+ private final String TAG = "DialogTestListActivity";
private final int mLayoutId;
private final int mTitleStringId;
private final int mInfoStringId;
@@ -170,7 +172,13 @@
mCurrentTestPosition = position;
((DialogTestListItem)test).performTest(this);
} else {
- super.handleItemClick(l, v, position, id);
+ try {
+ super.handleItemClick(l, v, position, id);
+ } catch (ActivityNotFoundException e) {
+ Log.d(TAG, "handleItemClick() threw exception: ", e);
+ setTestResult(test, TestResult.TEST_RESULT_FAILED);
+ showToast(R.string.test_failed_cannot_start_intent);
+ }
}
}
@@ -196,7 +204,7 @@
// do nothing, override in subclass if needed
}
- protected void setTestResult(DialogTestListItem test, int result) {
+ protected void setTestResult(TestListAdapter.TestListItem test, int result) {
// Bundle result in an intent to feed into handleLaunchTestResult
Intent resultIntent = new Intent();
TestResult.addResultData(resultIntent, result, test.testName, /* testDetails */ null,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index 51e0a62..3c108da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -232,6 +232,7 @@
public static final Feature[] ALL_MNC_FEATURES = {
new Feature(PackageManager.FEATURE_MIDI, false),
+ new Feature(PackageManager.FEATURE_AUDIO_PRO, false),
};
@Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
new file mode 100644
index 0000000..073412d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/AuthenticationBoundKeyTestActivity.java
@@ -0,0 +1,381 @@
+package com.android.cts.verifier.managedprovisioning;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.KeyguardManager;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.hardware.fingerprint.FingerprintManager;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.CountDownTimer;
+import android.provider.Settings;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyPermanentlyInvalidatedException;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.UserNotAuthenticatedException;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.DialogTestListActivity;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+
+/**
+ * Test device credential-bound keys in work profile.
+ * Currently there are two types, one is keys bound to lockscreen passwords which can be configured
+ * to remain available within a certain timeout after the latest successful user authentication.
+ * The other is keys bound to fingerprint authentication which require explicit fingerprint
+ * authentication before they can be accessed.
+ */
+public class AuthenticationBoundKeyTestActivity extends DialogTestListActivity {
+
+ public static final String ACTION_AUTH_BOUND_KEY_TEST =
+ "com.android.cts.verifier.managedprovisioning.action.AUTH_BOUND_KEY_TEST";
+
+ private static final int AUTHENTICATION_DURATION_SECONDS = 5;
+ private static final String LOCKSCREEN_KEY_NAME = "mp_lockscreen_key";
+ private static final String FINGERPRINT_KEY_NAME = "mp_fingerprint_key";
+ private static final byte[] SECRET_BYTE_ARRAY = new byte[] {1, 2, 3, 4, 5, 6};
+ private static final int CONFIRM_CREDENTIALS_REQUEST_CODE = 1;
+ private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0;
+
+ private static final int LOCKSCREEN = 1;
+ private static final int FINGERPRINT = 2;
+
+ private static final String KEYSTORE_NAME = "AndroidKeyStore";
+ private static final String CIPHER_TRANSFORMATION = KeyProperties.KEY_ALGORITHM_AES + "/"
+ + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7;
+
+
+ private KeyguardManager mKeyguardManager;
+ private FingerprintManager mFingerprintManager;
+ private boolean mFingerprintSupported;
+
+ private DialogTestListItem mLockScreenBoundKeyTest;
+ private DialogTestListItem mFingerprintBoundKeyTest;
+
+ private Cipher mFingerprintCipher;
+
+ public AuthenticationBoundKeyTestActivity() {
+ super(R.layout.provisioning_byod,
+ R.string.provisioning_byod_auth_bound_key,
+ R.string.provisioning_byod_auth_bound_key_info,
+ R.string.provisioning_byod_auth_bound_key_instruction);
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ mKeyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
+ mFingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
+ mFingerprintSupported = mFingerprintManager != null
+ && mFingerprintManager.isHardwareDetected();
+ // Need to have valid mFingerprintSupported value before calling super.onCreate() because
+ // mFingerprintSupported is used in setupTests() which gets called by super.onCreate().
+ super.onCreate(savedInstanceState);
+
+ mPrepareTestButton.setText(R.string.provisioning_byod_auth_bound_key_set_up);
+ mPrepareTestButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ startActivity(new Intent(Settings.ACTION_SECURITY_SETTINGS));
+ }
+ });
+ if (mFingerprintSupported) {
+ requestPermissions(new String[] {Manifest.permission.USE_FINGERPRINT},
+ FINGERPRINT_PERMISSION_REQUEST_CODE);
+ }
+ }
+
+ private class LockscreenCountDownTester extends CountDownTimer {
+
+ private Toast mToast;
+
+ public LockscreenCountDownTester() {
+ // Wait for AUTHENTICATION_DURATION_SECONDS so the key is evicted before the real test.
+ super(AUTHENTICATION_DURATION_SECONDS * 1000, 1000);
+ mToast = Toast.makeText(AuthenticationBoundKeyTestActivity.this, "", Toast.LENGTH_SHORT);
+ }
+
+ @Override
+ public void onFinish() {
+ mToast.cancel();
+ if (tryEncryptWithLockscreenKey()) {
+ showToast("Test failed. Key accessible without auth.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ } else {
+ // Start the Confirm Credentials screen.
+ Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
+ if (intent != null) {
+ startActivityForResult(intent, CONFIRM_CREDENTIALS_REQUEST_CODE);
+ } else {
+ showToast("Test failed. No lockscreen password exists.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ }
+ }
+
+ @Override
+ public void onTick(long millisUntilFinished) {
+ mToast.setText(String.format("Lockscreen challenge start in %d seconds..",
+ millisUntilFinished / 1000));
+ mToast.show();
+ }
+ }
+
+
+ @Override
+ protected void setupTests(ArrayTestListAdapter adapter) {
+ mLockScreenBoundKeyTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_lockscreen_bound_key,
+ "BYOD_LockScreenBoundKeyTest") {
+
+ @Override
+ public void performTest(DialogTestListActivity activity) {
+ if (checkPreconditions()) {
+ createKey(LOCKSCREEN);
+ new LockscreenCountDownTester().start();
+ }
+ }
+ };
+ adapter.add(mLockScreenBoundKeyTest);
+
+ if (mFingerprintSupported) {
+ mFingerprintBoundKeyTest = new DialogTestListItem(this,
+ R.string.provisioning_byod_fingerprint_bound_key,
+ "BYOD_FingerprintBoundKeyTest") {
+
+ @Override
+ public void performTest(DialogTestListActivity activity) {
+ if (checkPreconditions()) {
+ createKey(FINGERPRINT);
+ mFingerprintCipher = initFingerprintEncryptionCipher();
+ if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+ showToast("Test failed. Key accessible without auth.");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ } else {
+ new FingerprintAuthDialogFragment().show(getFragmentManager(),
+ "fingerprint_dialog");
+ }
+ }
+ }
+ };
+ adapter.add(mFingerprintBoundKeyTest);
+ }
+ }
+
+ private boolean checkPreconditions() {
+ if (!mKeyguardManager.isKeyguardSecure()) {
+ showToast("Please set a lockscreen password.");
+ return false;
+ } else if (mFingerprintSupported && !mFingerprintManager.hasEnrolledFingerprints()) {
+ showToast("Please enroll a fingerprint.");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private String getKeyName(int testType) {
+ return testType == LOCKSCREEN ? LOCKSCREEN_KEY_NAME : FINGERPRINT_KEY_NAME;
+ }
+ /**
+ * Creates a symmetric key in the Android Key Store which can only be used after the user has
+ * authenticated with device credentials.
+ */
+ private void createKey(int testType) {
+ try {
+ // Set the alias of the entry in Android KeyStore where the key will appear
+ // and the constrains (purposes) in the constructor of the Builder
+ KeyGenParameterSpec.Builder builder;
+ builder = new KeyGenParameterSpec.Builder(getKeyName(testType),
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setUserAuthenticationRequired(true)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7);
+ if (testType == LOCKSCREEN) {
+ // Require that the user unlocked the lockscreen in the last 5 seconds
+ builder.setUserAuthenticationValidityDurationSeconds(
+ AUTHENTICATION_DURATION_SECONDS);
+ }
+ KeyGenerator keyGenerator = KeyGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_NAME);
+ keyGenerator.init(builder.build());
+ keyGenerator.generateKey();
+ } catch (NoSuchAlgorithmException | NoSuchProviderException
+ | InvalidAlgorithmParameterException e) {
+ throw new RuntimeException("Failed to create a symmetric key", e);
+ }
+ }
+
+ private SecretKey loadSecretKey(int testType) {
+ try {
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_NAME);
+ keyStore.load(null);
+ return (SecretKey) keyStore.getKey(getKeyName(testType), null);
+ } catch (UnrecoverableKeyException | CertificateException |KeyStoreException | IOException
+ | NoSuchAlgorithmException e) {
+ throw new RuntimeException("Failed to load a symmetric key", e);
+ }
+ }
+
+ private boolean tryEncryptWithLockscreenKey() {
+ try {
+ // Try encrypting something, it will only work if the user authenticated within
+ // the last AUTHENTICATION_DURATION_SECONDS seconds.
+ Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(LOCKSCREEN));
+ cipher.doFinal(SECRET_BYTE_ARRAY);
+ return true;
+ } catch (UserNotAuthenticatedException e) {
+ // User is not authenticated, let's authenticate with device credentials.
+ return false;
+ } catch (KeyPermanentlyInvalidatedException e) {
+ // This happens if the lock screen has been disabled or reset after the key was
+ // generated.
+ createKey(LOCKSCREEN);
+ showToast("Set up lockscreen after test ran. Retry the test.");
+ return false;
+ } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException
+ | NoSuchPaddingException | NoSuchAlgorithmException e) {
+ throw new RuntimeException("Encrypt with lockscreen-bound key failed", e);
+ }
+ }
+
+ private Cipher initFingerprintEncryptionCipher() {
+ try {
+ Cipher cipher = Cipher.getInstance(CIPHER_TRANSFORMATION);
+ cipher.init(Cipher.ENCRYPT_MODE, loadSecretKey(FINGERPRINT));
+ return cipher;
+ } catch (NoSuchPaddingException | NoSuchAlgorithmException e) {
+ return null;
+ } catch (KeyPermanentlyInvalidatedException e) {
+ // This happens if the lock screen has been disabled or reset after the key was
+ // generated after the key was generated.
+ createKey(FINGERPRINT);
+ showToast("Set up lockscreen after test ran. Retry the test.");
+ return null;
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException("Init cipher with fingerprint-bound key failed", e);
+ }
+ }
+
+ private boolean tryEncryptWithFingerprintKey(Cipher cipher) {
+
+ try {
+ cipher.doFinal(SECRET_BYTE_ARRAY);
+ return true;
+ } catch (IllegalBlockSizeException e) {
+ // Cannot encrypt, key is unavailable
+ return false;
+ } catch (BadPaddingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ protected void handleActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case CONFIRM_CREDENTIALS_REQUEST_CODE:
+ if (resultCode == RESULT_OK) {
+ if (tryEncryptWithLockscreenKey()) {
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+ } else {
+ showToast("Test failed. Key not accessible after auth");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ } else {
+ showToast("Lockscreen challenge canceled.");
+ setTestResult(mLockScreenBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ break;
+ default:
+ super.handleActivityResult(requestCode, resultCode, data);
+ }
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(this, message, Toast.LENGTH_LONG).show();
+ }
+
+ public class FingerprintAuthDialogFragment extends DialogFragment {
+
+ private CancellationSignal mCancellationSignal;
+ private FingerprintManagerCallback mFingerprintManagerCallback;
+ private boolean mSelfCancelled;
+
+ class FingerprintManagerCallback extends FingerprintManager.AuthenticationCallback {
+ @Override
+ public void onAuthenticationError(int errMsgId, CharSequence errString) {
+ if (!mSelfCancelled) {
+ showToast(errString.toString());
+ }
+ }
+
+ @Override
+ public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ showToast(helpString.toString());
+ }
+
+ @Override
+ public void onAuthenticationFailed() {
+ showToast(getString(R.string.sec_fp_auth_failed));
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
+ if (tryEncryptWithFingerprintKey(mFingerprintCipher)) {
+ showToast("Test passed.");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_PASSED);
+ } else {
+ showToast("Test failed. Key not accessible after auth");
+ setTestResult(mFingerprintBoundKeyTest, TestResult.TEST_RESULT_FAILED);
+ }
+ FingerprintAuthDialogFragment.this.dismiss();
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mCancellationSignal.cancel();
+ mSelfCancelled = true;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ mCancellationSignal = new CancellationSignal();
+ mSelfCancelled = false;
+ mFingerprintManagerCallback = new FingerprintManagerCallback();
+ mFingerprintManager.authenticate(new FingerprintManager.CryptoObject(mFingerprintCipher),
+ mCancellationSignal, 0, mFingerprintManagerCallback, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setMessage(R.string.sec_fp_dialog_message);
+ return builder.create();
+ }
+
+ }
+
+}
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 0200a4f..b129665 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -19,6 +19,7 @@
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -59,7 +60,9 @@
private DialogTestListItem mProfileAccountVisibleTest;
private DialogTestListItem mDeviceAdminVisibleTest;
private DialogTestListItem mWorkAppVisibleTest;
- private DialogTestListItem mCrossProfileIntentFiltersTest;
+ private DialogTestListItem mCrossProfileIntentFiltersTestFromPersonal;
+ private DialogTestListItem mCrossProfileIntentFiltersTestFromWork;
+ private DialogTestListItem mAppLinkingTest;
private DialogTestListItem mDisableNonMarketTest;
private DialogTestListItem mEnableNonMarketTest;
private DialogTestListItem mWorkNotificationBadgedTest;
@@ -78,6 +81,7 @@
private DialogTestListItem mCrossProfileAudioCaptureSupportTest;
private TestListItem mKeyguardDisabledFeaturesTest;
private DialogTestListItem mDisableNfcBeamTest;
+ private TestListItem mAuthenticationBoundKeyTest;
public ByodFlowTestActivity() {
super(R.layout.provisioning_byod,
@@ -264,20 +268,39 @@
R.string.provisioning_byod_print_settings_instruction,
new Intent(Settings.ACTION_PRINT_SETTINGS));
- Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+ Intent intent = new Intent(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
+ intent.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, false);
Intent chooser = Intent.createChooser(intent,
getResources().getString(R.string.provisioning_cross_profile_chooser));
- mCrossProfileIntentFiltersTest = new DialogTestListItem(this,
- R.string.provisioning_byod_cross_profile,
- "BYOD_CrossProfileIntentFiltersTest",
- R.string.provisioning_byod_cross_profile_instruction,
+ mCrossProfileIntentFiltersTestFromPersonal = new DialogTestListItem(this,
+ R.string.provisioning_byod_cross_profile_from_personal,
+ "BYOD_CrossProfileIntentFiltersTestFromPersonal",
+ R.string.provisioning_byod_cross_profile_from_personal_instruction,
chooser);
+ mCrossProfileIntentFiltersTestFromWork = new DialogTestListItem(this,
+ R.string.provisioning_byod_cross_profile_from_work,
+ "BYOD_CrossProfileIntentFiltersTestFromWork",
+ R.string.provisioning_byod_cross_profile_from_work_instruction,
+ new Intent(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG));
+
+ mAppLinkingTest = new DialogTestListItem(this,
+ R.string.provisioning_app_linking,
+ "BYOD_AppLinking",
+ R.string.provisioning_byod_app_linking_instruction,
+ new Intent(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG));
+
mKeyguardDisabledFeaturesTest = TestListItem.newTest(this,
R.string.provisioning_byod_keyguard_disabled_features,
KeyguardDisabledFeaturesActivity.class.getName(),
new Intent(this, KeyguardDisabledFeaturesActivity.class), null);
+ mAuthenticationBoundKeyTest = TestListItem.newTest(this,
+ R.string.provisioning_byod_auth_bound_key,
+ AuthenticationBoundKeyTestActivity.class.getName(),
+ new Intent(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST),
+ null);
+
// Test for checking if the required intent filters are set during managed provisioning.
mIntentFiltersTest = new DialogTestListItem(this,
R.string.provisioning_byod_cross_profile_intent_filters,
@@ -287,7 +310,7 @@
checkIntentFilters();
}
};
-
+
Intent permissionCheckIntent = new Intent(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
mPermissionLockdownTest = new DialogTestListItem(this,
@@ -314,12 +337,15 @@
adapter.add(mDataUsageSettingsVisibleTest);
adapter.add(mPrintSettingsVisibleTest);
- adapter.add(mCrossProfileIntentFiltersTest);
+ adapter.add(mCrossProfileIntentFiltersTestFromPersonal);
+ adapter.add(mCrossProfileIntentFiltersTestFromWork);
+ adapter.add(mAppLinkingTest);
adapter.add(mDisableNonMarketTest);
adapter.add(mEnableNonMarketTest);
adapter.add(mIntentFiltersTest);
adapter.add(mPermissionLockdownTest);
adapter.add(mKeyguardDisabledFeaturesTest);
+ adapter.add(mAuthenticationBoundKeyTest);
if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
// Capture image intent can be resolved in primary profile, so test.
@@ -495,7 +521,8 @@
ByodHelperActivity.class.getName(),
WorkNotificationTestActivity.class.getName(),
WorkStatusTestActivity.class.getName(),
- PermissionLockdownTestActivity.ACTIVITY_ALIAS
+ PermissionLockdownTestActivity.ACTIVITY_ALIAS,
+ AuthenticationBoundKeyTestActivity.class.getName()
};
for (String component : components) {
getPackageManager().setComponentEnabledSetting(new ComponentName(this, component),
@@ -503,4 +530,5 @@
PackageManager.DONT_KILL_APP);
}
}
+
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 3d7d42d..9ea5061 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
import android.support.v4.content.FileProvider;
@@ -84,6 +85,18 @@
public static final String ACTION_CHECK_INTENT_FILTERS =
"com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS";
+ // Primary -> managed intent: will send a cross profile intent and check if the user sees an
+ // intent picker dialog and can open the apps.
+ public static final String ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG =
+ "com.android.cts.verifier.managedprovisioning.action.TEST_CROSS_PROFILE_INTENTS_DIALOG";
+
+ // Primary -> managed intent: will send an app link intent and check if the user sees a
+ // dialog and can open the apps. This test is extremely similar to
+ // ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG, but the intent used is a web intent, and there is
+ // some behavior which is specific to web intents.
+ public static final String ACTION_TEST_APP_LINKING_DIALOG =
+ "com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG";
+
public static final int RESULT_FAILED = RESULT_FIRST_USER;
private static final int REQUEST_INSTALL_PACKAGE = 1;
@@ -211,6 +224,16 @@
startActivity(testNfcBeamIntent);
finish();
return;
+ } else if (action.equals(ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG)) {
+ sendIntentInsideChooser(new Intent(
+ CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL));
+ } else if (action.equals(ACTION_TEST_APP_LINKING_DIALOG)) {
+ mDevicePolicyManager.addUserRestriction(
+ DeviceAdminTestReceiver.getReceiverComponentName(),
+ UserManager.ALLOW_PARENT_PROFILE_APP_LINKING);
+ Intent toSend = new Intent(Intent.ACTION_VIEW);
+ toSend.setData(Uri.parse("http://com.android.cts.verifier"));
+ sendIntentInsideChooser(toSend);
}
// This activity has no UI and is only used to respond to CtsVerifier in the primary side.
finish();
@@ -346,6 +369,13 @@
DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
}
+ private void sendIntentInsideChooser(Intent toSend) {
+ toSend.putExtra(CrossProfileTestActivity.EXTRA_STARTED_FROM_WORK, true);
+ Intent chooser = Intent.createChooser(toSend,
+ getResources().getString(R.string.provisioning_cross_profile_chooser));
+ startActivity(chooser);
+ }
+
@Override
public void onDialogClose() {
finish();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
index 6c38e12..3f316f21 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CrossProfileTestActivity.java
@@ -35,7 +35,12 @@
*/
public class CrossProfileTestActivity extends Activity {
// Intent for app in both profiles
- public static final String ACTION_CROSS_PROFILE = "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE";
+ public static final String ACTION_CROSS_PROFILE_TO_PERSONAL =
+ "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_PERSONAL";
+ public static final String ACTION_CROSS_PROFILE_TO_WORK =
+ "com.android.cts.verifier.managedprovisioning.CROSS_PROFILE_TO_WORK";
+ public static final String EXTRA_STARTED_FROM_WORK
+ = "com.android.cts.verifier.managedprovisioning.STARTED_FROM_WORK";
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -45,9 +50,15 @@
// Check if we are running in the work or personal side, by testing if currently we are the
// profile owner or not.
- textView.setText(isProfileOwner() ? R.string.provisioning_byod_cross_profile_app_work
- : R.string.provisioning_byod_cross_profile_app_personal);
-
+ boolean inWorkProfile = isProfileOwner();
+ boolean startedFromWork = getIntent().getBooleanExtra(EXTRA_STARTED_FROM_WORK, false);
+ if (inWorkProfile && !startedFromWork) {
+ textView.setText(R.string.provisioning_byod_cross_profile_app_work);
+ } else if (!inWorkProfile && startedFromWork) {
+ textView.setText(R.string.provisioning_byod_cross_profile_app_personal);
+ } else { // started from the same side we're currently running in
+ textView.setText(R.string.provisioning_byod_cross_profile_app_ctsverifier);
+ }
findViewById(R.id.button_finish).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
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 008091b..ed6b5eb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -64,7 +64,9 @@
filter.addAction(ByodHelperActivity.ACTION_KEYGUARD_DISABLED_FEATURES);
filter.addAction(ByodHelperActivity.ACTION_LOCKNOW);
filter.addAction(ByodHelperActivity.ACTION_TEST_NFC_BEAM);
- filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_CROSS_PROFILE_INTENTS_DIALOG);
+ filter.addAction(ByodHelperActivity.ACTION_TEST_APP_LINKING_DIALOG);
+ filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_WORK);
filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION_ON_LOCKSCREEN);
filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
@@ -72,12 +74,14 @@
filter.addAction(WorkStatusTestActivity.ACTION_WORK_STATUS_ICON);
filter.addAction(
PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
+ filter.addAction(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
// Work -> primary direction
filter = new IntentFilter();
filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
+ filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL);
dpm.addCrossProfileIntentFilter(getWho(context), filter,
DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
new file mode 100644
index 0000000..43f293a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/AppLinkTestActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.drawable.Drawable;
+import android.media.tv.TvContract;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Tests for verifying TV app behavior for TV app-link.
+ */
+public class AppLinkTestActivity extends TvAppVerifierActivity implements View.OnClickListener {
+ private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
+
+ private boolean mSelectAppLinkItemPassed;
+ private View mSelectAppLinkItem;
+ private View mVerifyAppLinkIntentItem;
+ private View mVerifyAppLinkCardItem;
+
+ Runnable mSelectAppLinkFailCallback;
+
+ @Override
+ public void onClick(View v) {
+ final View postTarget = getPostTarget();
+
+ if (containsButton(mSelectAppLinkItem, v)) {
+ Intent tvAppIntent = null;
+ String[] projection = { TvContract.Channels._ID };
+ try (Cursor cursor = getContentResolver().query(
+ TvContract.buildChannelsUriForInput(MockTvInputService.getInputId(this)),
+ projection, null, null, null)) {
+ if (cursor != null && cursor.moveToNext()) {
+ tvAppIntent = new Intent(Intent.ACTION_VIEW,
+ TvContract.buildChannelUri(cursor.getLong(0)));
+ }
+ }
+ if (tvAppIntent == null) {
+ Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ mSelectAppLinkFailCallback = new Runnable() {
+ @Override
+ public void run() {
+ mSelectAppLinkItemPassed = false;
+ setPassState(mSelectAppLinkItem, false);
+ setPassState(mVerifyAppLinkIntentItem, false);
+ }
+ };
+ postTarget.postDelayed(mSelectAppLinkFailCallback, TIMEOUT_MS);
+ mSelectAppLinkItemPassed = true;
+ setPassState(mSelectAppLinkItem, true);
+
+ startActivity(tvAppIntent);
+ } else if (containsButton(mVerifyAppLinkCardItem, v)) {
+ setPassState(mVerifyAppLinkCardItem, true);
+ getPassButton().setEnabled(true);
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ if (mSelectAppLinkItemPassed
+ && TextUtils.equals(MockTvInputSetupActivity.APP_LINK_TEST_VALUE,
+ intent.getStringExtra(MockTvInputSetupActivity.APP_LINK_TEST_KEY))) {
+ getPostTarget().removeCallbacks(mSelectAppLinkFailCallback);
+ setPassState(mVerifyAppLinkIntentItem, true);
+ setButtonEnabled(mVerifyAppLinkCardItem, true);
+ }
+ }
+
+ @Override
+ protected void createTestItems() {
+ mSelectAppLinkItem = createUserItem(R.string.tv_app_link_test_select_app_link,
+ R.string.tv_launch_tv_app, this);
+ setButtonEnabled(mSelectAppLinkItem, true);
+ mVerifyAppLinkIntentItem = createAutoItem(
+ R.string.tv_app_link_test_verify_link_clicked);
+ mVerifyAppLinkCardItem = createUserItem(R.string.tv_input_link_test_verify_link_interface,
+ android.R.string.yes, this);
+ TextView instructions = (TextView) mVerifyAppLinkCardItem.findViewById(R.id.instructions);
+ Drawable image = getDrawable(R.drawable.app_link_img);
+ image.setBounds(0, 0, 317, 241);
+ instructions.setCompoundDrawablePadding(10);
+ instructions.setCompoundDrawables(image, null, null, null);
+ }
+
+ @Override
+ protected void setInfoResources() {
+ setInfoResources(R.string.tv_app_link_test, R.string.tv_app_link_test_info, -1);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
index c05b753..43ed7da 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -17,9 +17,12 @@
package com.android.cts.verifier.tv;
import android.app.Activity;
+import android.content.ComponentName;
import android.content.ContentUris;
import android.content.ContentValues;
+import android.content.Intent;
import android.database.Cursor;
+import android.graphics.Color;
import android.media.tv.TvContract;
import android.media.tv.TvContract.Programs;
import android.media.tv.TvInputInfo;
@@ -28,6 +31,8 @@
import android.util.Pair;
import android.view.View;
+import com.android.cts.verifier.R;
+
import java.util.ArrayList;
public class MockTvInputSetupActivity extends Activity {
@@ -38,6 +43,10 @@
/* package-private */ static final String PROGRAM_TITLE = "Dummy Program";
/* package-private */ static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
+
+ /* package-private */ static final String APP_LINK_TEST_KEY = "app_link_test_key";
+ /* package-private */ static final String APP_LINK_TEST_VALUE = "app_link_test_value";
+ private static final String APP_LINK_TEXT = "Cts App-Link Text";
private static final long PROGRAM_LENGTH_MILLIS = 60 * 60 * 1000;
private static final int PROGRAM_COUNT = 24;
@@ -69,6 +78,18 @@
values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER);
values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, CHANNEL_NAME);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_TEXT, APP_LINK_TEXT);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_COLOR, Color.RED);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_ICON_URI,
+ "android.resource://" + getPackageName() + "/" + R.drawable.icon);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_POSTER_ART_URI,
+ "android.resource://" + getPackageName() + "/" + R.raw.sns_texture);
+ Intent appLinkIntentUri = new Intent(this, AppLinkTestActivity.class);
+ appLinkIntentUri.putExtra(APP_LINK_TEST_KEY, APP_LINK_TEST_VALUE);
+ appLinkIntentUri.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
+ values.put(TvContract.Channels.COLUMN_APP_LINK_INTENT_URI,
+ appLinkIntentUri.toUri(Intent.URI_INTENT_SCHEME));
+
Uri channelUri = getContentResolver().insert(uri, values);
// If the channel's ID happens to be zero, we add another and delete the one.
if (ContentUris.parseId(channelUri) == 0) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index 12e9652..acab18e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -35,8 +35,6 @@
public abstract class TvAppVerifierActivity extends PassFailButtons.Activity {
private static final String TAG = "TvAppVerifierActivity";
- private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
-
private LayoutInflater mInflater;
private ViewGroup mItemList;
private View mPostTarget;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
index 06f4f6f..e8e2cee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -25,8 +25,6 @@
import com.android.cts.verifier.R;
-import java.util.List;
-
/**
* Tests for verifying TV app behavior for third-party TV input apps.
*/
@@ -131,7 +129,7 @@
mGoToEpgItem = createUserItem(R.string.tv_input_discover_test_go_to_epg,
R.string.tv_launch_epg, this);
mVerifyEpgItem = createUserItem(R.string.tv_input_discover_test_verify_epg,
- R.string.tv_input_discover_test_yes, this);
+ android.R.string.yes, this);
}
@Override
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
index 70414ca..2ae2e10 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AdoptableHostTest.java
@@ -294,7 +294,7 @@
private LocalVolumeInfo getAdoptionVolume() throws Exception {
String[] lines = null;
int attempt = 0;
- while (attempt++ < 5) {
+ while (attempt++ < 15) {
lines = getDevice().executeShellCommand("sm list-volumes private").split("\n");
for (String line : lines) {
final LocalVolumeInfo info = new LocalVolumeInfo(line.trim());
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 345279f..f15f6b0 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -262,6 +262,13 @@
bug: 23008511
},
{
+ description: "Light status bar CTS coming in late",
+ names: [
+ "com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons"
+ ],
+ bug: 23427621
+},
+{
description: "known failures",
names: [
"android.hardware.cts.SensorBatchingTests#testAccelerometer_50hz_batching",
diff --git a/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java b/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
index a86a707..241535e 100644
--- a/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
+++ b/tests/leanbackjank/src/android/cts/leanbackjank/CtsDeviceLeanback.java
@@ -16,6 +16,7 @@
import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.cts.jank.leanback.IntentKeys;
import android.os.SystemClock;
import android.support.test.jank.GfxMonitor;
@@ -40,9 +41,30 @@
private final static String JAVA_PACKAGE = "android.cts.jank.leanback.ui";
private final static String CLASS = JAVA_PACKAGE + ".MainActivity";
+ private boolean shouldSkip() {
+ PackageManager packageManager =
+ getInstrumentation().getTargetContext().getPackageManager();
+ if (!packageManager.hasSystemFeature(
+ PackageManager.FEATURE_LEANBACK)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void runTest() throws Throwable {
+ if (shouldSkip()) {
+ return;
+ }
+ super.runTest();
+ }
+
@Override
protected void setUp() throws Exception {
super.setUp();
+ if (shouldSkip()) {
+ return;
+ }
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName(APP_PACKAGE, CLASS));
diff --git a/tests/tests/netlegacy22/api/Android.mk b/tests/netlegacy22.api/Android.mk
similarity index 100%
rename from tests/tests/netlegacy22/api/Android.mk
rename to tests/netlegacy22.api/Android.mk
diff --git a/tests/tests/netlegacy22/api/AndroidManifest.xml b/tests/netlegacy22.api/AndroidManifest.xml
similarity index 92%
rename from tests/tests/netlegacy22/api/AndroidManifest.xml
rename to tests/netlegacy22.api/AndroidManifest.xml
index d243e45..f13805c 100644
--- a/tests/tests/netlegacy22/api/AndroidManifest.xml
+++ b/tests/netlegacy22.api/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.net.legacy22">
+ package="com.android.cts.netlegacy22.api">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -30,7 +30,7 @@
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.net.legacy22"
+ android:targetPackage="com.android.cts.netlegacy22.api"
android:label="CTS tests of legacy android.net APIs as of API 22">
<meta-data android:name="listener"
android:value="com.android.cts.runner.CtsTestRunListener" />
diff --git a/tests/tests/netlegacy22/api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
similarity index 84%
rename from tests/tests/netlegacy22/api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
rename to tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
index 8a9002b..1836f06 100644
--- a/tests/tests/netlegacy22/api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
+++ b/tests/netlegacy22.api/src/android/net/cts/legacy/api22/ConnectivityManagerLegacyTest.java
@@ -85,24 +85,58 @@
((addr[1] & 0xff) << 8) | (addr[0] & 0xff);
}
- private void checkSourceAddress(String addrString, int type) throws Exception {
- DatagramSocket d = new DatagramSocket();
- d.connect(InetAddress.getByName(addrString), 7);
- InetAddress localAddress = d.getLocalAddress();
-
+ // Returns a list of all the IP addresses for all the networks of a given legacy type. We can't
+ // just fetch the IP addresses for that type because there is no public getLinkProperties API
+ // that takes a legacy type.
+ private List<InetAddress> getIpAddresses(int type) {
+ ArrayList<InetAddress> addresses = new ArrayList<>();
Network[] networks = mCm.getAllNetworks();
for (int i = 0; i < networks.length; i++) {
NetworkInfo ni = mCm.getNetworkInfo(networks[i]);
if (ni != null && ni.getType() == type) {
+ // This does not include IP addresses on stacked interfaces (e.g., 464xlat), because
+ // there is no public API that will return them.
LinkProperties lp = mCm.getLinkProperties(networks[i]);
for (LinkAddress address : lp.getLinkAddresses()) {
- if (address.getAddress().equals(localAddress)) {
- return;
- }
+ addresses.add(address.getAddress());
}
}
}
- fail("Local address " + localAddress + " not assigned to any network of type " + type);
+ return addresses;
+ }
+
+ private boolean hasIPv4(int type) {
+ for (InetAddress address : getIpAddresses(type)) {
+ if (address instanceof Inet4Address) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void checkSourceAddress(String addrString, int type) throws Exception {
+ // The public requestRouteToHost API only supports IPv4, but it will not return failure if
+ // the network does not have an IPv4 address. So don't check that it's working unless we
+ // know that the network has an IPv4 address. Note that it's possible that the network will
+ // have an IPv4 address but we don't know about it, because the IPv4 address might be on a
+ // stacked interface and we wouldn't be able to see it.
+ if (!hasIPv4(type)) {
+ Log.d(TAG, "Not checking source address on network type " + type + ", no IPv4 address");
+ return;
+ }
+
+ DatagramSocket d = new DatagramSocket();
+ d.connect(InetAddress.getByName(addrString), 7);
+ InetAddress localAddress = d.getLocalAddress();
+ String localAddrString = localAddress.getHostAddress();
+
+ Log.d(TAG, "Got source address " + localAddrString + " for destination " + addrString);
+
+ assertTrue(
+ "Local address " + localAddress + " not assigned to any network of type " + type,
+ getIpAddresses(type).contains(localAddress));
+
+ Log.d(TAG, "Source address " + localAddress + " found on network type " + type);
}
/** Test that hipri can be brought up when Wifi is enabled. */
@@ -127,7 +161,6 @@
assertTrue("Couldn't requestRouteToHost using HIPRI.",
mCm.requestRouteToHost(TYPE_MOBILE_HIPRI, ipv4AddrToInt(HOST_ADDRESS1)));
- try { Thread.sleep(1000); } catch(Exception e) {}
checkSourceAddress(HOST_ADDRESS1, TYPE_MOBILE);
checkSourceAddress(HOST_ADDRESS2, TYPE_WIFI);
diff --git a/tests/tests/netlegacy22/permission/Android.mk b/tests/netlegacy22.permission/Android.mk
similarity index 100%
rename from tests/tests/netlegacy22/permission/Android.mk
rename to tests/netlegacy22.permission/Android.mk
diff --git a/tests/tests/netlegacy22/permission/AndroidManifest.xml b/tests/netlegacy22.permission/AndroidManifest.xml
similarity index 89%
rename from tests/tests/netlegacy22/permission/AndroidManifest.xml
rename to tests/netlegacy22.permission/AndroidManifest.xml
index d407404..cd1d2ba 100644
--- a/tests/tests/netlegacy22/permission/AndroidManifest.xml
+++ b/tests/netlegacy22.permission/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.permission">
+ package="com.android.cts.netlegacy22.permission">
<uses-permission android:name="android.permission.INJECT_EVENTS" />
<application>
@@ -41,8 +41,8 @@
relies on hidden APIs.
-->
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.permission"
- android:label="CTS tests of com.android.cts.permission">
+ android:targetPackage="com.android.cts.netlegacy22.permission"
+ android:label="CTS tests of legacy android.net permissions as of API 22">
<meta-data android:name="listener"
android:value="com.android.cts.runner.CtsTestRunListener" />
</instrumentation>
diff --git a/tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
similarity index 100%
rename from tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
rename to tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/ConnectivityManagerPermissionTest.java
diff --git a/tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java b/tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
similarity index 100%
rename from tests/tests/netlegacy22/permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
rename to tests/netlegacy22.permission/src/android/net/cts/legacy/api22/permission/NoNetworkStatePermissionTest.java
diff --git a/tests/tests/alarmclock/AndroidTest.xml b/tests/tests/alarmclock/AndroidTest.xml
index 1cdd7f4..aafdb61 100644
--- a/tests/tests/alarmclock/AndroidTest.xml
+++ b/tests/tests/alarmclock/AndroidTest.xml
@@ -17,7 +17,5 @@
<option name="cts-apk-installer:test-file-name" value="CtsAlarmClockService.apk" />
<option name="run-command:run-command"
value="settings put secure voice_interaction_service android.alarmclock.service/.MainInteractionService" />
- <option name="run-command:teardown-command"
- value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
<option name="cts-apk-installer:test-file-name" value="CtsAlarmClockTestCases.apk" />
</configuration>
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
index 4e5b4ce..0d434f2 100644
--- a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockTestBase.java
@@ -70,7 +70,7 @@
new IntentFilter(Utils.BROADCAST_INTENT + testCaseType.toString()));
}
- private boolean isIntentAupported(TestcaseType testCaseType) {
+ private boolean isIntentSupported(TestcaseType testCaseType) {
Intent intent;
switch (testCaseType) {
case DISMISS_ALARM:
@@ -102,7 +102,7 @@
protected String runTest(TestcaseType testCaseType) throws Exception {
Log.i(TAG, "Begin Testing: " + testCaseType);
// Make sure the corresponding intent is supported by the platform, before testing.
- if (!isIntentAupported(testCaseType)) return Utils.COMPLETION_RESULT;
+ if (!isIntentSupported(testCaseType)) return Utils.COMPLETION_RESULT;
if (!startTestActivity(testCaseType)) {
fail("test activity start failed for testcase = " + testCaseType);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
index c807e03..22f092a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorParameterRangeTest.java
@@ -102,13 +102,13 @@
sensor.getMaximumRange() >= maxRange);
double actualMinFrequency = SensorCtsHelper.getFrequency(sensor.getMaxDelay(),
TimeUnit.MICROSECONDS);
- assertTrue(String.format("%s Min Frequency actual=%.2f expected=%dHz",
+ assertTrue(String.format("%s Min Frequency actual=%.2f expected=%.2fHz",
sensor.getName(), actualMinFrequency, minFrequency), actualMinFrequency <=
minFrequency + 0.1);
double actualMaxFrequency = SensorCtsHelper.getFrequency(sensor.getMinDelay(),
TimeUnit.MICROSECONDS);
- assertTrue(String.format("%s Max Frequency actual=%.2f expected=%dHz",
+ assertTrue(String.format("%s Max Frequency actual=%.2f expected=%.2fHz",
sensor.getName(), actualMaxFrequency, maxFrequency), actualMaxFrequency >=
maxFrequency - 0.1);
}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index f844b76..a0f7384 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -105,6 +105,7 @@
public boolean onError(MediaPlayer mp, int what, int extra) {
assertTrue(mp == mMediaPlayer);
assertTrue("mediaserver process died", what != MediaPlayer.MEDIA_ERROR_SERVER_DIED);
+ Log.w(LOG_TAG, "onError " + what);
return false;
}
});
@@ -122,7 +123,9 @@
afd.close();
mMediaPlayer.prepare();
mMediaPlayer.start();
- mOnCompletionCalled.waitForSignal();
+ if (!mOnCompletionCalled.waitForSignal(5000)) {
+ Log.w(LOG_TAG, "testIfMediaServerDied: Timed out waiting for Error/Completion");
+ }
mMediaPlayer.release();
}
diff --git a/tests/tests/os/jni/seccomp_sample_program.cpp b/tests/tests/os/jni/seccomp_sample_program.cpp
index 3c90196..3bc7da4 100644
--- a/tests/tests/os/jni/seccomp_sample_program.cpp
+++ b/tests/tests/os/jni/seccomp_sample_program.cpp
@@ -826,7 +826,7 @@
{0x35, 0, 4, 0x76},
{0x35, 0, 2, 0x79},
{0x35, 0, 241, 0x7a},
- {0x35, 240, 239, 0x7b},
+ {0x35, 240, 239, 0x7c},
{0x35, 238, 239, 0x77},
{0x35, 0, 2, 0x72},
{0x35, 0, 236, 0x73},
diff --git a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
index 00cbfd8..fdc3058 100644
--- a/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
+++ b/tests/tests/security/src/android/security/cts/ServicePermissionsTest.java
@@ -17,6 +17,7 @@
package android.security.cts;
import android.os.IBinder;
+import android.os.DeadObjectException;
import android.os.TransactionTooLargeException;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -108,7 +109,7 @@
// probably not checking for DUMP.
throw e;
}
- } catch (TransactionTooLargeException e) {
+ } catch (TransactionTooLargeException | DeadObjectException e) {
// SELinux likely prevented the dump - assume safe
continue;
} finally {
diff --git a/tests/tests/netlegacy22/Android.mk b/tests/tests/systemui/Android.mk
similarity index 63%
rename from tests/tests/netlegacy22/Android.mk
rename to tests/tests/systemui/Android.mk
index 3174652..1a15fd2 100644
--- a/tests/tests/netlegacy22/Android.mk
+++ b/tests/tests/systemui/Android.mk
@@ -12,5 +12,19 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# Build the API tests and the permissions tests using their own makefiles.
-include $(call all-subdir-makefiles)
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSystemUiTestCases
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml
new file mode 100644
index 0000000..bf5df5b
--- /dev/null
+++ b/tests/tests/systemui/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2015 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.systemui">
+ <uses-permission android:name="android.permission.INJECT_EVENTS" />
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application>
+ <activity android:name=".LightStatusBarActivity"
+ android:theme="@android:style/Theme.Material.NoActionBar"></activity>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.systemui">
+ </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
new file mode 100644
index 0000000..626a179
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/ColorUtils.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.systemui;
+
+/**
+ * Copies of non-public {@link android.graphics.Color} APIs
+ */
+public class ColorUtils {
+
+ public static float brightness(int argb) {
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = argb & 0xFF;
+
+ int V = Math.max(b, Math.max(r, g));
+
+ return (V / 255.f);
+ }
+
+ public static float hue(int argb) {
+ int r = (argb >> 16) & 0xFF;
+ int g = (argb >> 8) & 0xFF;
+ int b = argb & 0xFF;
+
+ int V = Math.max(b, Math.max(r, g));
+ int temp = Math.min(b, Math.min(r, g));
+
+ float H;
+
+ if (V == temp) {
+ H = 0;
+ } else {
+ final float vtemp = (float) (V - temp);
+ final float cr = (V - r) / vtemp;
+ final float cg = (V - g) / vtemp;
+ final float cb = (V - b) / vtemp;
+
+ if (r == V) {
+ H = cb - cg;
+ } else if (g == V) {
+ H = 2 + cr - cb;
+ } else {
+ H = 4 + cg - cr;
+ }
+
+ H /= 6.f;
+ if (H < 0) {
+ H++;
+ }
+ }
+
+ return H;
+ }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
new file mode 100644
index 0000000..3722320
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarActivity.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.cts.systemui;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+
+/**
+ * An activity that exercises SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.
+ */
+public class LightStatusBarActivity extends Activity {
+
+ private View mContent;
+
+ public void onCreate(Bundle bundle){
+ super.onCreate(bundle);
+
+ mContent = new View(this);
+ mContent.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ setContentView(mContent);
+ }
+
+ public void setLightStatusBar(boolean lightStatusBar) {
+ int vis = getWindow().getDecorView().getSystemUiVisibility();
+ if (lightStatusBar) {
+ vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ } else {
+ vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
+ }
+ getWindow().getDecorView().setSystemUiVisibility(vis);
+ }
+
+ public int getTop() {
+ return mContent.getLocationOnScreen()[1];
+ }
+
+ public int getWidth() {
+ return mContent.getWidth();
+ }
+}
diff --git a/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
new file mode 100644
index 0000000..b5bfd51
--- /dev/null
+++ b/tests/tests/systemui/src/com/android/cts/systemui/LightStatusBarTests.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.cts.systemui;
+
+import android.app.ActivityManager;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.support.test.InstrumentationRegistry;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Test for light status bar.
+ */
+public class LightStatusBarTests extends ActivityInstrumentationTestCase2<LightStatusBarActivity> {
+
+ public static final String TAG = "LightStatusBarTests";
+
+ public static final String DUMP_PATH = "/sdcard/lightstatustest.png";
+
+ public LightStatusBarTests() {
+ super(LightStatusBarActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // As the way to access Instrumentation is changed in the new runner, we need to inject it
+ // manually into ActivityInstrumentationTestCase2. ActivityInstrumentationTestCase2 will
+ // be marked as deprecated and replaced with ActivityTestRule.
+ injectInstrumentation(InstrumentationRegistry.getInstrumentation());
+ }
+
+ public void testLightStatusBarIcons() throws Throwable {
+ PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ if (pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
+ || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+ // No status bar on TVs and watches.
+ return;
+ }
+
+ if (!ActivityManager.isHighEndGfx()) {
+ // non-highEndGfx devices don't do colored system bars.
+ return;
+ }
+
+ requestLightStatusBar(Color.RED /* background */);
+ Thread.sleep(1000);
+
+ Bitmap bitmap = takeStatusBarScreenshot();
+ Stats s = evaluateLightStatusBarBitmap(bitmap, Color.RED /* background */);
+ boolean success = false;
+
+ try {
+ assertMoreThan("Not enough background pixels", 0.3f,
+ (float) s.backgroundPixels / s.totalPixels(),
+ "Is the status bar background showing correctly (solid red)?");
+
+ assertMoreThan("Not enough pixels colored as in the spec", 0.1f,
+ (float) s.iconPixels / s.foregroundPixels(),
+ "Are the status bar icons colored according to the spec "
+ + "(60% black and 24% black)?");
+
+ assertLessThan("Too many lighter pixels lighter than the background", 0.05f,
+ (float) s.sameHueLightPixels / s.foregroundPixels(),
+ "Are the status bar icons dark?");
+
+ assertLessThan("Too many pixels with a changed hue", 0.05f,
+ (float) s.unexpectedHuePixels / s.foregroundPixels(),
+ "Are the status bar icons color-free?");
+
+ success = true;
+ } finally {
+ if (!success) {
+ Log.e(TAG, "Dumping failed bitmap to " + DUMP_PATH);
+ dumpBitmap(bitmap);
+ }
+ }
+ }
+
+ private void assertMoreThan(String what, float expected, float actual, String hint) {
+ if (!(actual > expected)) {
+ fail(what + ": expected more than " + expected * 100 + "%, but only got " + actual * 100
+ + "%; " + hint);
+ }
+ }
+
+ private void assertLessThan(String what, float expected, float actual, String hint) {
+ if (!(actual < expected)) {
+ fail(what + ": expected less than " + expected * 100 + "%, but got " + actual * 100
+ + "%; " + hint);
+ }
+ }
+
+ private void requestLightStatusBar(final int background) throws Throwable {
+ final LightStatusBarActivity activity = getActivity();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ activity.getWindow().setStatusBarColor(background);
+ activity.setLightStatusBar(true);
+ }
+ });
+ }
+
+ private static class Stats {
+ int backgroundPixels;
+ int iconPixels;
+ int sameHueDarkPixels;
+ int sameHueLightPixels;
+ int unexpectedHuePixels;
+
+ int totalPixels() {
+ return backgroundPixels + iconPixels + sameHueDarkPixels
+ + sameHueLightPixels + unexpectedHuePixels;
+ }
+
+ int foregroundPixels() {
+ return iconPixels + sameHueDarkPixels
+ + sameHueLightPixels + unexpectedHuePixels;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{bg=%d, ic=%d, dark=%d, light=%d, bad=%d}",
+ backgroundPixels, iconPixels, sameHueDarkPixels, sameHueLightPixels,
+ unexpectedHuePixels);
+ }
+ }
+
+ private Stats evaluateLightStatusBarBitmap(Bitmap bitmap, int background) {
+ int iconColor = 0x99000000;
+ int iconPartialColor = 0x3d000000;
+
+ int mixedIconColor = mixSrcOver(background, iconColor);
+ int mixedIconPartialColor = mixSrcOver(background, iconPartialColor);
+
+ int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
+ bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
+
+ Stats s = new Stats();
+ float eps = 0.005f;
+
+ for (int c : pixels) {
+ if (c == background) {
+ s.backgroundPixels++;
+ continue;
+ }
+
+ // What we expect the icons to be colored according to the spec.
+ if (c == mixedIconColor || c == mixedIconPartialColor) {
+ s.iconPixels++;
+ continue;
+ }
+
+ // Due to anti-aliasing, there will be deviations from the ideal icon color, but it
+ // should still be mostly the same hue.
+ float hueDiff = Math.abs(ColorUtils.hue(background) - ColorUtils.hue(c));
+ if (hueDiff < eps || hueDiff > 1 - eps) {
+ // .. it shouldn't be lighter than the original background though.
+ if (ColorUtils.brightness(c) > ColorUtils.brightness(background)) {
+ s.sameHueLightPixels++;
+ } else {
+ s.sameHueDarkPixels++;
+ }
+ continue;
+ }
+
+ s.unexpectedHuePixels++;
+ }
+
+ return s;
+ }
+
+ private void dumpBitmap(Bitmap bitmap) {
+ FileOutputStream fileStream = null;
+ try {
+ fileStream = new FileOutputStream(DUMP_PATH);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 85, fileStream);
+ fileStream.flush();
+ } catch (Exception e) {
+ Log.e(TAG, "Dumping bitmap failed.", e);
+ } finally {
+ if (fileStream != null) {
+ try {
+ fileStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private int mixSrcOver(int background, int foreground) {
+ int bgAlpha = Color.alpha(background);
+ int bgRed = Color.red(background);
+ int bgGreen = Color.green(background);
+ int bgBlue = Color.blue(background);
+
+ int fgAlpha = Color.alpha(foreground);
+ int fgRed = Color.red(foreground);
+ int fgGreen = Color.green(foreground);
+ int fgBlue = Color.blue(foreground);
+
+ return Color.argb(fgAlpha + (255 - fgAlpha) * bgAlpha / 255,
+ fgRed + (255 - fgAlpha) * bgRed / 255,
+ fgGreen + (255 - fgAlpha) * bgGreen / 255,
+ fgBlue + (255 - fgAlpha) * bgBlue / 255);
+ }
+
+ private Bitmap takeStatusBarScreenshot() {
+ Bitmap fullBitmap = getInstrumentation().getUiAutomation().takeScreenshot();
+ return Bitmap.createBitmap(fullBitmap, 0, 0,
+ getActivity().getWidth(), getActivity().getTop());
+ }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
index 176c50c..e15b45f 100644
--- a/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/MmsTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.SystemClock;
import android.telephony.SmsManager;
@@ -84,6 +85,7 @@
private Random mRandom;
private SentReceiver mSentReceiver;
private TelephonyManager mTelephonyManager;
+ private PackageManager mPackageManager;
private static class SentReceiver extends BroadcastReceiver {
private final Object mLock;
@@ -164,15 +166,26 @@
mRandom = new Random();
mTelephonyManager =
(TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mPackageManager = mContext.getPackageManager();
}
public void testSendMmsMessage() {
- if (!mTelephonyManager.isSmsCapable()) {
- Log.i(TAG, "testSendMmsMessage skipped: not SMS capable");
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ Log.i(TAG, "testSendMmsMessage skipped: no telephony available");
return;
}
Log.i(TAG, "testSendMmsMessage");
+ // Prime the MmsService so that MMS config is loaded
+ final SmsManager smsManager = SmsManager.getDefault();
+ smsManager.getAutoPersisting();
+ // MMS config is loaded asynchronously. Wait a bit so it will be loaded.
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+
final Context context = getContext();
// Register sent receiver
mSentReceiver = new SentReceiver();
@@ -193,7 +206,7 @@
// Send
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, new Intent(ACTION_MMS_SENT), 0);
- SmsManager.getDefault().sendMultimediaMessage(context,
+ smsManager.sendMultimediaMessage(context,
contentUri, null/*locationUrl*/, null/*configOverrides*/, pendingIntent);
assertTrue(mSentReceiver.waitForSuccess(SENT_TIMEOUT));
sendFile.delete();
diff --git a/tests/tests/voiceinteraction/AndroidTest.xml b/tests/tests/voiceinteraction/AndroidTest.xml
index 374c216..fa1ab70 100644
--- a/tests/tests/voiceinteraction/AndroidTest.xml
+++ b/tests/tests/voiceinteraction/AndroidTest.xml
@@ -19,7 +19,5 @@
<option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionApp.apk" />
<option name="run-command:run-command"
value="settings put secure voice_interaction_service android.voiceinteraction.service/.MainInteractionService" />
- <option name="run-command:teardown-command"
- value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
<option name="cts-apk-installer:test-file-name" value="CtsVoiceInteractionTestCases.apk" />
</configuration>
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
index 0a3974d..e3be691 100644
--- a/tests/tests/voicesettings/AndroidTest.xml
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -17,7 +17,5 @@
<option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsService.apk" />
<option name="run-command:run-command"
value="settings put secure voice_interaction_service android.voicesettings.service/.MainInteractionService" />
- <option name="run-command:teardown-command"
- value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
<option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsTestCases.apk" />
</configuration>
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
index 4597651..2d19162 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -16,6 +16,8 @@
package android.voicesettings.cts;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE;
+
import android.provider.Settings;
import android.provider.Settings.Global;
import android.util.Log;
@@ -33,6 +35,9 @@
}
public void testAll() throws Exception {
+ if (!isIntentSupported(ACTION_VOICE_CONTROL_AIRPLANE_MODE)) {
+ return;
+ }
int mode;
try {
mode = getMode();
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
index 3d1357a..6fe82a7 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -16,6 +16,8 @@
package android.voicesettings.cts;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
+
import android.content.Context;
import android.os.PowerManager;
import android.util.Log;
@@ -30,6 +32,9 @@
}
public void testAll() throws Exception {
+ if (!isIntentSupported(ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE)) {
+ return;
+ }
startTestActivity("BATTERYSAVER_MODE");
boolean modeIsOn = isModeOn();
Log.i(TAG, "Before testing, BATTERYSAVER_MODE is set to: " + modeIsOn);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
index 5386497..b355e7b 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.Bundle;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -57,6 +58,17 @@
super.tearDown();
}
+ protected boolean isIntentSupported(String intentStr) {
+ Intent intent = new Intent(intentStr);
+ final PackageManager manager = mContext.getPackageManager();
+ assertNotNull(manager);
+ if (manager.resolveActivity(intent, 0) == null) {
+ Log.i(TAG, "No Voice Activity found for the intent: " + intentStr);
+ return false;
+ }
+ return true;
+ }
+
protected void startTestActivity(String intentSuffix) {
Intent intent = new Intent();
intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index ca86918..b0ddf44 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -16,6 +16,7 @@
package android.voicesettings.cts;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE;
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
@@ -39,6 +40,9 @@
}
public void testAll() throws Exception {
+ if (!isIntentSupported(ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE)) {
+ return;
+ }
int mode;
try {
mode = getMode();
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index a048ddc..adb6a4e 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -492,6 +492,9 @@
'android.voicesettings' : [
'android.voicesettings.cts.ZenModeTest#testAll',
],
+ 'com.android.cts.systemui' : [
+ 'com.android.cts.systemui.LightStatusBarTests#testLightStatusBarIcons',
+ ],
'com.android.cts.app.os' : [
'com.android.cts.app.os.OsHostTests#testNonExportedActivities',
],