Merge "Add CTS tests for default dialer permissions" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 8386124..4e1e008 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -67,6 +67,7 @@
cts_support_packages := \
CtsAccelerationTestStubs \
CtsAppTestStubs \
+ CtsAtraceTestApp \
CtsCertInstallerApp \
CtsDeviceAdmin \
CtsDeviceOpenGl \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 6c515fd..98e8acd 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1495,6 +1495,18 @@
-->
</activity>
+ <activity android:name=".audio.AudioRoutingNotificationsActivity"
+ android:label="@string/audio_routingnotifications_test">
+ <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_audio" />
+ <!--
+ <meta-data android:name="test_required_features" android:value="android.hardware.microphone" />
+ -->
+ </activity>
+
<service android:name=".tv.MockTvInputService"
android:permission="android.permission.BIND_TV_INPUT">
<intent-filter>
diff --git a/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml b/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
new file mode 100644
index 0000000..cef30d6
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/audio_routingnotifications_test.xml
@@ -0,0 +1,99 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:scrollbars="vertical"
+ android:gravity="bottom"
+ android:id="@+id/info_text"
+ android:text="@string/audio_dev_routingnotification_instructions" />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/audioTrackRoutingLayout">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_routingnotification_playHeader"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_audioTrack_change"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_playBtn"
+ android:text="@string/audio_routingnotification_playBtn"/>
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_playStopBtn"
+ android:text="@string/audio_routingnotification_playStopBtn"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/audioRecordRoutingLayout">
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/audio_routingnotification_recHeader"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_audioRecord_change"/>
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_recordBtn"
+ android:text="@string/audio_routingnotification_recBtn"/>
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/audio_routingnotification_recordStopBtn"
+ android:text="@string/audio_routingnotification_recStopBtn"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 1ccd6b7..d22f5ec 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1615,4 +1615,23 @@
<string name="audio_dev_notification_clearmsgs">Clear Messages</string>
<string name="audio_dev_notification_connectMsg">CONNECT DETECTED</string>
<string name="audio_dev_notification_disconnectMsg">DISCONNECT DETECTED</string>
+
+ <!-- Audio Routing Notifcations Test -->
+ <string name="audio_routingnotifications_test">Audio Routing Notifications Test</string>
+ <string name="audio_dev_routingnotification_instructions">
+ Click on the "Play" button in the AudioTrack Routing Notifictions section below to
+ start (silent) playback. Insert a wired headset. Observe a message acknowledging the
+ rerouting event below. Remove the wired headset and observe the new routing message.
+ Click on the "Stop" button to stop playback.\n
+ Repeat the process with "Record" and "Stop" button in the AudioRecord Routing
+ Notifications section below.
+ </string>
+ <string name="audio_routingnotification_playBtn">Play</string>
+ <string name="audio_routingnotification_playStopBtn">Stop</string>
+ <string name="audio_routingnotification_recBtn">Record</string>
+ <string name="audio_routingnotification_recStopBtn">Stop</string>
+ <string name="audio_routingnotification_playHeader">AudioTrack Routing Notifications</string>
+ <string name="audio_routingnotification_recHeader">AudioRecord Routing Notifications</string>
+ <string name="audio_routingnotification_trackRoutingMsg">AudioTrack rerouting</string>
+ <string name="audio_routingnotification_recordRoutingMsg">AudioRecord rerouting</string>
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
new file mode 100644
index 0000000..b6a4255
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioRoutingNotificationsActivity.java
@@ -0,0 +1,146 @@
+/*
+ * 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.audio;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.util.Log;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.TextView;
+
+/**
+ * Tests AudioTrack and AudioRecord (re)Routing messages.
+ */
+public class AudioRoutingNotificationsActivity extends PassFailButtons.Activity {
+ private static final String TAG = "AudioRoutingNotificationsActivity";
+
+ Context mContext;
+
+ OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
+
+ int mNumTrackNotifications = 0;
+ int mNumRecordNotifications = 0;
+
+ TrivialPlayer mAudioPlayer = new TrivialPlayer();
+ TrivialRecorder mAudioRecorder = new TrivialRecorder();
+
+ private class OnBtnClickListener implements OnClickListener {
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.audio_routingnotification_playBtn:
+ Log.i(TAG, "audio_routingnotification_playBtn");
+ mAudioPlayer.start();
+ break;
+
+ case R.id.audio_routingnotification_playStopBtn:
+ Log.i(TAG, "audio_routingnotification_playStopBtn");
+ mAudioPlayer.stop();
+ break;
+
+ case R.id.audio_routingnotification_recordBtn:
+ break;
+
+ case R.id.audio_routingnotification_recordStopBtn:
+ break;
+ }
+ }
+ }
+
+ private class AudioTrackRoutingChangeListener implements AudioTrack.OnRoutingChangedListener {
+ public void onRoutingChanged(AudioTrack audioTrack) {
+ mNumTrackNotifications++;
+ TextView textView =
+ (TextView)findViewById(R.id.audio_routingnotification_audioTrack_change);
+ String msg = mContext.getResources().getString(
+ R.string.audio_routingnotification_trackRoutingMsg);
+ AudioDeviceInfo routedDevice = audioTrack.getRoutedDevice();
+ CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
+ int deviceType = routedDevice != null ? routedDevice.getType() : -1;
+ textView.setText(msg + " - " +
+ deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
+ " - " + mNumTrackNotifications);
+ }
+ }
+
+ private class AudioRecordRoutingChangeListener implements AudioRecord.OnRoutingChangedListener {
+ public void onRoutingChanged(AudioRecord audioRecord) {
+ mNumRecordNotifications++;
+ TextView textView =
+ (TextView)findViewById(R.id.audio_routingnotification_audioRecord_change);
+ String msg = mContext.getResources().getString(
+ R.string.audio_routingnotification_recordRoutingMsg);
+ AudioDeviceInfo routedDevice = audioRecord.getRoutedDevice();
+ CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none";
+ int deviceType = routedDevice != null ? routedDevice.getType() : -1;
+ textView.setText(msg + " - " +
+ deviceName + " [0x" + Integer.toHexString(deviceType) + "]" +
+ " - " + mNumRecordNotifications);
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.audio_routingnotifications_test);
+
+ Button btn;
+ btn = (Button)findViewById(R.id.audio_routingnotification_playBtn);
+ btn.setOnClickListener(mBtnClickListener);
+ btn = (Button)findViewById(R.id.audio_routingnotification_playStopBtn);
+ btn.setOnClickListener(mBtnClickListener);
+ btn = (Button)findViewById(R.id.audio_routingnotification_recordBtn);
+ btn.setOnClickListener(mBtnClickListener);
+ btn = (Button)findViewById(R.id.audio_routingnotification_recordStopBtn);
+ btn.setOnClickListener(mBtnClickListener);
+
+ mContext = this;
+
+ AudioTrack audioTrack = mAudioPlayer.getAudioTrack();
+ audioTrack.addOnRoutingChangedListener(
+ new AudioTrackRoutingChangeListener(), new Handler());
+
+ AudioRecord audioRecord = mAudioRecorder.getAudioRecord();
+ audioRecord.addOnRoutingChangedListener(
+ new AudioRecordRoutingChangeListener(), new Handler());
+
+ setPassFailButtonClickListeners();
+ }
+
+ @Override
+ public void onBackPressed () {
+ mAudioPlayer.shutDown();
+ mAudioRecorder.shutDown();
+ super.onBackPressed();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialPlayer.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialPlayer.java
new file mode 100644
index 0000000..af09504
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialPlayer.java
@@ -0,0 +1,111 @@
+/*
+ * 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.audio;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+
+import java.lang.InterruptedException;
+import java.lang.Math;
+import java.lang.Runnable;
+
+public class TrivialPlayer implements Runnable {
+ AudioTrack mAudioTrack;
+ int mBufferSize;
+
+ boolean mPlay;
+ boolean mIsPlaying;
+
+ short[] mAudioData;
+
+ Thread mFillerThread = null;
+
+ public TrivialPlayer() {
+ mBufferSize =
+ AudioTrack.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT);
+ mAudioTrack =
+ new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ mBufferSize,
+ AudioTrack.MODE_STREAM);
+
+ mPlay = false;
+ mIsPlaying = false;
+
+ // setup audio data (silence will suffice)
+ mAudioData = new short[mBufferSize];
+ for (int index = 0; index < mBufferSize; index++) {
+ // mAudioData[index] = 0;
+ // keep this code since one might want to hear the playnig audio
+ // for debugging/verification.
+ mAudioData[index] =
+ (short)(((Math.random() * 2.0) - 1.0) * (double)Short.MAX_VALUE/2.0);
+ }
+ }
+
+ public AudioTrack getAudioTrack() { return mAudioTrack; }
+
+ public boolean isPlaying() {
+ synchronized (this) {
+ return mIsPlaying;
+ }
+ }
+
+ public void start() {
+ mPlay = true;
+ mFillerThread = new Thread(this);
+ mFillerThread.start();
+ }
+
+ public void stop() {
+ mPlay = false;
+ mFillerThread = null;
+ }
+
+ public void shutDown() {
+ stop();
+ while (isPlaying()) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ }
+ }
+ mAudioTrack.release();
+ }
+
+ @Override
+ public void run() {
+ mAudioTrack.play();
+ synchronized (this) {
+ mIsPlaying = true;
+ }
+ while (mAudioTrack != null && mPlay) {
+ mAudioTrack.write(mAudioData, 0, mBufferSize);
+ }
+ synchronized (this) {
+ mIsPlaying = false;
+ }
+ mAudioTrack.stop();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialRecorder.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialRecorder.java
new file mode 100644
index 0000000..f684681
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/TrivialRecorder.java
@@ -0,0 +1,104 @@
+/*
+ * 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.audio;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+
+import android.media.MediaRecorder;
+
+import java.lang.InterruptedException;
+import java.lang.Runnable;
+
+public class TrivialRecorder implements Runnable {
+ AudioRecord mAudioRecord;
+ int mBufferSize;
+
+ boolean mRecord;
+ boolean mIsRecording;
+
+ short[] mAudioData;
+
+ Thread mReaderThread = null;
+
+ public TrivialRecorder() {
+ mBufferSize =
+ AudioRecord.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_IN_MONO,
+ AudioFormat.ENCODING_PCM_16BIT);
+ mAudioRecord =
+ new AudioRecord(
+ MediaRecorder.AudioSource.DEFAULT,
+ 41000,
+ AudioFormat.CHANNEL_IN_MONO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ mBufferSize);
+
+ mRecord = false;
+ mIsRecording = false;
+
+ // setup audio data (silence will suffice)
+ mAudioData = new short[mBufferSize];
+ }
+
+ public AudioRecord getAudioRecord() { return mAudioRecord; }
+
+ public boolean mIsRecording() {
+ synchronized (this) {
+ return mIsRecording;
+ }
+ }
+
+ public void start() {
+ mRecord = true;
+ mReaderThread = new Thread(this);
+ mReaderThread.start();
+ }
+
+ public void stop() {
+ mRecord = false;
+ mReaderThread = null;
+ }
+
+ public void shutDown() {
+ stop();
+ while (mIsRecording()) {
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ }
+ }
+ mAudioRecord.release();
+ }
+
+ @Override
+ public void run() {
+ mAudioRecord.startRecording();
+ synchronized (this) {
+ mIsRecording = true;
+ }
+ while (mRecord) {
+ mAudioRecord.read(mAudioData, 0, mBufferSize);
+ }
+ mAudioRecord.stop();
+ synchronized (this) {
+ mIsRecording = false;
+ }
+ }
+}
diff --git a/hostsidetests/atrace/AtraceTestApp/Android.mk b/hostsidetests/atrace/AtraceTestApp/Android.mk
new file mode 100644
index 0000000..0eb7cfd
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 2009 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_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_PACKAGE_NAME := CtsAtraceTestApp
+
+# sign this app with a different cert than CtsSimpleAppInstallDiffCert
+#LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+
+#LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..a86f7f0
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.atracetestapp">
+ <!--
+ A simple app with a tracing section to test that apps tracing signals are
+ emitted by atrace.
+ -->
+ <application>
+ <activity android:name=".AtraceTestAppActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
new file mode 100644
index 0000000..0269d0d
--- /dev/null
+++ b/hostsidetests/atrace/AtraceTestApp/src/com/android/cts/atracetestapp/AtraceTestAppActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.atracetestapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Trace;
+
+public class AtraceTestAppActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Trace.beginSection("traceable-app-test-section");
+ super.onCreate(savedInstanceState);
+ Trace.endSection();
+ }
+}
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
index f7af31c..ee0511d 100644
--- a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -16,27 +16,40 @@
package android.atrace.cts;
+import com.android.cts.tradefed.build.CtsBuildHelper;
import com.android.ddmlib.Log;
+import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.StringReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Test to check that atrace is usable, to enable usage of systrace.
*/
-public class AtraceHostTest extends DeviceTestCase {
+public class AtraceHostTest extends DeviceTestCase implements IBuildReceiver {
private static final String TAG = "AtraceHostTest";
- private ITestDevice mDevice;
+ private static final String TEST_APK = "CtsAtraceTestApp.apk";
+ private static final String TEST_PKG = "com.android.cts.atracetestapp";
+ private CtsBuildHelper mCtsBuild;
+
+ /**
+ * {@inheritDoc}
+ */
@Override
- protected void setUp() throws Exception {
- super.setUp();
- mDevice = getDevice();
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
}
// Collection of all userspace tags, and 'sched'
@@ -65,7 +78,7 @@
* Tests that atrace exists and is runnable with no args
*/
public void testSimpleRun() throws Exception {
- String output = mDevice.executeShellCommand("atrace");
+ String output = getDevice().executeShellCommand("atrace");
String[] lines = output.split("\\r?\\n");
// check for expected stdout
@@ -80,7 +93,7 @@
* Tests the output of "atrace --list_categories" to ensure required categories exist.
*/
public void testCategories() throws Exception {
- String output = mDevice.executeShellCommand("atrace --list_categories");
+ String output = getDevice().executeShellCommand("atrace --list_categories");
String[] categories = output.split("\\r?\\n");
Set<String> requiredCategories = new HashSet<String>(sRequiredCategoriesList);
@@ -101,4 +114,120 @@
fail("Expected categories missing from atrace");
}
}
+
+
+ private static final String TRACE_MARKER_REGEX =
+ "\\s*(\\S+)-(\\d+)\\s+\\(\\s*(\\d+)\\).*tracing_mark_write:\\s*(B|E)(.*)";
+ /**
+ * Tests that atrace captures app launch, including app level tracing
+ */
+ public void testTracingContent() throws Exception {
+ String atraceOutput = null;
+ try {
+ // cleanup test apps that might be installed from previous partial test run
+ getDevice().uninstallPackage(TEST_PKG);
+
+ // install the test app
+ File testAppFile = mCtsBuild.getTestApp(TEST_APK);
+ String installResult = getDevice().installPackage(testAppFile, false);
+ assertNull(
+ String.format("failed to install simple app. Reason: %s", installResult),
+ installResult);
+
+ // capture a launch of the app with async tracing
+ String atraceArgs = "-a " + TEST_PKG + " -c -b 16000 view"; // TODO: zipping
+ getDevice().executeShellCommand("atrace --async_stop " + atraceArgs);
+ getDevice().executeShellCommand("atrace --async_start " + atraceArgs);
+ String start = getDevice().executeShellCommand("am start " + TEST_PKG);
+ getDevice().executeShellCommand("sleep 1");
+ atraceOutput = getDevice().executeShellCommand("atrace --async_dump " + atraceArgs);
+ } finally {
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+ assertNotNull(atraceOutput);
+
+
+ // now parse the trace data (see external/chromium-trace/systrace.py)
+ final String MARKER = "TRACE:";
+ int dataStart = atraceOutput.indexOf(MARKER);
+ assertTrue(dataStart >= 0);
+ String traceData = atraceOutput.substring(dataStart + MARKER.length());
+
+ /**
+ * Pattern that matches standard begin/end userspace tracing.
+ *
+ * Groups are:
+ * 1 - truncated thread name
+ * 2 - tid
+ * 3 - pid
+ * 4 - B/E
+ * 5 - ignored, for grouping
+ * 6 - if B, section title, else null
+ */
+ final Pattern beginEndPattern = Pattern.compile(
+ "\\s*(\\S+)-(\\d+)\\s+\\(\\s*(\\d+)\\).*tracing_mark_write:\\s*(B|E)(\\|\\d+\\|(.+))?");
+
+ int appPid = -1;
+ String line;
+
+ // list of tags expected to be seen on app launch, in order.
+ String[] requiredSectionList = {
+ "traceable-app-test-section",
+ "inflate",
+ "performTraversals",
+ "measure",
+ "layout",
+ "draw",
+ "Record View#draw()"
+ };
+ int nextSectionIndex = 0;
+ int matches = 0;
+ try (BufferedReader reader = new BufferedReader(new StringReader(traceData))) {
+ while ((line = reader.readLine()) != null) {
+ Matcher matcher = beginEndPattern.matcher(line);
+ if (matcher.find()) {
+ matches++;
+
+ String truncatedThreadName = matcher.group(1);
+ assertNotNull(truncatedThreadName);
+
+ int tid = assertInt(matcher.group(2));
+ assertTrue(tid > 0);
+ int pid = assertInt(matcher.group(3));
+ assertTrue(pid > 0);
+
+ if (TEST_PKG.endsWith(truncatedThreadName)) {
+ // should be something like "s.aptracetestapp" since beginning may be truncated
+ if (appPid == -1) {
+ appPid = pid;
+ } else {
+ assertEquals(appPid, pid);
+ }
+
+ if ("B".equals(matcher.group(4))) {
+ String sectionTitle = matcher.group(6);
+ if (nextSectionIndex < requiredSectionList.length
+ && requiredSectionList[nextSectionIndex].equals(sectionTitle)) {
+ nextSectionIndex++;
+ }
+ }
+ }
+ }
+ }
+ }
+ assertTrue("Unable to parse any userspace sections from atrace output",
+ matches != 0);
+ assertEquals("Didn't see required list of traced sections, in order",
+ requiredSectionList.length, nextSectionIndex);
+ }
+
+ private static int assertInt(String input) {
+ try {
+ return Integer.parseInt(input);
+ } catch (NumberFormatException e) {
+ fail("Expected an integer but found \"" + input + "\"");
+ // Won't be hit, above throws AssertException
+ return -1;
+ }
+ }
}
diff --git a/hostsidetests/devicepolicy/app/IntentSender/Android.mk b/hostsidetests/devicepolicy/app/IntentSender/Android.mk
index 21d2866..e5246c5 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/Android.mk
+++ b/hostsidetests/devicepolicy/app/IntentSender/Android.mk
@@ -24,7 +24,9 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := android.test.runner android-support-v4
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctstestrunner
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
index b74a7b6..f4adb31 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/Android.mk
@@ -24,9 +24,9 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit android-support-v4
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctstestrunner
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
new file mode 100644
index 0000000..2faf158
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.managedprofile;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+
+import com.android.cts.managedprofile.BaseManagedProfileTest;
+
+import org.junit.Ignore;
+
+/**
+ * Test Runtime Permissions APIs in DevicePolicyManager.
+ */
+public class PermissionsTest extends BaseManagedProfileTest {
+
+ private static final String SIMPLE_APP_PACKAGE_NAME = "com.android.cts.launcherapps.simpleapp";
+ private static final String PERMISSION_NAME = "android.permission.READ_CONTACTS";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // Make sure we are running in a managed profile, otherwise risk wiping the primary user's
+ // data.
+ assertTrue(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
+ assertTrue(mDevicePolicyManager.isProfileOwnerApp(
+ ADMIN_RECEIVER_COMPONENT.getPackageName()));
+ }
+
+ public void testPermissionGrantState() {
+ PackageManager pm = mContext.getPackageManager();
+ mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
+ PackageManager.PERMISSION_DENIED);
+
+ mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ // Should stay denied
+ assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
+ PackageManager.PERMISSION_DENIED);
+
+ mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
+ PackageManager.PERMISSION_GRANTED);
+
+ mDevicePolicyManager.setPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME,
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertTrue(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ SIMPLE_APP_PACKAGE_NAME, PERMISSION_NAME) ==
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ // Should stay granted
+ assertEquals(pm.checkPermission(PERMISSION_NAME, SIMPLE_APP_PACKAGE_NAME),
+ PackageManager.PERMISSION_GRANTED);
+
+ mDevicePolicyManager.setPermissionPolicy(ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY);
+ assertEquals(mDevicePolicyManager.getPermissionPolicy(ADMIN_RECEIVER_COMPONENT),
+ DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY);
+
+ mDevicePolicyManager.setPermissionPolicy(ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
+ assertEquals(mDevicePolicyManager.getPermissionPolicy(ADMIN_RECEIVER_COMPONENT),
+ DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
+
+ mDevicePolicyManager.setPermissionPolicy(ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.PERMISSION_POLICY_PROMPT);
+ assertEquals(mDevicePolicyManager.getPermissionPolicy(ADMIN_RECEIVER_COMPONENT),
+ DevicePolicyManager.PERMISSION_POLICY_PROMPT);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
index af74f57..791ae56 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/SimpleApp/AndroidManifest.xml
@@ -18,6 +18,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.launcherapps.simpleapp">
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+
<application>
<activity android:name=".SimpleActivity" >
<intent-filter>
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 5c2048e..7d8fa67 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -30,6 +30,8 @@
private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
+ private static final String SIMPLE_APP_APK = "CtsSimpleApp.apk";
+
private static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
private static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
@@ -406,6 +408,15 @@
}
}
+ public void testPermissionGrant() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installApp(SIMPLE_APP_APK);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionGrantState", mUserId));
+ }
+
private void disableActivityForUser(String activityName, int userId)
throws DeviceNotAvailableException {
String command = "am start -W --user " + userId
diff --git a/tests/tests/accounts/AndroidManifest.xml b/tests/tests/accounts/AndroidManifest.xml
index c671ff0..93529d0 100644
--- a/tests/tests/accounts/AndroidManifest.xml
+++ b/tests/tests/accounts/AndroidManifest.xml
@@ -43,10 +43,21 @@
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
-
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
+
+ <service android:name="MockCustomTokenAccountService" android:exported="true"
+ android:process="android.accounts.cts">
+ <intent-filter>
+ <action android:name="android.accounts.AccountAuthenticator" />
+ </intent-filter>
+ <meta-data android:name="android.accounts.AccountAuthenticator"
+ android:resource="@xml/custom_token_authenticator" />
+ <meta-data android:name="android.accounts.AccountAuthenticator.customTokens"
+ android:value="1" />
+
+ </service>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/accounts/res/drawable/ic_cts_minitab_selected_custom_account.png b/tests/tests/accounts/res/drawable/ic_cts_minitab_selected_custom_account.png
new file mode 100644
index 0000000..3fbbc94
--- /dev/null
+++ b/tests/tests/accounts/res/drawable/ic_cts_minitab_selected_custom_account.png
Binary files differ
diff --git a/tests/tests/accounts/res/drawable/ic_cts_selected_custom_account.png b/tests/tests/accounts/res/drawable/ic_cts_selected_custom_account.png
new file mode 100644
index 0000000..70e35c0
--- /dev/null
+++ b/tests/tests/accounts/res/drawable/ic_cts_selected_custom_account.png
Binary files differ
diff --git a/tests/tests/accounts/res/xml/custom_token_authenticator.xml b/tests/tests/accounts/res/xml/custom_token_authenticator.xml
new file mode 100644
index 0000000..3337917
--- /dev/null
+++ b/tests/tests/accounts/res/xml/custom_token_authenticator.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2009, 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.
+ */
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the Account Manager. -->
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:accountType="android.accounts.cts.custom.account.type"
+ android:icon="@drawable/ic_cts_selected_custom_account"
+ android:smallIcon="@drawable/ic_cts_minitab_selected_custom_account"
+ android:customTokens="true"
+ android:label="@string/label"
+/>
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
index c582c79..0e84fb9 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
@@ -26,16 +26,19 @@
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.content.Context;
-import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.Looper;
import android.os.StrictMode;
import android.test.ActivityInstrumentationTestCase2;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
/**
* You can run those unit tests with the following command line:
@@ -52,13 +55,15 @@
public static final String ACCOUNT_NAME_OTHER = "android.accounts.cts.account.name.other";
public static final String ACCOUNT_TYPE = "android.accounts.cts.account.type";
- public static final String ACCOUNT_TYPE_OTHER = "android.accounts.cts.account.type.other";
+ public static final String ACCOUNT_TYPE_CUSTOM = "android.accounts.cts.custom.account.type";
+ public static final String ACCOUNT_TYPE_ABSENT = "android.accounts.cts.account.type.absent";
public static final String ACCOUNT_PASSWORD = "android.accounts.cts.account.password";
- public static final String AUTH_TOKEN = "mockAuthToken";
public static final String AUTH_TOKEN_TYPE = "mockAuthTokenType";
+ public static final String AUTH_EXPIRING_TOKEN_TYPE = "mockAuthExpiringTokenType";
public static final String AUTH_TOKEN_LABEL = "mockAuthTokenLabel";
+ public static final long AUTH_TOKEN_DURATION_MILLIS = 10000L; // Ten seconds.
public static final String FEATURE_1 = "feature.1";
public static final String FEATURE_2 = "feature.2";
@@ -86,6 +91,9 @@
MockAccountAuthenticator.ACCOUNT_NAME_FOR_NEW_REMOVE_API, ACCOUNT_TYPE);
public static final Account ACCOUNT_SAME_TYPE = new Account(ACCOUNT_NAME_OTHER, ACCOUNT_TYPE);
+ public static final Account CUSTOM_TOKEN_ACCOUNT =
+ new Account(ACCOUNT_NAME,ACCOUNT_TYPE_CUSTOM);
+
private static MockAccountAuthenticator mockAuthenticator;
private static final int LATCH_TIMEOUT_MS = 500;
private static AccountManager am;
@@ -130,16 +138,101 @@
assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, mActivity, null /* callback */).getBoolean(
AccountManager.KEY_BOOLEAN_RESULT));
+ // Clean out any other accounts added during the tests.
+ Account[] ctsAccounts = am.getAccountsByType(ACCOUNT_TYPE);
+ Account[] ctsCustomAccounts = am.getAccountsByType(ACCOUNT_TYPE_CUSTOM);
+ ArrayList<Account> accounts = new ArrayList<>(Arrays.asList(ctsAccounts));
+ accounts.addAll(Arrays.asList(ctsCustomAccounts));
+ for (Account ctsAccount : accounts) {
+ removeAccount(am, ctsAccount, mActivity, null /* callback */);
+ }
+
// need to clean up the authenticator cached data
mockAuthenticator.clearData();
super.tearDown();
}
- private void validateAccountAndAuthTokenResult(Bundle result) {
- assertEquals(ACCOUNT_NAME, result.get(AccountManager.KEY_ACCOUNT_NAME));
- assertEquals(ACCOUNT_TYPE, result.get(AccountManager.KEY_ACCOUNT_TYPE));
- assertEquals(AUTH_TOKEN, result.get(AccountManager.KEY_AUTHTOKEN));
+ interface TokenFetcher {
+ public Bundle fetch(String tokenType)
+ throws OperationCanceledException, AuthenticatorException, IOException;
+ public Account getAccount();
+ }
+
+ private void validateSuccessfulTokenFetchingLifecycle(TokenFetcher fetcher, String tokenType)
+ throws OperationCanceledException, AuthenticatorException, IOException {
+ Account account = fetcher.getAccount();
+ Bundle expected = new Bundle();
+ expected.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+ expected.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+
+ // First fetch.
+ Bundle actual = fetcher.fetch(tokenType);
+ assertTrue(mockAuthenticator.isRecentlyCalled());
+ validateAccountAndAuthTokenResult(expected, actual);
+
+ /*
+ * On the second fetch the cache will be populated if we are using a authenticator with
+ * customTokens=false or we are using a scope that will cause the authenticator to set an
+ * expiration time (and that expiration time hasn't been reached).
+ */
+ actual = fetcher.fetch(tokenType);
+
+ boolean isCachingExpected =
+ ACCOUNT_TYPE.equals(account.type) || AUTH_EXPIRING_TOKEN_TYPE.equals(tokenType);
+ assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled());
+ validateAccountAndAuthTokenResult(expected, actual);
+
+ try {
+ // Delay further execution until expiring tokens can actually expire.
+ Thread.sleep(mockAuthenticator.getTokenDurationMillis() + 1L);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ /*
+ * With the time shift above, the third request will result in cache hits only from
+ * customToken=false authenticators.
+ */
+ actual = fetcher.fetch(tokenType);
+ isCachingExpected = ACCOUNT_TYPE.equals(account.type);
+ assertEquals(isCachingExpected, !mockAuthenticator.isRecentlyCalled());
+ validateAccountAndAuthTokenResult(expected, actual);
+
+ // invalidate token
+ String token = actual.getString(AccountManager.KEY_AUTHTOKEN);
+ am.invalidateAuthToken(account.type, token);
+
+ /*
+ * Upon invalidating the token, the cache should be clear regardless of authenticator.
+ */
+ actual = fetcher.fetch(tokenType);
+ assertTrue(mockAuthenticator.isRecentlyCalled());
+ validateAccountAndAuthTokenResult(expected, actual);
+ }
+
+ private void validateAccountAndAuthTokenResult(Bundle actual) {
+ assertEquals(
+ ACCOUNT.name,
+ actual.get(AccountManager.KEY_ACCOUNT_NAME));
+ assertEquals(
+ ACCOUNT.type,
+ actual.get(AccountManager.KEY_ACCOUNT_TYPE));
+ assertEquals(
+ mockAuthenticator.getLastTokenServed(),
+ actual.get(AccountManager.KEY_AUTHTOKEN));
+ }
+
+ private void validateAccountAndAuthTokenResult(Bundle expected, Bundle actual) {
+ assertEquals(
+ expected.get(AccountManager.KEY_ACCOUNT_NAME),
+ actual.get(AccountManager.KEY_ACCOUNT_NAME));
+ assertEquals(
+ expected.get(AccountManager.KEY_ACCOUNT_TYPE),
+ actual.get(AccountManager.KEY_ACCOUNT_TYPE));
+ assertEquals(
+ mockAuthenticator.getLastTokenServed(),
+ actual.get(AccountManager.KEY_AUTHTOKEN));
}
private void validateAccountAndNoAuthTokenResult(Bundle result) {
@@ -224,7 +317,6 @@
private boolean removeAccount(AccountManager am, Account account,
AccountManagerCallback<Boolean> callback) throws IOException, AuthenticatorException,
OperationCanceledException {
-
AccountManagerFuture<Boolean> futureBoolean = am.removeAccount(account,
callback,
null /* handler */);
@@ -357,6 +449,7 @@
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
@@ -483,9 +576,7 @@
/**
* Test addAccountExplicitly() and removeAccountExplictly().
*/
- public void testAddAccountExplicitlyAndRemoveAccountExplicitly() throws IOException,
- AuthenticatorException, OperationCanceledException {
-
+ public void testAddAccountExplicitlyAndRemoveAccountExplicitly() {
final int expectedAccountsCount = getAccountsCount();
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
@@ -581,7 +672,7 @@
assertEquals(2, accounts.length);
// Check if we dont have any account from the other type
- accounts = am.getAccountsByType(ACCOUNT_TYPE_OTHER);
+ accounts = am.getAccountsByType(ACCOUNT_TYPE_ABSENT);
assertEquals(0, accounts.length);
}
@@ -684,6 +775,7 @@
final CountDownLatch latch1 = new CountDownLatch(1);
AccountManagerCallback<Account[]> callback1 = new AccountManagerCallback<Account[]>() {
+ @Override
public void run(AccountManagerFuture<Account[]> accountsFuture) {
try {
Account[] accounts = accountsFuture.getResult();
@@ -724,6 +816,7 @@
final CountDownLatch latch2 = new CountDownLatch(1);
AccountManagerCallback<Account[]> callback2 = new AccountManagerCallback<Account[]>() {
+ @Override
public void run(AccountManagerFuture<Account[]> accountsFuture) {
try {
Account[] accounts = accountsFuture.getResult();
@@ -765,25 +858,24 @@
*/
public void testSetAndPeekAndInvalidateAuthToken() {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
-
- am.setAuthToken(ACCOUNT, AUTH_TOKEN_TYPE, AUTH_TOKEN);
+ String expected = "x";
+ am.setAuthToken(ACCOUNT, AUTH_TOKEN_TYPE, expected);
// Ask for the AuthToken
String token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE);
assertNotNull(token);
- assertEquals(AUTH_TOKEN, token);
+ assertEquals(expected, token);
- am.invalidateAuthToken(ACCOUNT_TYPE, AUTH_TOKEN);
+ am.invalidateAuthToken(ACCOUNT_TYPE, token);
token = am.peekAuthToken(ACCOUNT, AUTH_TOKEN_TYPE);
assertNull(token);
}
/**
- * Test blockingGetAuthToken()
+ * Test successful blockingGetAuthToken() with customTokens=false authenticator.
*/
- public void testBlockingGetAuthToken() throws IOException, AuthenticatorException,
- OperationCanceledException {
-
+ public void testBlockingGetAuthToken_DefaultToken_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null);
String token = am.blockingGetAuthToken(ACCOUNT,
@@ -792,17 +884,61 @@
// Ask for the AuthToken
assertNotNull(token);
- assertEquals(AUTH_TOKEN, token);
+ assertEquals(mockAuthenticator.getLastTokenServed(), token);
+ }
+
+ private static class BlockingGetAuthTokenFetcher implements TokenFetcher {
+ private final Account mAccount;
+
+ BlockingGetAuthTokenFetcher(Account account) {
+ mAccount = account;
+ }
+
+ @Override
+ public Bundle fetch(String tokenType)
+ throws OperationCanceledException, AuthenticatorException, IOException {
+ String token = am.blockingGetAuthToken(
+ getAccount(),
+ tokenType,
+ false /* no failure notification */);
+ Bundle result = new Bundle();
+ result.putString(AccountManager.KEY_AUTHTOKEN, token);
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);
+ return result;
+ }
+ @Override
+ public Account getAccount() {
+ return CUSTOM_TOKEN_ACCOUNT;
+ }
}
/**
- * Test getAuthToken()
+ * Test successful blockingGetAuthToken() with customTokens=true authenticator.
*/
- public void testGetAuthToken() throws IOException, AuthenticatorException,
- OperationCanceledException {
+ public void testBlockingGetAuthToken_CustomToken_NoCaching_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
+ TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT);
+ validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE);
+ }
+ /**
+ * Test successful blockingGetAuthToken() with customTokens=true authenticator.
+ */
+ public void testBlockingGetAuthToken_CustomToken_ExpiringCache_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
+ TokenFetcher f = new BlockingGetAuthTokenFetcher(CUSTOM_TOKEN_ACCOUNT);
+ validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE);
+ }
+
+ /**
+ * Test successful getAuthToken() using a future with customTokens=false authenticator.
+ */
+ public void testDeprecatedGetAuthTokenWithFuture_NoOptions_DefaultToken_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
-
AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT,
AUTH_TOKEN_TYPE,
false /* no failure notification */,
@@ -820,6 +956,226 @@
}
/**
+ * Test successful getAuthToken() using a future with customTokens=false without
+ * expiring tokens.
+ */
+ public void testDeprecatedGetAuthTokenWithFuture_NoOptions_CustomToken_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
+ // validateSuccessfulTokenFetchingLifecycle(AccountManager am, TokenFetcher fetcher, String tokenType)
+ TokenFetcher f = new TokenFetcher() {
+ @Override
+ public Bundle fetch(String tokenType)
+ throws OperationCanceledException, AuthenticatorException, IOException {
+ AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(
+ getAccount(),
+ tokenType,
+ false /* no failure notification */,
+ null /* no callback */,
+ null /* no handler */
+ );
+ Bundle actual = futureBundle.getResult();
+ assertTrue(futureBundle.isDone());
+ assertNotNull(actual);
+ return actual;
+ }
+
+ @Override
+ public Account getAccount() {
+ return CUSTOM_TOKEN_ACCOUNT;
+ }
+ };
+ validateSuccessfulTokenFetchingLifecycle(f, AUTH_EXPIRING_TOKEN_TYPE);
+ validateSuccessfulTokenFetchingLifecycle(f, AUTH_TOKEN_TYPE);
+ }
+
+ /**
+ * Test successful getAuthToken() using a future with customTokens=false without
+ * expiring tokens.
+ */
+ public void testGetAuthTokenWithFuture_Options_DefaultToken_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
+
+ AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT,
+ AUTH_TOKEN_TYPE,
+ OPTIONS_BUNDLE,
+ mActivity,
+ null /* no callback */,
+ null /* no handler */
+ );
+
+ Bundle resultBundle = futureBundle.getResult();
+
+ assertTrue(futureBundle.isDone());
+ assertNotNull(resultBundle);
+
+ // Assert returned result
+ validateAccountAndAuthTokenResult(resultBundle);
+
+ validateOptions(null, mockAuthenticator.mOptionsAddAccount);
+ validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
+ validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
+ validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsGetAuthToken);
+ validateSystemOptions(mockAuthenticator.mOptionsGetAuthToken);
+ }
+
+ /**
+ * Test successful getAuthToken() using a future with customTokens=false without
+ * expiring tokens.
+ */
+ public void testGetAuthTokenWithFuture_Options_CustomToken_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
+ TokenFetcher fetcherWithOptions = new TokenFetcher() {
+ @Override
+ public Bundle fetch(String tokenType)
+ throws OperationCanceledException, AuthenticatorException, IOException {
+ AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(
+ getAccount(),
+ tokenType,
+ OPTIONS_BUNDLE,
+ false /* no failure notification */,
+ null /* no callback */,
+ null /* no handler */
+ );
+ Bundle actual = futureBundle.getResult();
+ assertTrue(futureBundle.isDone());
+ assertNotNull(actual);
+ return actual;
+ }
+
+ @Override
+ public Account getAccount() {
+ return CUSTOM_TOKEN_ACCOUNT;
+ }
+ };
+ validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE);
+ validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE);
+ }
+
+
+ /**
+ * Test successful getAuthToken() using a future with customTokens=false without
+ * expiring tokens.
+ */
+ public void testGetAuthTokenWithCallback_Options_Handler_DefaultToken_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null);
+ final HandlerThread handlerThread = new HandlerThread("accounts.test");
+ handlerThread.start();
+ TokenFetcher fetcherWithOptions = new TokenFetcher() {
+ @Override
+ public Bundle fetch(String tokenType)
+ throws OperationCanceledException, AuthenticatorException, IOException {
+ final AtomicReference<Bundle> actualRef = new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
+ public void run(AccountManagerFuture<Bundle> bundleFuture) {
+ Bundle resultBundle = null;
+ try {
+ resultBundle = bundleFuture.getResult();
+ actualRef.set(resultBundle);
+ } catch (OperationCanceledException e) {
+ fail("should not throw an OperationCanceledException");
+ } catch (IOException e) {
+ fail("should not throw an IOException");
+ } catch (AuthenticatorException e) {
+ fail("should not throw an AuthenticatorException");
+ } finally {
+ latch.countDown();
+ }
+ }
+ };
+
+ am.getAuthToken(getAccount(),
+ tokenType,
+ OPTIONS_BUNDLE,
+ false /* no failure notification */,
+ callback,
+ new Handler(handlerThread.getLooper()));
+
+ // Wait with timeout for the callback to do its work
+ try {
+ latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("should not throw an InterruptedException");
+ }
+ return actualRef.get();
+ }
+
+ @Override
+ public Account getAccount() {
+ return ACCOUNT;
+ }
+ };
+ validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE);
+ validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE);
+ }
+
+ /**
+ * Test successful getAuthToken() using a future with customTokens=false without
+ * expiring tokens.
+ */
+ public void testGetAuthTokenWithCallback_Options_Handler_CustomToken_Success()
+ throws IOException, AuthenticatorException, OperationCanceledException {
+ addAccountExplicitly(CUSTOM_TOKEN_ACCOUNT, ACCOUNT_PASSWORD, null);
+ final HandlerThread handlerThread = new HandlerThread("accounts.test");
+ handlerThread.start();
+ TokenFetcher fetcherWithOptions = new TokenFetcher() {
+ @Override
+ public Bundle fetch(String tokenType)
+ throws OperationCanceledException, AuthenticatorException, IOException {
+ final AtomicReference<Bundle> actualRef = new AtomicReference<>();
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
+ public void run(AccountManagerFuture<Bundle> bundleFuture) {
+ Bundle resultBundle = null;
+ try {
+ resultBundle = bundleFuture.getResult();
+ actualRef.set(resultBundle);
+ } catch (OperationCanceledException e) {
+ fail("should not throw an OperationCanceledException");
+ } catch (IOException e) {
+ fail("should not throw an IOException");
+ } catch (AuthenticatorException e) {
+ fail("should not throw an AuthenticatorException");
+ } finally {
+ latch.countDown();
+ }
+ }
+ };
+
+ am.getAuthToken(getAccount(),
+ tokenType,
+ OPTIONS_BUNDLE,
+ false /* no failure notification */,
+ callback,
+ new Handler(handlerThread.getLooper()));
+
+ // Wait with timeout for the callback to do its work
+ try {
+ latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ fail("should not throw an InterruptedException");
+ }
+ return actualRef.get();
+ }
+
+ @Override
+ public Account getAccount() {
+ return CUSTOM_TOKEN_ACCOUNT;
+ }
+ };
+ validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_TOKEN_TYPE);
+ validateSuccessfulTokenFetchingLifecycle(fetcherWithOptions, AUTH_EXPIRING_TOKEN_TYPE);
+ }
+
+ /**
* Test getAuthToken() with callback and handler
*/
public void testGetAuthTokenWithCallbackAndHandler() throws IOException, AuthenticatorException,
@@ -837,6 +1193,7 @@
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
@@ -880,37 +1237,6 @@
}
/**
- * test getAuthToken() with options
- */
- public void testGetAuthTokenWithOptions() throws IOException, AuthenticatorException,
- OperationCanceledException {
-
- addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
-
- AccountManagerFuture<Bundle> futureBundle = am.getAuthToken(ACCOUNT,
- AUTH_TOKEN_TYPE,
- OPTIONS_BUNDLE,
- mActivity,
- null /* no callback */,
- null /* no handler */
- );
-
- Bundle resultBundle = futureBundle.getResult();
-
- assertTrue(futureBundle.isDone());
- assertNotNull(resultBundle);
-
- // Assert returned result
- validateAccountAndAuthTokenResult(resultBundle);
-
- validateOptions(null, mockAuthenticator.mOptionsAddAccount);
- validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
- validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
- validateOptions(OPTIONS_BUNDLE, mockAuthenticator.mOptionsGetAuthToken);
- validateSystemOptions(mockAuthenticator.mOptionsGetAuthToken);
- }
-
- /**
* test getAuthToken() with options and callback and handler
*/
public void testGetAuthTokenWithOptionsAndCallback() throws IOException,
@@ -928,15 +1254,14 @@
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
try {
resultBundle = bundleFuture.getResult();
-
// Assert returned result
validateAccountAndAuthTokenResult(resultBundle);
-
} catch (OperationCanceledException e) {
fail("should not throw an OperationCanceledException");
} catch (IOException e) {
@@ -1039,7 +1364,6 @@
validateOptions(null, mockAuthenticator.mOptionsUpdateCredentials);
validateOptions(null, mockAuthenticator.mOptionsConfirmCredentials);
validateOptions(null, mockAuthenticator.mOptionsGetAuthToken);
-
}
/**
@@ -1154,8 +1478,7 @@
/**
* Test confirmCredentials() with callback
*/
- public void testConfirmCredentialsWithCallbackAndHandler() throws IOException,
- AuthenticatorException, OperationCanceledException {
+ public void testConfirmCredentialsWithCallbackAndHandler() {
addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
@@ -1163,12 +1486,10 @@
testConfirmCredentialsWithCallbackAndHandler(new Handler(Looper.getMainLooper()));
}
- private void testConfirmCredentialsWithCallbackAndHandler(Handler handler) throws IOException,
- AuthenticatorException, OperationCanceledException {
-
+ private void testConfirmCredentialsWithCallbackAndHandler(Handler handler) {
final CountDownLatch latch = new CountDownLatch(1);
-
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
@@ -1191,15 +1512,11 @@
}
}
};
-
AccountManagerFuture<Bundle> futureBundle = am.confirmCredentials(ACCOUNT,
OPTIONS_BUNDLE,
mActivity,
callback,
handler);
-
- // futureBundle.getResult();
-
// Wait with timeout for the callback to do its work
try {
latch.await(3 * LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
@@ -1249,6 +1566,7 @@
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
Bundle resultBundle = null;
@@ -1320,6 +1638,7 @@
final CountDownLatch latch = new CountDownLatch(1);
AccountManagerCallback<Bundle> callback = new AccountManagerCallback<Bundle>() {
+ @Override
public void run(AccountManagerFuture<Bundle> bundleFuture) {
try {
// Assert returned result
@@ -1381,6 +1700,7 @@
final CountDownLatch latch = new CountDownLatch(1);
OnAccountsUpdateListener listener = new OnAccountsUpdateListener() {
+ @Override
public void onAccountsUpdated(Account[] accounts) {
latch.countDown();
}
@@ -1423,6 +1743,7 @@
final CountDownLatch latch = new CountDownLatch(1);
OnAccountsUpdateListener listener = new OnAccountsUpdateListener() {
+ @Override
public void onAccountsUpdated(Account[] accounts) {
fail("should not be called");
}
@@ -1513,6 +1834,7 @@
private AccountManagerCallback<Boolean> getAssertTrueCallback(final CountDownLatch latch) {
return new AccountManagerCallback<Boolean>() {
+ @Override
public void run(AccountManagerFuture<Boolean> booleanFuture) {
try {
// Assert returned result should be TRUE
@@ -1528,6 +1850,7 @@
private AccountManagerCallback<Boolean> getAssertFalseCallback(final CountDownLatch latch) {
return new AccountManagerCallback<Boolean>() {
+ @Override
public void run(AccountManagerFuture<Boolean> booleanFuture) {
try {
// Assert returned result should be FALSE
@@ -1613,7 +1936,7 @@
Bundle options = new Bundle();
options.putBoolean(MockAccountAuthenticator.KEY_RETURN_INTENT, true);
// Not really confirming, but a way to get last authenticated timestamp
- Bundle result = am.confirmCredentials(ACCOUNT,
+ Bundle result = am.confirmCredentials(account,
options,// OPTIONS_BUNDLE,
null, /* activity */
null /* callback */,
@@ -1631,8 +1954,7 @@
/**
* Tests that AccountManagerService is properly caching data.
*/
- public void testGetsAreCached() throws IOException, AuthenticatorException,
- OperationCanceledException {
+ public void testGetsAreCached() {
// Add an account,
assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT));
diff --git a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
index eec4eff..faebd53 100644
--- a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
+++ b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
@@ -24,13 +24,17 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.util.Log;
import java.util.ArrayList;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A simple Mock Account Authenticator
*/
public class MockAccountAuthenticator extends AbstractAccountAuthenticator {
+ private static String TAG = "AccountManagerTest";
public static String KEY_ACCOUNT_INFO = "key_account_info";
public static String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "key_account_authenticator_response";
@@ -40,6 +44,9 @@
public static String ACCOUNT_NAME_FOR_NEW_REMOVE_API1 = "call new removeAccount api";
private final Context mContext;
+ private final AtomicInteger mTokenCounter = new AtomicInteger(0);
+ private final AtomicBoolean mIsRecentlyCalled = new AtomicBoolean(false);
+
AccountAuthenticatorResponse mResponse;
String mAccountType;
String mAuthTokenType;
@@ -52,6 +59,7 @@
String[] mFeatures;
final ArrayList<String> mockFeatureList = new ArrayList<String>();
+ private final long mTokenDurationMillis = 1000; // 1 second
public MockAccountAuthenticator(Context context) {
super(context);
@@ -62,6 +70,18 @@
mockFeatureList.add(AccountManagerTest.FEATURE_2);
}
+ public long getTokenDurationMillis() {
+ return mTokenDurationMillis;
+ }
+
+ public boolean isRecentlyCalled() {
+ return mIsRecentlyCalled.getAndSet(false);
+ }
+
+ public String getLastTokenServed() {
+ return Integer.toString(mTokenCounter.get());
+ }
+
public AccountAuthenticatorResponse getResponse() {
return mResponse;
}
@@ -113,8 +133,9 @@
Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, AccountManagerTest.ACCOUNT_NAME);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, AccountManagerTest.ACCOUNT_TYPE);
- result.putString(AccountManager.KEY_AUTHTOKEN, AccountManagerTest.AUTH_TOKEN);
-
+ result.putString(
+ AccountManager.KEY_AUTHTOKEN,
+ Integer.toString(mTokenCounter.incrementAndGet()));
return result;
}
@@ -125,13 +146,11 @@
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
String authTokenType, String[] requiredFeatures, Bundle options)
throws NetworkErrorException {
-
this.mResponse = response;
this.mAccountType = accountType;
this.mAuthTokenType = authTokenType;
this.mRequiredFeatures = requiredFeatures;
this.mOptionsAddAccount = options;
-
return createResultBundle();
}
@@ -141,12 +160,10 @@
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
String authTokenType, Bundle options) throws NetworkErrorException {
-
this.mResponse = response;
this.mAccount = account;
this.mAuthTokenType = authTokenType;
this.mOptionsUpdateCredentials = options;
-
return createResultBundle();
}
@@ -157,10 +174,8 @@
*/
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
-
this.mResponse = response;
this.mAccountType = accountType;
-
return createResultBundle();
}
@@ -170,11 +185,9 @@
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
Bundle options) throws NetworkErrorException {
-
this.mResponse = response;
this.mAccount = account;
this.mOptionsConfirmCredentials = options;
-
Bundle result = new Bundle();
if (options.containsKey(KEY_RETURN_INTENT)) {
Intent intent = new Intent();
@@ -191,15 +204,35 @@
* Gets the authtoken for an account.
*/
@Override
- public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
- String authTokenType, Bundle options) throws NetworkErrorException {
-
+ public Bundle getAuthToken(
+ AccountAuthenticatorResponse response,
+ Account account,
+ String authTokenType,
+ Bundle options) throws NetworkErrorException {
+ Log.w(TAG, "MockAuth - getAuthToken@" + System.currentTimeMillis());
+ mIsRecentlyCalled.set(true);
this.mResponse = response;
this.mAccount = account;
this.mAuthTokenType = authTokenType;
this.mOptionsGetAuthToken = options;
+ Bundle result = new Bundle();
- return createResultBundle();
+ result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+ result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+ String token;
+ if (AccountManagerTest.AUTH_EXPIRING_TOKEN_TYPE.equals(authTokenType)) {
+ /*
+ * The resultant token should simply be the expiration timestamp. E.g. the time after
+ * which getting a new AUTH_EXPIRING_TOKEN_TYPE typed token will return a different
+ * value.
+ */
+ long expiry = System.currentTimeMillis() + mTokenDurationMillis;
+ result.putLong(AbstractAccountAuthenticator.KEY_CUSTOM_TOKEN_EXPIRY, expiry);
+ }
+ result.putString(
+ AccountManager.KEY_AUTHTOKEN,
+ Integer.toString(mTokenCounter.incrementAndGet()));
+ return result;
}
/**
@@ -208,7 +241,6 @@
@Override
public String getAuthTokenLabel(String authTokenType) {
this.mAuthTokenType = authTokenType;
-
return AccountManagerTest.AUTH_TOKEN_LABEL;
}
@@ -261,4 +293,4 @@
}
return result;
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/accounts/src/android/accounts/cts/MockCustomTokenAccountService.java b/tests/tests/accounts/src/android/accounts/cts/MockCustomTokenAccountService.java
new file mode 100644
index 0000000..ea06a41
--- /dev/null
+++ b/tests/tests/accounts/src/android/accounts/cts/MockCustomTokenAccountService.java
@@ -0,0 +1,13 @@
+package android.accounts.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class MockCustomTokenAccountService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return AccountManagerTest.getMockAuthenticator(this).getIBinder();
+ }
+}
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 6cddfd1..a20befb 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -43,6 +43,12 @@
android:label="@string/permlab_costMoney"
android:description="@string/permdesc_costMoney"/>
+ <permission android:name="com.android.cts.content.CALL_ABROAD_PERMISSION"
+ android:label="@string/permlab_callAbroad"
+ android:description="@string/permdesc_callAbroad"
+ android:protectionLevel="normal"
+ android:permissionGroup="android.permission-group.COST_MONEY" />
+
<!-- Used for PackageManager test, don't delete! -->
<uses-configuration/>
<uses-feature android:name="android.hardware.camera" />
diff --git a/tests/tests/content/res/values/strings.xml b/tests/tests/content/res/values/strings.xml
index c546d8a..8ffb477 100644
--- a/tests/tests/content/res/values/strings.xml
+++ b/tests/tests/content/res/values/strings.xml
@@ -179,5 +179,7 @@
<string name="permlab_costMoney">Cost money</string>
<string name="permdesc_costMoney">Do things that can cost you money.</string>
+ <string name="permlab_callAbroad">Call abroad</string>
+ <string name="permdesc_callAbroad">Make calls abroad</string>
</resources>
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
index aaab8c4..cb3de2a 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
@@ -107,10 +107,10 @@
checkActivityInfoName(RECEIVER_NAME, broadcastReceivers);
// Test queryPermissionsByGroup, queryContentProviders
- String testPermissionsGroup = "android.permission-group.NETWORK";
+ String testPermissionsGroup = "android.permission-group.COST_MONEY";
List<PermissionInfo> permissions = mPackageManager.queryPermissionsByGroup(
testPermissionsGroup, PackageManager.GET_META_DATA);
- checkPermissionInfoName(PERMISSION_NAME, permissions);
+ checkPermissionInfoName("com.android.cts.content.CALL_ABROAD_PERMISSION", permissions);
ApplicationInfo appInfo = mPackageManager.getApplicationInfo(PACKAGE_NAME, 0);
List<ProviderInfo> providers = mPackageManager.queryContentProviders(PACKAGE_NAME,
@@ -148,17 +148,12 @@
}
private void checkPermissionInfoName(String expectedName, List<PermissionInfo> permissions) {
- boolean isContained = false;
- Iterator<PermissionInfo> infoIterator = permissions.iterator();
- String current;
- while (infoIterator.hasNext()) {
- current = infoIterator.next().name;
- if (current.equals(expectedName)) {
- isContained = true;
- break;
- }
+ List<String> names = new ArrayList<String>();
+ for (PermissionInfo permission : permissions) {
+ names.add(permission.name);
}
- assertTrue(isContained);
+ boolean isContained = names.contains(expectedName);
+ assertTrue("Permission " + expectedName + " not present in " + names, isContained);
}
private void checkProviderInfoName(String expectedName, List<ProviderInfo> providers) {
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
index 4a00adb..769e110 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimatedVectorDrawableTest.java
@@ -190,6 +190,19 @@
assertEquals(originalAlpha, d3.getAlpha());
}
+ public void testReset() {
+ final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
+ // The AVD has a duration as 100ms.
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ d1.reset();
+ assertFalse(d1.isRunning());
+ }
+ });
+
+ }
+
public void testAddCallback() throws InterruptedException {
MyCallback callback = new MyCallback();
final AnimatedVectorDrawable d1 = (AnimatedVectorDrawable) mResources.getDrawable(mResId);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
index fdb8dae..fdc7b7a 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
@@ -21,6 +21,9 @@
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.VectorDrawable;
import android.graphics.drawable.Drawable.ConstantState;
import android.test.AndroidTestCase;
@@ -288,4 +291,12 @@
d2.setAlpha(originalAlpha);
}
+
+ public void testColorFilter() {
+ PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.RED, Mode.SRC_IN);
+ VectorDrawable vectorDrawable = new VectorDrawable();
+ vectorDrawable.setColorFilter(filter);
+
+ assertEquals(filter, vectorDrawable.getColorFilter());
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
index 8ca650f..a9a6fce 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
@@ -77,7 +77,7 @@
* polynomial approximates the default tonemapping curve used for ACR3.
*/
private static final float[] DEFAULT_ACR3_TONEMAP_CURVE_COEFFS = new float[] {
- 1.041f, -2.973f, 2.932f, 0f
+ -1.087f, 1.643f, 0.443f, 0f
};
/**
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
index 577e830..d3f4ccd 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
@@ -36,9 +36,8 @@
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.ECKey;
+import java.security.interfaces.RSAKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1090,14 +1089,18 @@
final PrivateKey privKey = keyEntry.getPrivateKey();
final PublicKey pubKey = keyEntry.getCertificate().getPublicKey();
- if (expectedKey instanceof ECPrivateKey) {
+ if (expectedKey instanceof ECKey) {
+ assertTrue("Returned PrivateKey " + privKey.getClass() + " should be instanceof ECKey",
+ privKey instanceof ECKey);
assertEquals("Returned PrivateKey should be what we inserted",
- ((ECPrivateKey) expectedKey).getParams().getCurve(),
- ((ECPublicKey) pubKey).getParams().getCurve());
- } else if (expectedKey instanceof RSAPrivateKey) {
+ ((ECKey) expectedKey).getParams().getCurve(),
+ ((ECKey) privKey).getParams().getCurve());
+ } else if (expectedKey instanceof RSAKey) {
+ assertTrue("Returned PrivateKey " + privKey.getClass() + " should be instanceof RSAKey",
+ privKey instanceof RSAKey);
assertEquals("Returned PrivateKey should be what we inserted",
- ((RSAPrivateKey) expectedKey).getModulus(),
- ((RSAPrivateKey) privKey).getModulus());
+ ((RSAKey) expectedKey).getModulus(),
+ ((RSAKey) privKey).getModulus());
}
assertNull("getFormat() should return null", privKey.getFormat());
@@ -1143,15 +1146,16 @@
Key key = mKeyStore.getKey(TEST_ALIAS_1, null);
assertNotNull("Key should exist", key);
- assertTrue("Should be a RSAPrivateKey", key instanceof RSAPrivateKey);
+ assertTrue("Should be a PrivateKey", key instanceof PrivateKey);
+ assertTrue("Should be a RSAKey", key instanceof RSAKey);
- RSAPrivateKey actualKey = (RSAPrivateKey) key;
+ RSAKey actualKey = (RSAKey) key;
KeyFactory keyFact = KeyFactory.getInstance("RSA");
PrivateKey expectedKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
assertEquals("Inserted key should be same as retrieved key",
- ((RSAPrivateKey) expectedKey).getModulus(), actualKey.getModulus());
+ ((RSAKey) expectedKey).getModulus(), actualKey.getModulus());
}
public void testKeyStore_GetKey_Certificate_Unencrypted_Failure() throws Exception {
@@ -1893,14 +1897,17 @@
kpg.generateKeyPair();
- RSAPrivateKey key = (RSAPrivateKey) ks.getKey(alias, null);
- assertNotNull(key);
- String cipher = key.getAlgorithm() + "/NONE/NOPADDING";
+ PrivateKey privateKey = (PrivateKey) ks.getKey(alias, null);
+ assertNotNull(privateKey);
+ PublicKey publicKey = ks.getCertificate(alias).getPublicKey();
+ assertNotNull(publicKey);
+ String cipher = privateKey.getAlgorithm() + "/NONE/NOPADDING";
Cipher encrypt = Cipher.getInstance(cipher);
assertNotNull(encrypt);
- encrypt.init(Cipher.ENCRYPT_MODE, key);
+ encrypt.init(Cipher.ENCRYPT_MODE, privateKey);
- byte[] plainText = new byte[encrypt.getBlockSize()];
+ int modulusSizeBytes = (((RSAKey) publicKey).getModulus().bitLength() + 7) / 8;
+ byte[] plainText = new byte[modulusSizeBytes];
Arrays.fill(plainText, (byte) 0xFF);
// We expect a BadPaddingException here as the message size (plaintext)
diff --git a/tests/tests/location/src/android/location/cts/LocationTest.java b/tests/tests/location/src/android/location/cts/LocationTest.java
index b60b8b1..db2820c 100644
--- a/tests/tests/location/src/android/location/cts/LocationTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationTest.java
@@ -437,7 +437,7 @@
final long timeout = 500;
// should refuse binding
- IBinder binder = service.onBind(intent);
+ IBinder binder = service.callOnBind(intent);
assertNull("onBind should always fail.", binder);
// test if result consistent with the truth
@@ -445,7 +445,7 @@
service.setEnabled(false);
resultHandler.expectEnabled(false);
resultHandler.expectMessage(true);
- ret = service.onStartCommand(intent, SettingInjectorService.START_NOT_STICKY, 0);
+ ret = service.callOnStartCommand(intent, SettingInjectorService.START_NOT_STICKY, 0);
assertEquals("onStartCommand return value invalid in (enabled == false) case.",
ret, SettingInjectorService.START_NOT_STICKY);
assertTrue("Message time out in (enabled == false case).",
@@ -455,7 +455,7 @@
service.setEnabled(true);
resultHandler.expectEnabled(true);
resultHandler.expectMessage(true);
- ret = service.onStartCommand(intent, SettingInjectorService.START_NOT_STICKY, 0);
+ ret = service.callOnStartCommand(intent, SettingInjectorService.START_NOT_STICKY, 0);
assertEquals("onStartCommand return value invalid in (enabled == true) case.",
ret, SettingInjectorService.START_NOT_STICKY);
assertTrue("Message time out in (enabled == true) case.",
@@ -463,7 +463,7 @@
// should not respond to the deprecated method
resultHandler.expectMessage(false);
- service.onStart(intent, 0);
+ service.callOnStart(intent, 0);
resultHandler.waitForMessage(timeout);
}
@@ -574,6 +574,20 @@
public void setEnabled(boolean enabled) {
mEnabled = enabled;
}
+
+ // API coverage dashboard will not cound method call from derived class.
+ // Thus, it is necessary to make explicit call to SettingInjectorService public methods.
+ public IBinder callOnBind(Intent intent) {
+ return super.onBind(intent);
+ }
+
+ public void callOnStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+ }
+
+ public int callOnStartCommand(Intent intent, int flags, int startId) {
+ return super.onStartCommand(intent, flags, startId);
+ }
}
}
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 9014f8d..ee40207 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -1134,7 +1134,7 @@
public void testCodecEarlyEOSHEVC() throws Exception {
testCodecEarlyEOS(
- R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
+ R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz,
120 /* eosframe */);
}
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
old mode 100644
new mode 100755
index d3d15a5..26e0c54
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -70,6 +70,7 @@
"311660", // MetroPCS
"310120", // Sprint
"44050", // KDDI
+ "44051", // KDDI
"44053", // KDDI
"44054", // KDDI
"44070", // KDDI
@@ -129,6 +130,7 @@
Arrays.asList(
"44010", // NTT DOCOMO
"44020", // SBM
+ "44051", // KDDI
"302720", // Rogers
"30272", // Rogers
"302370", // Fido
@@ -167,6 +169,7 @@
Arrays.asList(
"44010", // NTT DOCOMO
"44020", // SBM
+ "44051", // KDDI
"302720", // Rogers
"30272", // Rogers
"302370", // Fido
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index 3a64658..e3f0e0a 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -21,6 +21,7 @@
import android.text.GetChars;
import android.text.GraphicsOperations;
import android.text.Layout.Alignment;
+import android.text.TextUtils.TruncateAt;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.SpannedString;
@@ -113,6 +114,104 @@
}
}
+ public void testBuilder() {
+ {
+ // Obtain.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ StaticLayout layout = builder.build();
+ // Check values passed to obtain().
+ assertEquals(LAYOUT_TEXT, layout.getText());
+ assertEquals(mDefaultPaint, layout.getPaint());
+ assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
+ // Check default values.
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ layout.getTextDirectionHeuristic());
+ assertEquals(Alignment.ALIGN_NORMAL, layout.getAlignment());
+ assertEquals(0.0f, layout.getSpacingAdd());
+ assertEquals(1.0f, layout.getSpacingMultiplier());
+ assertEquals(DEFAULT_OUTER_WIDTH, layout.getEllipsizedWidth());
+ }
+ {
+ // Obtain with null objects.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(null, 0, 0, null, 0);
+ try {
+ StaticLayout layout = builder.build();
+ fail("should throw NullPointerException here");
+ } catch (NullPointerException e) {
+ }
+ }
+ {
+ // setText.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setText(LAYOUT_TEXT_SINGLE_LINE);
+ StaticLayout layout = builder.build();
+ assertEquals(LAYOUT_TEXT_SINGLE_LINE, layout.getText());
+ }
+ {
+ // setAlignment.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setAlignment(DEFAULT_ALIGN);
+ StaticLayout layout = builder.build();
+ assertEquals(DEFAULT_ALIGN, layout.getAlignment());
+ }
+ {
+ // setTextDirection.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setTextDirection(TextDirectionHeuristics.RTL);
+ StaticLayout layout = builder.build();
+ // Always returns TextDirectionHeuristics.FIRSTSTRONG_LTR.
+ assertEquals(TextDirectionHeuristics.FIRSTSTRONG_LTR,
+ layout.getTextDirectionHeuristic());
+ }
+ {
+ // setLineSpacing.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setLineSpacing(1.0f, 2.0f);
+ StaticLayout layout = builder.build();
+ assertEquals(1.0f, layout.getSpacingAdd());
+ assertEquals(2.0f, layout.getSpacingMultiplier());
+ }
+ {
+ // setEllipsizedWidth and setEllipsize.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setEllipsize(TruncateAt.END);
+ builder.setEllipsizedWidth(ELLIPSIZE_WIDTH);
+ StaticLayout layout = builder.build();
+ assertEquals(ELLIPSIZE_WIDTH, layout.getEllipsizedWidth());
+ assertEquals(DEFAULT_OUTER_WIDTH, layout.getWidth());
+ assertTrue(layout.getEllipsisCount(0) == 0);
+ assertTrue(layout.getEllipsisCount(5) > 0);
+ }
+ {
+ // setMaxLines.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setMaxLines(1);
+ builder.setEllipsize(TruncateAt.END);
+ StaticLayout layout = builder.build();
+ assertTrue(layout.getEllipsisCount(0) > 0);
+ assertEquals(1, layout.getLineCount());
+ }
+ {
+ // Setter methods that cannot be directly tested.
+ // setBreakStrategy, setHyphenationFrequency, setIncludePad, and setIndents.
+ StaticLayout.Builder builder = StaticLayout.Builder.obtain(LAYOUT_TEXT, 0,
+ LAYOUT_TEXT.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
+ builder.setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY);
+ builder.setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_FULL);
+ builder.setIncludePad(true);
+ builder.setIndents(null, null);
+ StaticLayout layout = builder.build();
+ assertNotNull(layout);
+ }
+ }
+
/*
* Get the line number corresponding to the specified vertical position.
* If you ask for a position above 0, you get 0. above 0 means pixel above the fire line
diff --git a/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk b/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
index 60a0ba6..3827754 100644
--- a/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
+++ b/tests/uiautomator/test-apps/CtsUiAutomatorApp/Android.mk
@@ -24,7 +24,7 @@
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := CtsUiAutomatorApp
-LOCAL_JAVA_LIBRARIES = android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4
LOCAL_PROGUARD_ENABLED := disabled