am 5163f537: Merge "Set CTS Version to 4.0_r1" into ics-mr0
* commit '5163f53730f5f49fc5c8007d1653eecb9720be82':
Set CTS Version to 4.0_r1
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1da4cb9..5dc71f5 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -20,11 +20,13 @@
android:versionCode="1"
android:versionName="1337">
- <uses-sdk android:minSdkVersion="5"></uses-sdk>
+ <!-- Using 10 for more complete NFC support... -->
+ <uses-sdk android:minSdkVersion="10"></uses-sdk>
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.CAMERA" />
+ <uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
@@ -229,6 +231,29 @@
<meta-data android:name="test_category" android:value="@string/test_category_features" />
</activity>
+ <activity android:name=".nfc.NfcTestActivity"
+ android:label="@string/nfc_test"
+ android:configChanges="keyboardHidden|orientation">
+ <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_hardware" />
+ <meta-data android:name="test_required_features" android:value="android.hardware.nfc" />
+ </activity>
+
+ <activity android:name=".nfc.NdefPushSenderActivity"
+ android:label="@string/nfc_ndef_push_sender"
+ android:configChanges="keyboardHidden|orientation" />
+
+ <activity android:name=".nfc.NdefPushReceiverActivity"
+ android:label="@string/nfc_ndef_push_receiver"
+ android:configChanges="keyboardHidden|orientation" />
+
+ <activity android:name=".nfc.TagVerifierActivity"
+ android:label="@string/nfc_tag_verifier"
+ android:configChanges="keyboardHidden|orientation" />
+
<activity android:name=".sensors.AccelerometerTestActivity" android:label="@string/snsr_accel_test"
android:screenOrientation="nosensor">
<intent-filter>
diff --git a/apps/CtsVerifier/res/layout/nfc_tag.xml b/apps/CtsVerifier/res/layout/nfc_tag.xml
new file mode 100644
index 0000000..64da622
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/nfc_tag.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <ListView android:id="@id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ />
+
+ <TextView android:id="@id/android:empty"
+ android:gravity="center"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ style="@style/InstructionsFont"
+ />
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/pass_fail_text.xml b/apps/CtsVerifier/res/layout/pass_fail_text.xml
new file mode 100644
index 0000000..432f26d
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pass_fail_text.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <ScrollView android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ >
+ <TextView android:id="@+id/text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/InstructionsFont"
+ />
+ </ScrollView>
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 9770ce0..6e7d399 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -191,6 +191,67 @@
<string name="empty"></string>
+ <!-- Strings for NfcTestActivity -->
+ <string name="nfc_test">NFC Test</string>
+ <string name="nfc_test_info">The Peer-to-Peer Data Exchange tests require two devices with
+ NFC enabled to exchange messages. One device must be the candidate device running the
+ software build to be tested, while the other device must be an implementation already
+ known to be compatible.\n\nThe Tag Verification tests check that your
+ device can properly read and write to tags of different technologies. The MIFARE
+ Ultralight test is only applicable for devices that support it.
+ </string>
+
+ <string name="nfc_not_enabled">NFC is not enabled!</string>
+ <string name="nfc_not_enabled_message">These tests require NFC to be enabled. Click the
+ button below to goto Settings and enable it.</string>
+ <string name="nfc_settings">NFC Settings</string>
+
+ <string name="nfc_pee_2_pee">Peer-to-Peer Data Exchange</string>
+ <string name="nfc_ndef_push_sender">NDEF Push Sender</string>
+ <string name="nfc_ndef_push_receiver">NDEF Push Receiver</string>
+
+ <string name="nfc_tag_verification">Tag Verification</string>
+ <string name="nfc_ndef">NDEF</string>
+ <string name="nfc_mifare_ultralight">MIFARE Ultralight</string>
+
+ <string name="nfc_ndef_push_sender_info">Start the \"CTS Verifier NDEF Receiver\" test on
+ another device and touch the devices back to back. The receiver should show a
+ dialog indicating it has successfully received the correct message!</string>
+ <string name="nfc_ndef_push_sender_instructions">Touch this device to the back of another
+ device running the \"CTS Verifier NDEF Receiver\"...</string>
+
+ <string name="nfc_ndef_push_receiver_info">Start the \"CTS Verifier NDEF Sender\" test on
+ another device and touch the devices back to back. The receiver should show a
+ dialog indicating it has successfully received the correct message!</string>
+ <string name="nfc_ndef_push_receiver_instructions">Touch this device to the back of another
+ device running the \"CTS Verifier NDEF Sender\"...</string>
+ <string name="nfc_ndef_push_receive_success">Successfully received the correct NDEF push
+ message.</string>
+ <string name="nfc_ndef_push_receive_failure">Failed to receive the correct NDEF push
+ message.</string>
+
+ <string name="nfc_tag_verifier">NFC Tag Verifier</string>
+ <string name="nfc_tag_verifier_info">Follow the on-screen instructions to write and read
+ a tag of the chosen technology.</string>
+
+ <string name="nfc_scan_tag">Place device on a writable %s tag...</string>
+ <string name="nfc_write_tag_title">Writable tag discovered!</string>
+ <string name="nfc_write_tag_message">Press OK to write to this tag...</string>
+ <string name="nfc_scan_tag_again">Tap the same %s tag again to confirm that its contents match...</string>
+ <string name="nfc_wrong_tag_title">Wrong type of tag scanned</string>
+ <string name="nfc_no_tech">No tag technologies detected...</string>
+
+ <string name="nfc_writing_tag">Writing NFC tag...</string>
+ <string name="nfc_writing_tag_error">Error writing NFC tag...</string>
+ <string name="nfc_reading_tag">Reading NFC tag...</string>
+ <string name="nfc_reading_tag_error">Error reading NFC tag...</string>
+
+ <string name="nfc_result_success">Test passed!</string>
+ <string name="nfc_result_failure">Test failed!</string>
+
+ <string name="nfc_result_message">Written data:\n%1$s\n\nRead data:\n%2$s</string>
+ <string name="nfc_ndef_content">Id: %1$s\nMime: %2$s\nPayload: %3$s</string>
+
<!-- Strings for AccelerometerTestActivity and GyroscopeTestActivity -->
<string name="snsr_accel_test">Accelerometer Test</string>
<string name="snsr_accel_test_info">This test verifies that the accelerometer is working properly. As you move the device around through space, the triangle should always point down (i.e. in the direction of gravity.) If it does not, the accelerometer is improperly configured.</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
index 52d9b32..2cc79fb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java
@@ -83,11 +83,20 @@
/** Features necessary to run this test. */
final String[] requiredFeatures;
+ public static TestListItem newTest(Context context, int titleResId, String testName,
+ Intent intent, String[] requiredFeatures) {
+ return newTest(context.getString(titleResId), testName, intent, requiredFeatures);
+ }
+
public static TestListItem newTest(String title, String testName, Intent intent,
String[] requiredFeatures) {
return new TestListItem(title, testName, intent, requiredFeatures);
}
+ public static TestListItem newCategory(Context context, int titleResId) {
+ return newCategory(context.getString(titleResId));
+ }
+
public static TestListItem newCategory(String title) {
return new TestListItem(title, null, null, null);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
new file mode 100644
index 0000000..f976a45
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushReceiverActivity.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011 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.nfc;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.tech.NfcUtils;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.nfc.NdefMessage;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.widget.TextView;
+
+/**
+ * Test activity that waits to receive a particular NDEF Push message from another NFC device.
+ */
+public class NdefPushReceiverActivity extends PassFailButtons.Activity {
+
+ private static final int NFC_NOT_ENABLED_DIALOG_ID = 1;
+
+ private static final int RESULT_DIALOG_ID = 2;
+
+ private static final String IS_MATCH_ARG = "isMatch";
+
+ private NfcAdapter mNfcAdapter;
+
+ private PendingIntent mPendingIntent;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_text);
+ setInfoResources(R.string.nfc_ndef_push_receiver, R.string.nfc_ndef_push_receiver_info, 0);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ TextView text = (TextView) findViewById(R.id.text);
+ text.setText(R.string.nfc_ndef_push_receiver_instructions);
+
+ NfcManager nfcManager = (NfcManager) getSystemService(NFC_SERVICE);
+ mNfcAdapter = nfcManager.getDefaultAdapter();
+ mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
+ .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
+
+ if (!mNfcAdapter.isEnabled()) {
+ showDialog(NFC_NOT_ENABLED_DIALOG_ID);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mNfcAdapter.disableForegroundDispatch(this);
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ NdefMessage[] messages = getNdefMessages(intent);
+ boolean isMatch = messages != null
+ && messages.length > 0
+ && NfcUtils.areMessagesEqual(messages[0], NdefPushSenderActivity.TEST_MESSAGE);
+
+ getPassButton().setEnabled(isMatch);
+
+ Bundle args = new Bundle();
+ args.putBoolean(IS_MATCH_ARG, isMatch);
+ showDialog(RESULT_DIALOG_ID, args);
+ }
+
+ private NdefMessage[] getNdefMessages(Intent intent) {
+ Parcelable[] rawMessages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+ if (rawMessages != null) {
+ NdefMessage[] messages = new NdefMessage[rawMessages.length];
+ for (int i = 0; i < messages.length; i++) {
+ messages[i] = (NdefMessage) rawMessages[i];
+ }
+ return messages;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(int id, Bundle args) {
+ switch (id) {
+ case NFC_NOT_ENABLED_DIALOG_ID:
+ return NfcDialogs.createNotEnabledDialog(this);
+
+ case RESULT_DIALOG_ID:
+ // Set placeholder titles and messages for now. Final titles and messages will
+ // be set in onPrepareDialog.
+ return new AlertDialog.Builder(this)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(R.string.nfc_result_failure)
+ .setMessage("")
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+
+ default:
+ return super.onCreateDialog(id, args);
+ }
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+ switch (id) {
+ case RESULT_DIALOG_ID:
+ boolean isMatch = args.getBoolean(IS_MATCH_ARG);
+ AlertDialog alert = (AlertDialog) dialog;
+ alert.setTitle(isMatch
+ ? R.string.nfc_result_success
+ : R.string.nfc_result_failure);
+ alert.setMessage(isMatch
+ ? getString(R.string.nfc_ndef_push_receive_success)
+ : getString(R.string.nfc_ndef_push_receive_failure));
+ break;
+
+ default:
+ super.onPrepareDialog(id, dialog, args);
+ break;
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushSenderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushSenderActivity.java
new file mode 100644
index 0000000..2a507d3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NdefPushSenderActivity.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 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.nfc;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.Dialog;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import java.nio.charset.Charset;
+
+/**
+ * Test activity that sends a particular NDEF Push message to another NFC device.
+ */
+public class NdefPushSenderActivity extends PassFailButtons.Activity {
+
+ static final NdefMessage TEST_MESSAGE = getTestMessage();
+
+ private static final int NFC_NOT_ENABLED_DIALOG_ID = 1;
+
+ private NfcAdapter mNfcAdapter;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_text);
+ setInfoResources(R.string.nfc_ndef_push_sender, R.string.nfc_ndef_push_sender_info, 0);
+ setPassFailButtonClickListeners();
+
+ TextView text = (TextView) findViewById(R.id.text);
+ text.setText(R.string.nfc_ndef_push_sender_instructions);
+
+ NfcManager nfcManager = (NfcManager) getSystemService(NFC_SERVICE);
+ mNfcAdapter = nfcManager.getDefaultAdapter();
+
+ if (!mNfcAdapter.isEnabled()) {
+ showDialog(NFC_NOT_ENABLED_DIALOG_ID);
+ }
+ }
+
+ private static NdefMessage getTestMessage() {
+ byte[] mimeBytes = "application/com.android.cts.verifier.nfc"
+ .getBytes(Charset.forName("US-ASCII"));
+ byte[] id = new byte[] {1, 3, 3, 7};
+ byte[] payload = "CTS Verifier NDEF Push Tag".getBytes(Charset.forName("US-ASCII"));
+ return new NdefMessage(new NdefRecord[] {
+ new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, id, payload)
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mNfcAdapter.enableForegroundNdefPush(this, TEST_MESSAGE);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mNfcAdapter.disableForegroundNdefPush(this);
+ }
+
+ @Override
+ public Dialog onCreateDialog(int id, Bundle args) {
+ switch (id) {
+ case NFC_NOT_ENABLED_DIALOG_ID:
+ return NfcDialogs.createNotEnabledDialog(this);
+
+ default:
+ return super.onCreateDialog(id, args);
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
new file mode 100644
index 0000000..1130f2d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcDialogs.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2011 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.nfc;
+
+import com.android.cts.verifier.R;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.provider.Settings;
+
+/** Class containing methods to create common dialogs for NFC activities. */
+class NfcDialogs {
+
+ static AlertDialog createNotEnabledDialog(final Context context) {
+ return new AlertDialog.Builder(context)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.nfc_not_enabled)
+ .setMessage(R.string.nfc_not_enabled_message)
+ .setPositiveButton(R.string.nfc_settings, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+ context.startActivity(intent);
+ }
+ })
+ .create();
+ }
+
+ private NfcDialogs() {
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
new file mode 100644
index 0000000..3a85860
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 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.nfc;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+import android.content.Intent;
+import android.nfc.tech.MifareUltralight;
+import android.nfc.tech.Ndef;
+import android.nfc.tech.TagTechnology;
+import android.os.Bundle;
+
+/** Activity that lists all the NFC tests. */
+public class NfcTestActivity extends PassFailButtons.TestListActivity {
+
+ private static final String NDEF_ID =
+ TagVerifierActivity.getTagTestId(Ndef.class);
+
+ private static final String MIFARE_ULTRALIGHT_ID =
+ TagVerifierActivity.getTagTestId(MifareUltralight.class);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pass_fail_list);
+ setInfoResources(R.string.nfc_test, R.string.nfc_test_info, 0);
+ setPassFailButtonClickListeners();
+
+ ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+ adapter.add(TestListItem.newCategory(this, R.string.nfc_pee_2_pee));
+ adapter.add(TestListItem.newTest(this, R.string.nfc_ndef_push_sender,
+ NdefPushSenderActivity.class.getName(),
+ new Intent(this, NdefPushSenderActivity.class), null));
+ adapter.add(TestListItem.newTest(this, R.string.nfc_ndef_push_receiver,
+ NdefPushReceiverActivity.class.getName(),
+ new Intent(this, NdefPushReceiverActivity.class), null));
+
+ adapter.add(TestListItem.newCategory(this, R.string.nfc_tag_verification));
+ adapter.add(TestListItem.newTest(this, R.string.nfc_ndef,
+ NDEF_ID, getTagIntent(Ndef.class), null));
+ adapter.add(TestListItem.newTest(this, R.string.nfc_mifare_ultralight,
+ MIFARE_ULTRALIGHT_ID, getTagIntent(MifareUltralight.class), null));
+
+ setTestListAdapter(adapter);
+ }
+
+ private Intent getTagIntent(Class<? extends TagTechnology> primaryTech) {
+ return new Intent(this, TagVerifierActivity.class)
+ .putExtra(TagVerifierActivity.EXTRA_TECH, primaryTech.getName());
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
new file mode 100644
index 0000000..082c143
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/TagVerifierActivity.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2011 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.nfc;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.nfc.tech.MifareUltralightTagTester;
+import com.android.cts.verifier.nfc.tech.NdefTagTester;
+import com.android.cts.verifier.nfc.tech.TagTester;
+import com.android.cts.verifier.nfc.tech.TagVerifier;
+import com.android.cts.verifier.nfc.tech.TagVerifier.Result;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.app.ProgressDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcManager;
+import android.nfc.Tag;
+import android.nfc.tech.MifareUltralight;
+import android.nfc.tech.Ndef;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Test activity for reading and writing NFC tags using different technologies.
+ * First, it asks the user to write some random data to the tag. Then it asks
+ * the user to scan that tag again to verify that the data was properly written
+ * and read back.
+ */
+public class TagVerifierActivity<T> extends PassFailButtons.ListActivity {
+
+ static final String TAG = TagVerifierActivity.class.getSimpleName();
+
+ /** Non-optional argument specifying the tag technology to be used to read and write tags. */
+ static final String EXTRA_TECH = "tech";
+
+ private static final int NFC_NOT_ENABLED_DIALOG_ID = 1;
+ private static final int TESTABLE_TAG_DISCOVERED_DIALOG_ID = 2;
+ private static final int TESTABLE_TAG_REMINDER_DIALOG_ID = 3;
+ private static final int WRITE_PROGRESS_DIALOG_ID = 4;
+ private static final int READ_PROGRESS_DIALOG_ID = 5;
+ private static final int VERIFY_RESULT_DIALOG_ID = 6;
+
+ // Arguments used for the dialog showing what was written to the tag and read from the tag.
+ private static final String EXPECTED_CONTENT_ID = "expectedContent";
+ private static final String ACTUAL_CONTENT_ID = "actualContent";
+ private static final String IS_MATCH_ID = "isMatch";
+
+ // The test activity has two states - writing data to a tag and then verifying it.
+ private static final int WRITE_STEP = 0;
+ private static final int VERIFY_STEP = 1;
+
+ private NfcAdapter mNfcAdapter;
+ private PendingIntent mPendingIntent;
+ private Class<?> mTechClass;
+
+ private int mStep;
+ private TagTester mTagTester;
+ private TagVerifier mTagVerifier;
+ private Tag mTag;
+ private ArrayAdapter<String> mTechListAdapter;
+ private TextView mEmptyText;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.nfc_tag);
+ setInfoResources(R.string.nfc_tag_verifier, R.string.nfc_tag_verifier_info, 0);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+
+ parseIntentExtras();
+ if (mTechClass != null) {
+ mTagTester = getTagTester(mTechClass);
+
+ mEmptyText = (TextView) findViewById(android.R.id.empty);
+
+ mTechListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
+ setListAdapter(mTechListAdapter);
+
+ NfcManager nfcManager = (NfcManager) getSystemService(NFC_SERVICE);
+ mNfcAdapter = nfcManager.getDefaultAdapter();
+ mPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass())
+ .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
+
+ if (!mNfcAdapter.isEnabled()) {
+ showDialog(NFC_NOT_ENABLED_DIALOG_ID);
+ }
+
+ goToWriteStep();
+ } else {
+ finish();
+ }
+ }
+
+ private void parseIntentExtras() {
+ try {
+ String tech = getIntent().getStringExtra(EXTRA_TECH);
+ if (tech != null) {
+ mTechClass = Class.forName(tech);
+ }
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Couldn't find tech for class", e);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mNfcAdapter.disableForegroundDispatch(this);
+ }
+
+ private TagTester getTagTester(Class<?> techClass) {
+ if (Ndef.class.equals(techClass)) {
+ return new NdefTagTester(this);
+ } else if (MifareUltralight.class.equals(techClass)) {
+ return new MifareUltralightTagTester();
+ } else {
+ throw new IllegalArgumentException("Unsupported technology: " + techClass);
+ }
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+ if (tag != null) {
+ mTag = tag;
+ updateTechListAdapter(tag);
+ switch (mStep) {
+ case WRITE_STEP:
+ handleWriteStep(tag);
+ break;
+
+ case VERIFY_STEP:
+ handleVerifyStep();
+ break;
+ }
+ }
+ }
+
+ private void handleWriteStep(Tag tag) {
+ if (mTagTester.isTestableTag(tag)) {
+ brutallyDismissDialog(TESTABLE_TAG_REMINDER_DIALOG_ID);
+ showDialog(TESTABLE_TAG_DISCOVERED_DIALOG_ID);
+ } else {
+ brutallyDismissDialog(TESTABLE_TAG_DISCOVERED_DIALOG_ID);
+ showDialog(TESTABLE_TAG_REMINDER_DIALOG_ID);
+ }
+ }
+
+ private void brutallyDismissDialog(int id) {
+ try {
+ dismissDialog(id);
+ } catch (IllegalArgumentException e) {
+ // Don't care if it hasn't been shown before...
+ }
+ }
+
+ private void handleVerifyStep() {
+ new VerifyTagTask().execute(mTag);
+ }
+
+ private void updateTechListAdapter(Tag tag) {
+ mEmptyText.setText(R.string.nfc_no_tech);
+ String[] techList = tag.getTechList();
+ mTechListAdapter.clear();
+ for (String tech : techList) {
+ mTechListAdapter.add(tech);
+ }
+ }
+
+ class WriteTagTask extends AsyncTask<Tag, Void, TagVerifier> {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ showDialog(WRITE_PROGRESS_DIALOG_ID);
+ }
+
+ @Override
+ protected TagVerifier doInBackground(Tag... tags) {
+ try {
+ return mTagTester.writeTag(tags[0]);
+ } catch (Exception e) {
+ Log.e(TAG, "Error writing NFC tag...", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(TagVerifier tagVerifier) {
+ dismissDialog(WRITE_PROGRESS_DIALOG_ID);
+ mTagVerifier = tagVerifier;
+ if (tagVerifier != null) {
+ goToVerifyStep();
+ } else {
+ Toast.makeText(TagVerifierActivity.this, R.string.nfc_writing_tag_error,
+ Toast.LENGTH_SHORT).show();
+ goToWriteStep();
+ }
+ }
+ }
+
+ private void goToWriteStep() {
+ mStep = WRITE_STEP;
+ mEmptyText.setText(getString(R.string.nfc_scan_tag, mTechClass.getSimpleName()));
+ mTechListAdapter.clear();
+ }
+
+ private void goToVerifyStep() {
+ mStep = VERIFY_STEP;
+ mEmptyText.setText(getString(R.string.nfc_scan_tag_again, mTechClass.getSimpleName()));
+ mTechListAdapter.clear();
+ }
+
+ class VerifyTagTask extends AsyncTask<Tag, Void, Result> {
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ showDialog(READ_PROGRESS_DIALOG_ID);
+ }
+
+ @Override
+ protected Result doInBackground(Tag... tags) {
+ try {
+ return mTagVerifier.verifyTag(tags[0]);
+ } catch (Exception e) {
+ Log.e(TAG, "Error verifying NFC tag...", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Result result) {
+ super.onPostExecute(result);
+ dismissDialog(READ_PROGRESS_DIALOG_ID);
+ mTagVerifier = null;
+ if (result != null) {
+ getPassButton().setEnabled(result.isMatch());
+
+ Bundle args = new Bundle();
+ args.putCharSequence(EXPECTED_CONTENT_ID, result.getExpectedContent());
+ args.putCharSequence(ACTUAL_CONTENT_ID, result.getActualContent());
+ args.putBoolean(IS_MATCH_ID, result.isMatch());
+ showDialog(VERIFY_RESULT_DIALOG_ID, args);
+
+ goToWriteStep();
+ } else {
+ Toast.makeText(TagVerifierActivity.this, R.string.nfc_reading_tag_error,
+ Toast.LENGTH_SHORT).show();
+ goToWriteStep();
+ }
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(int id, Bundle args) {
+ switch (id) {
+ case NFC_NOT_ENABLED_DIALOG_ID:
+ return NfcDialogs.createNotEnabledDialog(this);
+
+ case TESTABLE_TAG_DISCOVERED_DIALOG_ID:
+ return createTestableTagDiscoveredDialog();
+
+ case TESTABLE_TAG_REMINDER_DIALOG_ID:
+ return createTestableTagReminderDialog();
+
+ case WRITE_PROGRESS_DIALOG_ID:
+ return createWriteProgressDialog();
+
+ case READ_PROGRESS_DIALOG_ID:
+ return createReadProgressDialog();
+
+ case VERIFY_RESULT_DIALOG_ID:
+ return createVerifyResultDialog();
+
+ default:
+ return super.onCreateDialog(id, args);
+ }
+ }
+
+ private AlertDialog createTestableTagDiscoveredDialog() {
+ return new AlertDialog.Builder(this)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(R.string.nfc_write_tag_title)
+ .setMessage(R.string.nfc_write_tag_message)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ new WriteTagTask().execute(mTag);
+ }
+ })
+ .setOnCancelListener(new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ goToWriteStep();
+ }
+ })
+ .show();
+ }
+
+ private AlertDialog createTestableTagReminderDialog() {
+ return new AlertDialog.Builder(this)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.nfc_wrong_tag_title)
+ .setMessage(getString(R.string.nfc_scan_tag, mTechClass.getSimpleName()))
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ }
+
+ private ProgressDialog createWriteProgressDialog() {
+ ProgressDialog dialog = new ProgressDialog(this);
+ dialog.setMessage(getString(R.string.nfc_writing_tag));
+ return dialog;
+ }
+
+ private ProgressDialog createReadProgressDialog() {
+ ProgressDialog dialog = new ProgressDialog(this);
+ dialog.setMessage(getString(R.string.nfc_reading_tag));
+ return dialog;
+ }
+
+ private AlertDialog createVerifyResultDialog() {
+ // Placeholder title and message that will be set properly in onPrepareDialog
+ return new AlertDialog.Builder(this)
+ .setIcon(android.R.drawable.ic_dialog_alert)
+ .setTitle(R.string.nfc_result_failure)
+ .setMessage("")
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
+ }
+
+ @Override
+ protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
+ switch (id) {
+ case VERIFY_RESULT_DIALOG_ID:
+ prepareVerifyResultDialog(dialog, args);
+ break;
+
+ default:
+ super.onPrepareDialog(id, dialog, args);
+ break;
+ }
+ }
+
+ private void prepareVerifyResultDialog(Dialog dialog, Bundle args) {
+ CharSequence expectedContent = args.getCharSequence(EXPECTED_CONTENT_ID);
+ CharSequence actualContent = args.getCharSequence(ACTUAL_CONTENT_ID);
+ boolean isMatch = args.getBoolean(IS_MATCH_ID);
+
+ AlertDialog alert = (AlertDialog) dialog;
+ alert.setTitle(isMatch
+ ? R.string.nfc_result_success
+ : R.string.nfc_result_failure);
+ alert.setMessage(getString(R.string.nfc_result_message, expectedContent, actualContent));
+ }
+
+ @Override
+ public String getTestId() {
+ return getTagTestId(mTechClass);
+ }
+
+ static String getTagTestId(Class<?> primaryTech) {
+ return NfcTestActivity.class.getName() + "_" + primaryTech.getSimpleName();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/MifareUltralightTagTester.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/MifareUltralightTagTester.java
new file mode 100644
index 0000000..23f4762
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/MifareUltralightTagTester.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2011 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.nfc.tech;
+
+import android.nfc.Tag;
+import android.nfc.tech.MifareUltralight;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Random;
+
+/**
+ * {@link TagTester} for MIFARE Ultralight tags. It writes random bytes to the
+ * tag's first user page and verifies that it matches when scanned later.
+ */
+public class MifareUltralightTagTester implements TagTester {
+
+ private static final int USER_PAGE_OFFSET = 4;
+
+ private static final int NUM_PAGES = 4;
+
+ @Override
+ public boolean isTestableTag(Tag tag) {
+ if (tag != null) {
+ for (String tech : tag.getTechList()) {
+ if (tech.equals(MifareUltralight.class.getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public TagVerifier writeTag(Tag tag) throws IOException {
+ Random random = new Random();
+ MifareUltralight ultralight = MifareUltralight.get(tag);
+ ultralight.connect();
+
+ final byte[] fourPages = new byte[NUM_PAGES * MifareUltralight.PAGE_SIZE];
+ byte[] onePage = new byte[MifareUltralight.PAGE_SIZE];
+ for (int i = 0; i < NUM_PAGES; i++) {
+ random.nextBytes(onePage);
+ System.arraycopy(onePage, 0, fourPages, i * onePage.length, onePage.length);
+ ultralight.writePage(USER_PAGE_OFFSET + i, onePage);
+ }
+
+ final CharSequence expectedContent = NfcUtils.displayByteArray(fourPages);
+
+ return new TagVerifier() {
+ @Override
+ public Result verifyTag(Tag tag) throws IOException {
+ MifareUltralight ultralight = MifareUltralight.get(tag);
+ if (ultralight != null) {
+ ultralight.connect();
+ byte[] actualFourPages = ultralight.readPages(USER_PAGE_OFFSET);
+ CharSequence actualContent = NfcUtils.displayByteArray(actualFourPages);
+ return new Result(expectedContent, actualContent,
+ Arrays.equals(fourPages, actualFourPages));
+ } else {
+ return new Result(expectedContent, null, false);
+ }
+ }
+ };
+ }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/NdefTagTester.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/NdefTagTester.java
new file mode 100644
index 0000000..668e526
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/NdefTagTester.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 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.nfc.tech;
+
+import com.android.cts.verifier.R;
+
+import android.content.Context;
+import android.nfc.FormatException;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.Tag;
+import android.nfc.tech.Ndef;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Random;
+
+/**
+ * {@link TagTester} for NDEF tags. It writes a semi-random NDEF tag with a random id but
+ * constant mime type and payload.
+ */
+public class NdefTagTester implements TagTester {
+
+ private static final String TAG = NdefTagTester.class.getSimpleName();
+
+ private static final String MIME_TYPE = "application/com.android.cts.verifier.nfc";
+
+ private static final String PAYLOAD = "CTS Verifier NDEF Tag";
+
+ private final Context mContext;
+
+ public NdefTagTester(Context context) {
+ this.mContext = context;
+ }
+
+ @Override
+ public boolean isTestableTag(Tag tag) {
+ if (tag != null) {
+ for (String tech : tag.getTechList()) {
+ if (tech.equals(Ndef.class.getName())) {
+ Ndef ndef = Ndef.get(tag);
+ return ndef != null && ndef.isWritable();
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public TagVerifier writeTag(Tag tag) throws IOException, FormatException {
+ Random random = new Random();
+ NdefRecord mimeRecord = createRandomMimeRecord(random);
+ NdefRecord[] expectedRecords = new NdefRecord[] {mimeRecord};
+
+ final NdefMessage expectedMessage = new NdefMessage(expectedRecords);
+ writeMessage(tag, expectedMessage);
+
+ final String expectedContent = mContext.getString(R.string.nfc_ndef_content,
+ NfcUtils.displayByteArray(mimeRecord.getId()), MIME_TYPE, PAYLOAD);
+
+ return new TagVerifier() {
+ @Override
+ public Result verifyTag(Tag tag) throws IOException, FormatException {
+ String actualContent;
+ NdefMessage message = readMessage(tag);
+ NdefRecord[] records = message.getRecords();
+
+ if (records.length > 0) {
+ NdefRecord record = records[0];
+ actualContent = mContext.getString(R.string.nfc_ndef_content,
+ NfcUtils.displayByteArray(record.getId()),
+ new String(record.getType(), Charset.forName("US-ASCII")),
+ new String(record.getPayload(), Charset.forName("US-ASCII")));
+ } else {
+ actualContent = null;
+ }
+
+ return new Result(expectedContent, actualContent,
+ NfcUtils.areMessagesEqual(message, expectedMessage));
+ }
+ };
+ }
+
+ private NdefRecord createRandomMimeRecord(Random random) {
+ byte[] mimeBytes = MIME_TYPE.getBytes(Charset.forName("US-ASCII"));
+ byte[] id = new byte[4];
+ random.nextBytes(id);
+ byte[] payload = PAYLOAD.getBytes(Charset.forName("US-ASCII"));
+ return new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, id, payload);
+ }
+
+ private void writeMessage(Tag tag, NdefMessage message) throws IOException, FormatException {
+ Ndef ndef = null;
+ try {
+ ndef = Ndef.get(tag);
+ ndef.connect();
+ ndef.writeNdefMessage(message);
+ } finally {
+ if (ndef != null) {
+ try {
+ ndef.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IOException while closing NDEF...", e);
+ }
+ }
+ }
+ }
+
+ private NdefMessage readMessage(Tag tag) throws IOException, FormatException {
+ Ndef ndef = null;
+ try {
+ ndef = Ndef.get(tag);
+ if (ndef != null) {
+ ndef.connect();
+ return ndef.getNdefMessage();
+ }
+ } finally {
+ if (ndef != null) {
+ try {
+ ndef.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing Ndef...", e);
+ }
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/NfcUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/NfcUtils.java
new file mode 100644
index 0000000..80e3989
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/NfcUtils.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2011 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.nfc.tech;
+
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+
+import java.util.Arrays;
+
+/** Class with utility methods for testing equality of messages and displaying byte payloads. */
+public class NfcUtils {
+
+ public static boolean areMessagesEqual(NdefMessage message, NdefMessage otherMessage) {
+ return message != null && otherMessage != null
+ && areRecordArraysEqual(message.getRecords(), otherMessage.getRecords());
+ }
+
+ private static boolean areRecordArraysEqual(NdefRecord[] records, NdefRecord[] otherRecords) {
+ if (records.length == otherRecords.length) {
+ for (int i = 0; i < records.length; i++) {
+ if (!areRecordsEqual(records[i], otherRecords[i])) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private static boolean areRecordsEqual(NdefRecord record, NdefRecord otherRecord) {
+ return Arrays.equals(record.toByteArray(), otherRecord.toByteArray());
+ }
+
+ static CharSequence displayByteArray(byte[] bytes) {
+ StringBuilder builder = new StringBuilder().append("[");
+ for (int i = 0; i < bytes.length; i++) {
+ builder.append(Byte.toString(bytes[i]));
+ if (i + 1 < bytes.length) {
+ builder.append(", ");
+ }
+ }
+ return builder.append("]");
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/TagTester.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/TagTester.java
new file mode 100644
index 0000000..d0dd67c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/TagTester.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2011 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.nfc.tech;
+
+import android.nfc.Tag;
+
+/** Tag tester that writes data to the tag and returns a way to confirm a successful write. */
+public interface TagTester {
+
+ /** @return true if the tag is testable by this {@link TagTester} */
+ boolean isTestableTag(Tag tag);
+
+ /** Writes some data to the tag and returns a {@link TagVerifier} to confirm it. */
+ TagVerifier writeTag(Tag tag) throws Exception;
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/TagVerifier.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/TagVerifier.java
new file mode 100644
index 0000000..8c55610
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/tech/TagVerifier.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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.nfc.tech;
+
+import android.nfc.FormatException;
+import android.nfc.Tag;
+
+import java.io.IOException;
+
+/** Tag verifier for checking that the {@link Tag} has some expected value. */
+public interface TagVerifier {
+
+ /** @return true if the tag has the expected value */
+ Result verifyTag(Tag tag) throws FormatException, IOException;
+
+ /** Class with info necessary to show the user what was written and read from a tag. */
+ public static class Result {
+
+ private final CharSequence mExpectedContent;
+
+ private final CharSequence mActualContent;
+
+ private final boolean mIsMatch;
+
+ public Result(CharSequence expectedContent, CharSequence actualContent, boolean isMatch) {
+ this.mExpectedContent = expectedContent;
+ this.mActualContent = actualContent;
+ this.mIsMatch = isMatch;
+ }
+
+ /** @return {@link CharSequence} representation of the data written to the tag */
+ public CharSequence getExpectedContent() {
+ return mExpectedContent;
+ }
+
+ /** @return {@link CharSequence} representation of the data read back from the tag */
+ public CharSequence getActualContent() {
+ return mActualContent;
+ }
+
+ /** @return whether or not the expected content matched the actual content of the tag */
+ public boolean isMatch() {
+ return mIsMatch;
+ }
+ }
+}
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 9cdb13b..f2e9c1b 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -128,7 +128,8 @@
</activity>
<activity android:name="android.media.cts.MediaStubActivity"
- android:label="MediaStubActivity">
+ android:label="MediaStubActivity"
+ android:screenOrientation="nosensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
@@ -764,7 +765,8 @@
</activity>
<activity android:name="android.view.cts.GestureDetectorStubActivity"
- android:label="GestureDetectorStubActivity"/>
+ android:label="GestureDetectorStubActivity"
+ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
<!--Test for PackageManager-->
<activity android:name="android.content.pm.cts.TestPmActivity"
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
deleted file mode 100644
index 844e5b9..0000000
--- a/tests/expectations/knownfailures.txt
+++ /dev/null
@@ -1,89 +0,0 @@
-[
-{
- name: "android.graphics.drawable.cts.AnimationDrawableTest#testStart",
- bug: 5513533
-},
-{
- name: "android.hardware.cts.CameraGLTest#testSetPreviewTextureBothCallbacks",
- bug: 5534460
-},
-{
- name: "android.hardware.cts.CameraGLTest#testSetPreviewTexturePreviewCallback",
- bug: 5534170
-},
-{
- name: "android.hardware.cts.CameraTest#testVideoSnapshot",
- bug: 5492725
-},
-{
- name: "android.media.cts.MediaPlayerTest#testVideoSurfaceResetting",
- bug: 5382086
-},
-{
- names: [
- "android.media.cts.VisualizerTest#test2_0PollingCapture",
- "android.media.cts.VisualizerTest#test2_1ListenerCapture"
- ],
- bug: 5519606
-},
-{
- names: [
- "android.net.cts.ListeningPortsTest#testNoListeningTcp6Ports",
- "android.net.cts.ListeningPortsTest#testNoListeningUdp6Ports"
- ],
- bug: 5477538
-},
-{
- name: "android.telephony.cts.PhoneNumberFormattingTextWatcherTest#testPhoneNumberFormattingTextWatcher",
- bug: 3198578
-},
-{
- name: "android.text.cts.AndroidCharacterTest#testMirror",
- bug: 4371654
-},
-{
- names: [
- "android.theme.cts.ActivitySnapshotTests",
- "android.theme.cts.SplitActivitySnapshotTests",
- "android.theme.cts.ThemeTest"
- ],
- bug: 5404694
-},
-{
- name: "android.webkit.cts.WebViewTest#testRequestFocusNodeHref",
- bug: 3241968
-},
-{
- name: "android.webkit.cts.WebViewTest#testGetHitTestResult",
- bug: 5361502
-},
-{
- name: "android.widget.cts.ViewFlipperTest#testViewFlipper",
- bug: 5051682
-},
-{
- name: "libcore.java.net.URLConnectionTest#testServerClosesSocket",
- bug: 5534202
-},
-{
- name: "dalvik.system.DexClassLoaderTest#test_twoJar_callInstanceMethod",
- bug: 5534165
-},
-{
- description: "Test exceeds Tradefed's 10 minute timeout",
- name: "libcore.java.security.KeyPairGeneratorTest",
- bug: 5513723
-},
-{
- description: "Reevaluate these tests for the next release",
- names: [
- "android.content.cts.ContextWrapperTest#testAccessTheme",
- "android.provider.cts.Contacts_PeopleTest#testAddToGroup",
- "android.view.cts.GestureDetectorTest#testOnTouchEvent",
- "android.view.cts.ViewTest#testWindowVisibilityChanged",
- "android.widget.cts.AbsListViewTest#testGetContextMenuInfo",
- "android.webkit.cts.WebChromeClientTest#testOnReceivedIcon",
- "android.webkit.cts.WebViewTest#testRequestImageRef"
- ]
-}
-]
diff --git a/tests/src/android/media/cts/VideoSurfaceView.java b/tests/src/android/media/cts/VideoSurfaceView.java
index 0f23a2d..ae3da1a 100644
--- a/tests/src/android/media/cts/VideoSurfaceView.java
+++ b/tests/src/android/media/cts/VideoSurfaceView.java
@@ -37,7 +37,7 @@
class VideoSurfaceView extends GLSurfaceView {
private static final String TAG = "VideoSurfaceView";
- private static final int SLEEP_TIME = 1000;
+ private static final int SLEEP_TIME_MS = 1000;
VideoRender mRenderer;
private MediaPlayer mMediaPlayer = null;
@@ -62,26 +62,14 @@
}
public void startTest() throws Exception {
- Thread.sleep(SLEEP_TIME);
+ Thread.sleep(SLEEP_TIME_MS);
mMediaPlayer.start();
- Thread.sleep(SLEEP_TIME * 5);
+ Thread.sleep(SLEEP_TIME_MS * 5);
mMediaPlayer.setSurface(null);
- Thread.sleep(SLEEP_TIME * 1);
- SurfaceTexture surfaceTexture = new SurfaceTexture(0);
- Surface surface = new Surface(surfaceTexture);
- surface.release();
- try {
- mMediaPlayer.setSurface(surface);
- throw new RuntimeException("setSurface with released Surface object didn't throw " +
- "IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expected
- }
-
while (mMediaPlayer.isPlaying()) {
- Thread.sleep(SLEEP_TIME);
+ Thread.sleep(SLEEP_TIME_MS);
}
}
diff --git a/tests/src/android/renderscript/cts/RenderscriptGLStubActivity.java b/tests/src/android/renderscript/cts/RenderscriptGLStubActivity.java
index 3bb6212..d6d05ac 100644
--- a/tests/src/android/renderscript/cts/RenderscriptGLStubActivity.java
+++ b/tests/src/android/renderscript/cts/RenderscriptGLStubActivity.java
@@ -87,10 +87,15 @@
destroyRenderScriptGL();
}
}
+
+ public void forceDestroy() {
+ onDetachedFromWindow();
+ }
}
// Custom view to use with RenderScript
private HelloWorldView mView;
+ private HelloWorldView mView2;
@Override
public void onCreate(Bundle icicle) {
@@ -102,16 +107,28 @@
}
public void recreateView() {
+ HelloWorldView oldView = mView;
mView = new HelloWorldView(this);
setContentView(mView);
+ oldView.forceDestroy();
+ }
+
+ public void destroyAll() {
+ if (mView != null) {
+ mView.forceDestroy();
+ }
+ if (mView2 != null) {
+ mView2.forceDestroy();
+ }
}
public void recreateMultiView() {
- HelloWorldView view1 = new HelloWorldView(this);
- HelloWorldView view2 = new HelloWorldView(this);
- setContentView(view1);
- setContentView(view2);
- mView = view2;
+ HelloWorldView oldView = mView;
+ mView = new HelloWorldView(this);
+ mView2 = new HelloWorldView(this);
+ setContentView(mView);
+ setContentView(mView2);
+ oldView.forceDestroy();
}
@Override
diff --git a/tests/src/android/renderscript/cts/utils.rs b/tests/src/android/renderscript/cts/utils.rs
new file mode 100755
index 0000000..86e7997
--- /dev/null
+++ b/tests/src/android/renderscript/cts/utils.rs
@@ -0,0 +1,39 @@
+#include "shared.rsh"
+
+static bool test_color_pack_unpack() {
+ bool failed = false;
+ start();
+
+ for(uint i = 0; i < 256; i ++) {
+ uchar4 v = (uchar)i;
+ float4 f = rsUnpackColor8888(v);
+ uchar4 res = rsPackColorTo8888(f);
+ _RS_ASSERT(v.x == res.x);
+ _RS_ASSERT(v.y == res.y);
+ _RS_ASSERT(v.z == res.z);
+ _RS_ASSERT(v.w == res.w);
+ }
+
+ float time = end();
+
+ if (failed) {
+ rsDebug("test_color_pack_unpack FAILED", time);
+ }
+ else {
+ rsDebug("test_color_pack_unpack PASSED", time);
+ }
+
+ return failed;
+}
+void test() {
+ bool failed = false;
+ failed |= test_color_pack_unpack();
+
+ if (failed) {
+ rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+ }
+ else {
+ rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+ }
+}
+
diff --git a/tests/tests/database/src/android/database/cts/CursorWindowTest.java b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
index 1febfd0..9156d02 100644
--- a/tests/tests/database/src/android/database/cts/CursorWindowTest.java
+++ b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
@@ -16,7 +16,6 @@
package android.database.cts;
-import com.android.common.ArrayListCursor;
import com.google.android.collect.Lists;
import dalvik.annotation.TestLevel;
@@ -28,6 +27,7 @@
import android.database.AbstractCursor;
import android.database.CharArrayBuffer;
import android.database.CursorWindow;
+import android.database.MatrixCursor;
import android.database.sqlite.SQLiteException;
import android.os.Parcel;
import android.test.AndroidTestCase;
@@ -81,10 +81,13 @@
})
public void testWriteCursorToWindow() throws Exception {
// create cursor
- String[] colNames = new String[]{"name", "number", "profit"};
+ String[] colNames = new String[]{"_id", "name", "number", "profit"};
int colsize = colNames.length;
ArrayList<ArrayList> list = createTestList(10, colsize);
- AbstractCursor cursor = new ArrayListCursor(colNames, list);
+ MatrixCursor cursor = new MatrixCursor(colNames, list.size());
+ for (ArrayList row : list) {
+ cursor.addRow(row);
+ }
// fill window
CursorWindow window = new CursorWindow(false);
@@ -674,8 +677,7 @@
list.add(col);
for (int j = 0; j < cols; j++) {
// generate random number
- Integer r = generator.nextInt();
- col.add(r);
+ col.add(j == 0 ? i : generator.nextInt());
}
}
return list;
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index 9999d6c..e453820 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -23,6 +23,7 @@
import android.content.EntityIterator;
import android.database.Cursor;
import android.database.DatabaseUtils;
+import android.database.SQLException;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
@@ -30,6 +31,7 @@
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.CalendarEntity;
import android.provider.CalendarContract.Calendars;
+import android.provider.CalendarContract.Colors;
import android.provider.CalendarContract.Events;
import android.provider.CalendarContract.EventsEntity;
import android.provider.CalendarContract.ExtendedProperties;
@@ -39,6 +41,7 @@
import android.test.InstrumentationTestCase;
import android.test.InstrumentationCtsTestRunner;
import android.test.suitebuilder.annotation.*;
+import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.Log;
@@ -79,6 +82,7 @@
Calendars.NAME,
Calendars.CALENDAR_DISPLAY_NAME,
Calendars.CALENDAR_COLOR,
+ Calendars.CALENDAR_COLOR_KEY,
Calendars.CALENDAR_ACCESS_LEVEL,
Calendars.VISIBLE,
Calendars.SYNC_EVENTS,
@@ -89,6 +93,8 @@
Calendars.CAN_MODIFY_TIME_ZONE,
Calendars.MAX_REMINDERS,
Calendars.ALLOWED_REMINDERS,
+ Calendars.ALLOWED_AVAILABILITY,
+ Calendars.ALLOWED_ATTENDEE_TYPES,
Calendars.DELETED,
Calendars.CAL_SYNC1,
Calendars.CAL_SYNC2,
@@ -145,6 +151,8 @@
values.put(Calendars.CAN_MODIFY_TIME_ZONE, seed % 2);
values.put(Calendars.MAX_REMINDERS, 3);
values.put(Calendars.ALLOWED_REMINDERS, "0,1,2"); // does not include SMS (3)
+ values.put(Calendars.ALLOWED_ATTENDEE_TYPES, "0,1,2,3");
+ values.put(Calendars.ALLOWED_AVAILABILITY, "0,1,2,3");
values.put(Calendars.CAL_SYNC1, "SYNC1:" + seedString);
values.put(Calendars.CAL_SYNC2, "SYNC2:" + seedString);
values.put(Calendars.CAL_SYNC3, "SYNC3:" + seedString);
@@ -299,6 +307,8 @@
Events.DTEND,
Events.EVENT_TIMEZONE,
Events.EVENT_END_TIMEZONE,
+ Events.EVENT_COLOR,
+ Events.EVENT_COLOR_KEY,
Events.DURATION,
Events.ALL_DAY,
Events.ACCESS_LEVEL,
@@ -325,10 +335,11 @@
Events.SYNC_DATA5,
Events.DIRTY,
Events.SYNC_DATA8,
- Events.SYNC_DATA2, // Events.SYNC_DATA1
- // Events.SYNC_DATA2
- // Events.SYNC_DATA3
- // Events.SYNC_DATA4
+ Events.SYNC_DATA2,
+ Events.SYNC_DATA1,
+ Events.SYNC_DATA2,
+ Events.SYNC_DATA3,
+ Events.SYNC_DATA4,
};
// @formatter:on
@@ -355,6 +366,7 @@
values.put(Events.DTSTART, seed);
values.put(Events.DTEND, seed + DateUtils.HOUR_IN_MILLIS);
values.put(Events.EVENT_TIMEZONE, TIME_ZONES[seed % TIME_ZONES.length]);
+ values.put(Events.EVENT_COLOR, seed);
// values.put(Events.EVENT_TIMEZONE2, TIME_ZONES[(seed +1) %
// TIME_ZONES.length]);
if ((seed % 2) == 0) {
@@ -616,6 +628,115 @@
}
}
+ /**
+ * Helper class for manipulating entries in the Colors table.
+ */
+ private static class ColorHelper {
+ public static final String WHERE_COLOR_ACCOUNT = Colors.ACCOUNT_NAME + "=? AND "
+ + Colors.ACCOUNT_TYPE + "=?";
+ public static final String WHERE_COLOR_ACCOUNT_AND_INDEX = WHERE_COLOR_ACCOUNT + " AND "
+ + Colors.COLOR_KEY + "=?";
+
+ public static final String[] COLORS_PROJECTION = new String[] {
+ Colors._ID, // 0
+ Colors.ACCOUNT_NAME, // 1
+ Colors.ACCOUNT_TYPE, // 2
+ Colors.DATA, // 3
+ Colors.COLOR_TYPE, // 4
+ Colors.COLOR_KEY, // 5
+ Colors.COLOR, // 6
+ };
+ // indexes into projection
+ public static final int COLORS_ID_INDEX = 0;
+ public static final int COLORS_INDEX_INDEX = 5;
+ public static final int COLORS_COLOR_INDEX = 6;
+
+ public static final int[] DEFAULT_TYPES = new int[] {
+ Colors.TYPE_CALENDAR, Colors.TYPE_CALENDAR, Colors.TYPE_CALENDAR,
+ Colors.TYPE_CALENDAR, Colors.TYPE_EVENT, Colors.TYPE_EVENT, Colors.TYPE_EVENT,
+ Colors.TYPE_EVENT,
+ };
+ public static final int[] DEFAULT_COLORS = new int[] {
+ 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFFAA00AA, 0xFF00AAAA, 0xFF333333, 0xFFAAAA00,
+ 0xFFAAAAAA,
+ };
+ public static final String[] DEFAULT_INDICES = new String[] {
+ "000", "001", "010", "011", "100", "101", "110", "111",
+ };
+
+ public static final int C_COLOR_0 = 0;
+ public static final int C_COLOR_1 = 1;
+ public static final int C_COLOR_2 = 2;
+ public static final int C_COLOR_3 = 3;
+ public static final int E_COLOR_0 = 4;
+ public static final int E_COLOR_1 = 5;
+ public static final int E_COLOR_2 = 6;
+ public static final int E_COLOR_3 = 7;
+
+ // do not instantiate
+ private ColorHelper() {
+ }
+
+ /**
+ * Adds a new color to the colors table.
+ *
+ * @return the _id of the new color, or -1 on failure
+ */
+ public static long addColor(ContentResolver resolver, String accountName,
+ String accountType, String data, String index, int type, int color) {
+ Uri uri = asSyncAdapter(Colors.CONTENT_URI, accountName, accountType);
+
+ ContentValues colorValues = new ContentValues();
+ colorValues.put(Colors.DATA, data);
+ colorValues.put(Colors.COLOR_KEY, index);
+ colorValues.put(Colors.COLOR_TYPE, type);
+ colorValues.put(Colors.COLOR, color);
+ Uri result = resolver.insert(uri, colorValues);
+ return ContentUris.parseId(result);
+ }
+
+ /**
+ * Finds the color specified by an account name/type and a color index.
+ * The returned cursor will use {@link ColorHelper#COLORS_PROJECTION}.
+ */
+ public static Cursor findColorByIndex(ContentResolver resolver, String accountName,
+ String accountType, String index) {
+ return resolver.query(Colors.CONTENT_URI, COLORS_PROJECTION,
+ WHERE_COLOR_ACCOUNT_AND_INDEX,
+ new String[] {accountName, accountType, index}, null);
+ }
+
+ public static Cursor findColorsByAccount(ContentResolver resolver, String accountName,
+ String accountType) {
+ return resolver.query(Colors.CONTENT_URI, COLORS_PROJECTION, WHERE_COLOR_ACCOUNT,
+ new String[] { accountName, accountType }, null);
+ }
+
+ /**
+ * Adds a default set of test colors to the Colors table under the given
+ * account.
+ *
+ * @return true if the default colors were added successfully
+ */
+ public static boolean addDefaultColorsToAccount(ContentResolver resolver,
+ String accountName, String accountType) {
+ for (int i = 0; i < DEFAULT_INDICES.length; i++) {
+ long id = addColor(resolver, accountName, accountType, null, DEFAULT_INDICES[i],
+ DEFAULT_TYPES[i], DEFAULT_COLORS[i]);
+ if (id == -1) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static void deleteColorsByAccount(ContentResolver resolver, String accountName,
+ String accountType) {
+ Uri uri = asSyncAdapter(Colors.CONTENT_URI, accountName, accountType);
+ resolver.delete(uri, WHERE_COLOR_ACCOUNT, new String[] { accountName, accountType });
+ }
+ }
+
/**
* Helper class for manipulating entries in the Reminders table.
@@ -1322,6 +1443,339 @@
removeAndVerifyCalendar(account, calendarId);
}
+ @MediumTest
+ public void testColorWriteRequirements() {
+ String account = "colw_account";
+ String account2 = "colw2_account";
+ int seed = 0;
+ Uri uri = asSyncAdapter(Colors.CONTENT_URI, account, CTS_TEST_TYPE);
+ Uri uri2 = asSyncAdapter(Colors.CONTENT_URI, account2, CTS_TEST_TYPE);
+
+ // Clean up just in case
+ ColorHelper.deleteColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+ ColorHelper.deleteColorsByAccount(mContentResolver, account2, CTS_TEST_TYPE);
+
+ ContentValues colorValues = new ContentValues();
+ // Account name/type must be in the query params, so may be left
+ // out here
+ colorValues.put(Colors.DATA, "0");
+ colorValues.put(Colors.COLOR_KEY, "1");
+ colorValues.put(Colors.COLOR_TYPE, 0);
+ colorValues.put(Colors.COLOR, 0xff000000);
+
+ // Verify only a sync adapter can write to Colors
+ try {
+ mContentResolver.insert(Colors.CONTENT_URI, colorValues);
+ fail("Should not allow non-sync adapter to insert colors");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ // Verify everything except DATA is required
+ ContentValues testVals = new ContentValues(colorValues);
+ for (String key : colorValues.keySet()) {
+
+ testVals.remove(key);
+ try {
+ Uri colUri = mContentResolver.insert(uri, testVals);
+ if (!TextUtils.equals(key, Colors.DATA)) {
+ // The DATA field is allowed to be empty.
+ fail("Should not allow color creation without " + key);
+ }
+ ColorHelper.deleteColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+ } catch (IllegalArgumentException e) {
+ if (TextUtils.equals(key, Colors.DATA)) {
+ // The DATA field is allowed to be empty.
+ fail("Should allow color creation without " + key);
+ }
+ }
+ testVals.put(key, colorValues.getAsString(key));
+ }
+
+ // Verify writing a color works
+ Uri col1 = mContentResolver.insert(uri, colorValues);
+
+ // Verify adding the same color fails
+ try {
+ mContentResolver.insert(uri, colorValues);
+ fail("Should not allow adding the same color twice");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ // Verify specifying a different account than the query params doesn't work
+ colorValues.put(Colors.ACCOUNT_NAME, account2);
+ try {
+ mContentResolver.insert(uri, colorValues);
+ fail("Should use the account from the query params, not the values.");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ // Verify adding a color to a different account works
+ Uri col2 = mContentResolver.insert(uri2, colorValues);
+
+ // And a different index on the same account
+ colorValues.put(Colors.COLOR_KEY, "2");
+ Uri col3 = mContentResolver.insert(uri2, colorValues);
+
+ // Verify that all three colors are in the table
+ Cursor c = ColorHelper.findColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+ assertEquals(1, c.getCount());
+ c.close();
+ c = ColorHelper.findColorsByAccount(mContentResolver, account2, CTS_TEST_TYPE);
+ assertEquals(2, c.getCount());
+ c.close();
+
+ // Verify deleting them works
+ ColorHelper.deleteColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+ ColorHelper.deleteColorsByAccount(mContentResolver, account2, CTS_TEST_TYPE);
+
+ c = ColorHelper.findColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+ assertEquals(0, c.getCount());
+ c.close();
+ c = ColorHelper.findColorsByAccount(mContentResolver, account2, CTS_TEST_TYPE);
+ assertEquals(0, c.getCount());
+ c.close();
+ }
+
+ /**
+ * Tests Colors interaction with the Calendars table.
+ */
+ @MediumTest
+ public void testCalendarColors() {
+ String account = "cc_account";
+ int seed = 0;
+
+ // Clean up just in case
+ CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
+ ColorHelper.deleteColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+
+ // Test inserting a calendar with an invalid color index
+ ContentValues cv = CalendarHelper.getNewCalendarValues(account, seed++);
+ cv.put(Calendars.CALENDAR_COLOR_KEY, "badIndex");
+ Uri calSyncUri = asSyncAdapter(Calendars.CONTENT_URI, account, CTS_TEST_TYPE);
+ Uri colSyncUri = asSyncAdapter(Colors.CONTENT_URI, account, CTS_TEST_TYPE);
+
+ try {
+ Uri uri = mContentResolver.insert(calSyncUri, cv);
+ fail("Should not allow insertion of invalid color index into Calendars");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ // Test updating a calendar with an invalid color index
+ long calendarId = createAndVerifyCalendar(account, seed++, null);
+ cv.clear();
+ cv.put(Calendars.CALENDAR_COLOR_KEY, "badIndex2");
+ Uri calendarUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calendarId);
+ try {
+ mContentResolver.update(calendarUri, cv, null, null);
+ fail("Should not allow update of invalid color index into Calendars");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ assertTrue(ColorHelper.addDefaultColorsToAccount(mContentResolver, account, CTS_TEST_TYPE));
+
+ // Test that inserting a valid color index works
+ cv = CalendarHelper.getNewCalendarValues(account, seed++);
+ cv.put(Calendars.CALENDAR_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.C_COLOR_0]);
+
+ Uri uri = mContentResolver.insert(calSyncUri, cv);
+ long calendarId2 = ContentUris.parseId(uri);
+ assertTrue(calendarId2 >= 0);
+ // And updates the calendar's color to the one in the table
+ cv.put(Calendars.CALENDAR_COLOR, ColorHelper.DEFAULT_COLORS[ColorHelper.C_COLOR_0]);
+ verifyCalendar(account, cv, calendarId2, 2);
+
+ // Test that updating a valid color index also updates the color in a
+ // calendar
+ cv.clear();
+ cv.put(Calendars.CALENDAR_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.C_COLOR_0]);
+ mContentResolver.update(calendarUri, cv, null, null);
+ Cursor c = mContentResolver.query(calendarUri,
+ new String[] { Calendars.CALENDAR_COLOR_KEY, Calendars.CALENDAR_COLOR },
+ null, null, null);
+ try {
+ c.moveToFirst();
+ String index = c.getString(0);
+ int color = c.getInt(1);
+ assertEquals(index, ColorHelper.DEFAULT_INDICES[ColorHelper.C_COLOR_0]);
+ assertEquals(color, ColorHelper.DEFAULT_COLORS[ColorHelper.C_COLOR_0]);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // And clearing it doesn't change the color
+ cv.put(Calendars.CALENDAR_COLOR_KEY, (String) null);
+ mContentResolver.update(calendarUri, cv, null, null);
+ c = mContentResolver.query(calendarUri,
+ new String[] { Calendars.CALENDAR_COLOR_KEY, Calendars.CALENDAR_COLOR },
+ null, null, null);
+ try {
+ c.moveToFirst();
+ String index = c.getString(0);
+ int color = c.getInt(1);
+ assertEquals(index, null);
+ assertEquals(ColorHelper.DEFAULT_COLORS[ColorHelper.C_COLOR_0], color);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // Test that setting a calendar color to an event color fails
+ cv.put(Calendars.CALENDAR_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_0]);
+ try {
+ mContentResolver.update(calendarUri, cv, null, null);
+ fail("Should not allow a calendar to use an event color");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ // Test that you can't remove a color that is referenced by a calendar
+ cv.put(Calendars.CALENDAR_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.C_COLOR_3]);
+ mContentResolver.update(calendarUri, cv, null, null);
+
+ try {
+ mContentResolver.delete(colSyncUri, ColorHelper.WHERE_COLOR_ACCOUNT_AND_INDEX,
+ new String[] {
+ account, CTS_TEST_TYPE,
+ ColorHelper.DEFAULT_INDICES[ColorHelper.C_COLOR_3]
+ });
+ fail("Should not allow deleting referenced color");
+ } catch (UnsupportedOperationException e) {
+ // WAI
+ }
+
+ // Clean up
+ CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
+ ColorHelper.deleteColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+ }
+
+ /**
+ * Tests Colors interaction with the Events table.
+ */
+ @MediumTest
+ public void testEventColors() {
+ String account = "ec_account";
+ int seed = 0;
+
+ // Clean up just in case
+ CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
+ ColorHelper.deleteColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+
+ // Test inserting an event with an invalid color index
+ long cal_id = createAndVerifyCalendar(account, seed++, null);
+
+ Uri colSyncUri = asSyncAdapter(Colors.CONTENT_URI, account, CTS_TEST_TYPE);
+
+ ContentValues ev = EventHelper.getNewEventValues(account, seed++, cal_id, false);
+ ev.put(Events.EVENT_COLOR_KEY, "badIndex");
+
+ try {
+ Uri uri = mContentResolver.insert(Events.CONTENT_URI, ev);
+ fail("Should not allow insertion of invalid color index into Events");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ // Test updating an event with an invalid color index fails
+ long event_id = createAndVerifyEvent(account, seed++, cal_id, false, null);
+ ev.clear();
+ ev.put(Events.EVENT_COLOR_KEY, "badIndex2");
+ Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, event_id);
+ try {
+ mContentResolver.update(eventUri, ev, null, null);
+ fail("Should not allow update of invalid color index into Events");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ assertTrue(ColorHelper.addDefaultColorsToAccount(mContentResolver, account, CTS_TEST_TYPE));
+
+ // Test that inserting a valid color index works
+ ev = EventHelper.getNewEventValues(account, seed++, cal_id, false);
+ ev.put(Events.EVENT_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_0]);
+
+ Uri uri = mContentResolver.insert(Events.CONTENT_URI, ev);
+ long eventId2 = ContentUris.parseId(uri);
+ assertTrue(eventId2 >= 0);
+ // And updates the event's color to the one in the table
+ ev.put(Events.EVENT_COLOR, ColorHelper.DEFAULT_COLORS[ColorHelper.E_COLOR_0]);
+ verifyEvent(ev, eventId2);
+
+ // Test that updating a valid color index also updates the color in an
+ // event
+ ev.clear();
+ ev.put(Events.EVENT_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_1]);
+ mContentResolver.update(eventUri, ev, null, null);
+ Cursor c = mContentResolver.query(eventUri, new String[] {
+ Events.EVENT_COLOR_KEY, Events.EVENT_COLOR
+ }, null, null, null);
+ try {
+ c.moveToFirst();
+ String index = c.getString(0);
+ int color = c.getInt(1);
+ assertEquals(index, ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_1]);
+ assertEquals(color, ColorHelper.DEFAULT_COLORS[ColorHelper.E_COLOR_1]);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // And clearing it doesn't change the color
+ ev.put(Events.EVENT_COLOR_KEY, (String) null);
+ mContentResolver.update(eventUri, ev, null, null);
+ c = mContentResolver.query(eventUri, new String[] {
+ Events.EVENT_COLOR_KEY, Events.EVENT_COLOR
+ }, null, null, null);
+ try {
+ c.moveToFirst();
+ String index = c.getString(0);
+ int color = c.getInt(1);
+ assertEquals(index, null);
+ assertEquals(ColorHelper.DEFAULT_COLORS[ColorHelper.E_COLOR_1], color);
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+
+ // Test that setting an event color to a calendar color fails
+ ev.put(Events.EVENT_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.C_COLOR_2]);
+ try {
+ mContentResolver.update(eventUri, ev, null, null);
+ fail("Should not allow an event to use a calendar color");
+ } catch (IllegalArgumentException e) {
+ // WAI
+ }
+
+ // Test that you can't remove a color that is referenced by an event
+ ev.put(Events.EVENT_COLOR_KEY, ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_1]);
+ mContentResolver.update(eventUri, ev, null, null);
+ try {
+ mContentResolver.delete(colSyncUri, ColorHelper.WHERE_COLOR_ACCOUNT_AND_INDEX,
+ new String[] {
+ account, CTS_TEST_TYPE,
+ ColorHelper.DEFAULT_INDICES[ColorHelper.E_COLOR_1]
+ });
+ fail("Should not allow deleting referenced color");
+ } catch (UnsupportedOperationException e) {
+ // WAI
+ }
+
+ // TODO test colors with exceptions
+
+ // Clean up
+ CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
+ ColorHelper.deleteColorsByAccount(mContentResolver, account, CTS_TEST_TYPE);
+ }
+
/**
* Tests creation and manipulation of ExtendedProperties.
*/
@@ -1517,7 +1971,7 @@
values, seed++);
assertEquals(1, mContentResolver.update(uri, updateValues, null, null));
- verifyCalendar(account, values, id);
+ verifyCalendar(account, values, id, 1);
// Update the calendar using selection + args
String selection = Calendars._ID + "=?";
@@ -1528,7 +1982,7 @@
assertEquals(1, mContentResolver.update(
Calendars.CONTENT_URI, updateValues, selection, selectionArgs));
- verifyCalendar(account, values, id);
+ verifyCalendar(account, values, id, 1);
removeAndVerifyCalendar(account, id);
}
@@ -1605,6 +2059,26 @@
removeAndVerifyEvent(eventUri, eventValues, account);
+ // Attempt to create an event without a calendar ID.
+ ContentValues badValues = EventHelper.getNewEventValues(account, seed++, calendarId, true);
+ badValues.remove(Events.CALENDAR_ID);
+ try {
+ createAndVerifyEvent(account, seed, calendarId, true, badValues);
+ fail("was allowed to create an event without CALENDAR_ID");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+
+ // Validation may be relaxed for content providers, so test missing timezone as app.
+ badValues = EventHelper.getNewEventValues(account, seed++, calendarId, false);
+ badValues.remove(Events.EVENT_TIMEZONE);
+ try {
+ createAndVerifyEvent(account, seed, calendarId, false, badValues);
+ fail("was allowed to create an event without EVENT_TIMEZONE");
+ } catch (IllegalArgumentException iae) {
+ // expected
+ }
+
removeAndVerifyCalendar(account, calendarId);
}
@@ -1633,6 +2107,17 @@
updateValues.put(Events.DIRTY, 1); // provider should have marked as dirty
verifyEvent(updateValues, eventId);
+ // Try nulling out a required value.
+ ContentValues badValues = new ContentValues(updateValues);
+ badValues.putNull(Events.EVENT_TIMEZONE);
+ badValues.remove(Events.DIRTY);
+ try {
+ mContentResolver.update(eventUri, badValues, null, null);
+ fail("was allowed to null out EVENT_TIMEZONE");
+ } catch (IllegalArgumentException iae) {
+ // good
+ }
+
removeAndVerifyEvent(eventUri, eventValues, account);
// delete the calendar
@@ -2596,6 +3081,7 @@
insertUri = asSyncAdapter(insertUri, account, CTS_TEST_TYPE);
}
Uri uri = mContentResolver.insert(insertUri, values);
+ assertNotNull(uri);
// Verify
EventHelper.addDefaultReadOnlyValues(values, account, asSyncAdapter);
@@ -2744,7 +3230,7 @@
long calendarId = ContentUris.parseId(uri);
assertTrue(calendarId >= 0);
- verifyCalendar(account, values, calendarId);
+ verifyCalendar(account, values, calendarId, 1);
return calendarId;
}
@@ -2768,19 +3254,21 @@
}
/**
- * Check all the fields of a calendar contained in values + id. This assumes
- * a single calendar has been created on the given account.
+ * Check all the fields of a calendar contained in values + id.
*
* @param account the account of the calendar
* @param values the values to check against the db
* @param id the _id of the calendar
+ * @param expectedCount the number of calendars expected on this account
*/
- private void verifyCalendar(String account, ContentValues values, long id) {
+ private void verifyCalendar(String account, ContentValues values, long id, int expectedCount) {
// Verify
Cursor c = CalendarHelper.getCalendarsByAccount(mContentResolver, account);
- assertEquals(1, c.getCount());
+ assertEquals(expectedCount, c.getCount());
assertTrue(c.moveToFirst());
- assertEquals(id, c.getLong(0));
+ while (c.getLong(0) != id) {
+ assertTrue(c.moveToNext());
+ }
for (String key : values.keySet()) {
int index = c.getColumnIndex(key);
assertTrue("Key " + key + " not in projection", index >= 0);
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java
index 59c4de4..5418a80 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ComputeTest.java
@@ -324,6 +324,18 @@
assertEquals(result, RS_MSG_TEST_PASSED);
}
+ /**
+ * Test utility functions.
+ */
+ public void testUtilityFunctions() {
+ ScriptC_primitives t = new ScriptC_primitives(mRS,
+ mRes,
+ R.raw.utils);
+ t.invoke_test();
+ waitForMessage();
+ assertEquals(result, RS_MSG_TEST_PASSED);
+ }
+
void setUpAllocation(Allocation a, int val) {
Type t = a.getType();
int x = t.getX();
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ContextCreationTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ContextCreationTest.java
index 84c8ade..358c494 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ContextCreationTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ContextCreationTest.java
@@ -50,6 +50,12 @@
super.setUp();
mActivity = getActivity();
}
+
+ @Override
+ protected void tearDown() throws Exception {
+ mActivity.destroyAll();
+ super.tearDown();
+ }
/**
* Test repeated recreation of the renderscript context with a
@@ -74,7 +80,7 @@
public void testCreationWithoutDelay() {
for (int i = 0; i < NUM_RECREATE_ITERATIONS_WITHOUT_DELAY; i++) {
if (LOG_RECREATE) {
- Log.w(TAG, "Recreate (no delay) step " + i + " - pause");
+ Log.w(TAG, "Recreate (no delay) step " + i);
}
mActivity.recreateView();
}
diff --git a/tests/tests/security/src/android/security/cts/VoldExploitTest.java b/tests/tests/security/src/android/security/cts/VoldExploitTest.java
index 1f0929f..00d0c1a 100644
--- a/tests/tests/security/src/android/security/cts/VoldExploitTest.java
+++ b/tests/tests/security/src/android/security/cts/VoldExploitTest.java
@@ -16,12 +16,19 @@
package android.security.cts;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.cts.NetlinkSocket;
-import junit.framework.TestCase;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.storage.StorageManager;
+import android.test.AndroidTestCase;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -29,10 +36,139 @@
import java.util.Scanner;
import java.util.Set;
-public class VoldExploitTest extends TestCase {
+public class VoldExploitTest extends AndroidTestCase {
/**
- * Try to crash the vold program.
+ * Try to inject vold commands.
+ */
+ public void testTryCommandInjection() throws Exception {
+ final StorageManager sm = (StorageManager) getContext().getSystemService(
+ Context.STORAGE_SERVICE);
+ try {
+ sm.getMountedObbPath("/dev/null\0asec list");
+ fail("able to inject vold commands");
+ } catch(IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ /**
+ * Validate that this device isn't vulnerable to the "ZergRush"
+ * vold vulnerability (CVE-2011-3874).
+ *
+ * https://github.com/revolutionary/zergRush/blob/master/zergRush.c
+ *
+ * Note: If the ZergRush vulnerability is present, the call to
+ * {@link StorageManager#getMountedObbPath(String)} below hangs until CTS
+ * kills the testsuite (10 minutes). A timeout, while not desirable,
+ * is the typical failure for this test.
+ */
+ public void testZergRushCrash() throws Exception {
+ Set<Integer> pids = getPids();
+ assertTrue(pids.size() > 1); // at least vold and netd should exist
+
+ StorageManager sm = (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE);
+ try {
+ sm.getMountedObbPath("AAAA AAAA AAAA AAAA "
+ + "AAAA AAAA AAAA AAAA "
+ + "AAAA AAAA AAAA AAAA "
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA");
+ fail("Should have gotten an IllegalStateException");
+ } catch (IllegalStateException e) {
+ // expected
+ }
+
+ // Check to see if all the processes are still alive. If
+ // any of them have died, we found an exploitable bug.
+ for (int i : pids) {
+ assertTrue(
+ "PID=" + i + " crashed due to a malformed mount message."
+ + " Detected unpatched ZergRush vulnerability (CVE-2011-3874).",
+ new File("/proc/" + i + "/cmdline").exists());
+ }
+ }
+
+ /**
+ * Validate that this device isn't vulnerable to the "ZergRush"
+ * vold vulnerability (CVE-2011-3874).
+ *
+ * https://github.com/revolutionary/zergRush/blob/master/zergRush.c
+ *
+ * Note: If the ZergRush vulnerability is present, the call to
+ * {@link IBinder#transact(int, android.os.Parcel, android.os.Parcel, int)}}
+ * below hangs until CTS kills the testsuite (10 minutes). A timeout,
+ * while not desirable, is the typical failure for this test.
+ *
+ * This test accomplishes the same thing as {@link #testZergRushCrash()}
+ */
+ public void testZergRushUsingRelection() {
+ // This test assumes we have the MOUNT_UNMOUNT_FILESYSTEMS permission
+ // Check it first so we know we're reaching the vulnerable code.
+ assertEquals(PackageManager.PERMISSION_GRANTED,
+ getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS));
+
+ Set<Integer> pids = getPids();
+ assertTrue(pids.size() > 1); // at least vold and netd should exist
+
+ try {
+ Object iBinderObj = Class.forName("android.os.ServiceManager")
+ .getDeclaredMethod("getService", String.class)
+ .invoke(null, "mount");
+ if (!(iBinderObj instanceof IBinder)) {
+ // unexpected return value, return. Assume not exploitable.
+ return;
+ }
+
+ String[] names = new String[] {
+ "IMountService", // Android 2.3
+ "android.os.storage.IMountService" // Android 2.2
+ };
+
+ for (String name : names) {
+ IBinder iBinder = (IBinder) iBinderObj;
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(name);
+ data.writeString("AAAA AAAA AAAA AAAA "
+ + "AAAA AAAA AAAA AAAA "
+ + "AAAA AAAA AAAA AAAA "
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA"
+ + "AAAA AAAA AAAA AAAA");
+
+ // If vold crashes, this next line will hang forever.
+ iBinder.transact(6, data, null, 0);
+ }
+ } catch (ClassNotFoundException e) {
+ // class doesn't exist. Assume not exploitable.
+ } catch (NoSuchMethodException e) {
+ // no such method exists. Assume not exploitable.
+ } catch (InvocationTargetException e) {
+ // can't invoke. Assume not exploitable.
+ } catch (IllegalAccessException e) {
+ // can't access. Assume not exploitable.
+ } catch (RemoteException e) {
+ // remote failure. Assume not exploitable.
+ }
+
+ // Check to see if all the processes are still alive. If
+ // any of them have died, we found an exploitable bug.
+ for (int i : pids) {
+ assertTrue(
+ "PID=" + i + " crashed due to a malformed mount message."
+ + " Detected unpatched ZergRush vulnerability (CVE-2011-3874).",
+ new File("/proc/" + i + "/cmdline").exists());
+ }
+ }
+
+ /**
+ * Try to crash the vold program using CVE-2011-1823.
*
* This test attempts to send an invalid netlink messages to
* any process which is listening for the messages. If we detect
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 7ea9612..ddce3cd 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -96,8 +96,10 @@
"302720", // Rogers
"30272", // Rogers
"302370", // Fido
- "30237", // Fido
- "45008" // KT
+ "30237", // Fido
+ "45008", // KT
+ "45005", // SKT Mobility
+ "45002" // SKT Mobility
);
// List of network operators that doesn't support Maltipart SMS message
diff --git a/tests/tests/view/src/android/view/cts/GestureDetectorTestUtil.java b/tests/tests/view/src/android/view/cts/GestureDetectorTestUtil.java
index b43c8e2..98444b3 100644
--- a/tests/tests/view/src/android/view/cts/GestureDetectorTestUtil.java
+++ b/tests/tests/view/src/android/view/cts/GestureDetectorTestUtil.java
@@ -31,6 +31,13 @@
*/
public static void testGestureDetector(InstrumentationTestCase testcase,
GestureDetectorStubActivity activity) {
+ // wait for launching view complete
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
View view = activity.getView();
TouchUtils.clickView(testcase, view);
TouchUtils.longClickView(testcase, view);
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index b06109a..3eb49b3 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -622,26 +622,26 @@
"<body onload=\"document.title = typeof window.injectedObject;\"></body></html>";
// Test that the property is initially undefined.
- mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", "UTF-8");
+ mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
waitForLoadComplete();
assertEquals("undefined", mWebView.getTitle());
// Test that adding a null object has no effect.
mWebView.addJavascriptInterface(null, "injectedObject");
- mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", "UTF-8");
+ mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
waitForLoadComplete();
assertEquals("undefined", mWebView.getTitle());
// Test that adding an object gives an object type.
final Object obj = new Object();
mWebView.addJavascriptInterface(obj, "injectedObject");
- mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", "UTF-8");
+ mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
waitForLoadComplete();
assertEquals("object", mWebView.getTitle());
// Test that trying to replace with a null object has no effect.
mWebView.addJavascriptInterface(null, "injectedObject");
- mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", "UTF-8");
+ mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
waitForLoadComplete();
assertEquals("object", mWebView.getTitle());
}
@@ -677,12 +677,12 @@
"</head><body onload=\"updateTitle();\"></body></html>";
mWebView.addJavascriptInterface(obj, name);
- mWebView.loadData(Uri.encode(setTitleToPropertyTypeHtml), "text/html", "UTF-8");
+ mWebView.loadData(Uri.encode(setTitleToPropertyTypeHtml), "text/html", null);
waitForLoadComplete();
assertEquals("object", mWebView.getTitle());
mWebView.removeJavascriptInterface(name);
- mWebView.loadData(Uri.encode(setTitleToPropertyTypeHtml), "text/html", "UTF-8");
+ mWebView.loadData(Uri.encode(setTitleToPropertyTypeHtml), "text/html", null);
waitForLoadComplete();
assertEquals("undefined", mWebView.getTitle());
}
@@ -702,13 +702,13 @@
// Test that adding an object gives an object type.
mWebView.addJavascriptInterface(new Object(), "injectedObject");
- mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", "UTF-8");
+ mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
waitForLoadComplete();
assertEquals("object", mWebView.getTitle());
// Test that reloading the page after removing the object leaves the property undefined.
mWebView.removeJavascriptInterface("injectedObject");
- mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", "UTF-8");
+ mWebView.loadData(setTitleToPropertyTypeHtml, "text/html", null);
waitForLoadComplete();
assertEquals("undefined", mWebView.getTitle());
}
@@ -763,7 +763,7 @@
mWebView.loadData("<html><head></head>" +
"<body onload=\"window.removedObject.remove();" +
"resultObject.setResult(removedObject.toString());\"></body></html>",
- "text/html", "UTF-8");
+ "text/html", null);
}
});
assertEquals("removedObject", resultObject.getResult());
@@ -1100,7 +1100,7 @@
assertNull(mWebView.getTitle());
mWebView.loadData("<html><head><title>Hello,World!</title></head><body></body>" +
"</html>",
- "text/html", "UTF-8");
+ "text/html", null);
waitForLoadComplete();
assertEquals("Hello,World!", mWebView.getTitle());
}
@@ -1138,7 +1138,7 @@
"document.title = " +
"document.getElementById('frame').contentWindow.location.href;" +
"\"><iframe id=\"frame\" src=\"" + crossOriginUrl + "\"></body></html>",
- "text/html", "UTF-8");
+ "text/html", null);
}
});
assertEquals(ConsoleMessage.MessageLevel.ERROR, webChromeClient.getMessageLevel());
@@ -1217,7 +1217,7 @@
public void testFindAll() {
String p = "<p>Find all instances of find on the page and highlight them.</p>";
- mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
waitForLoadComplete();
assertEquals(2, mWebView.findAll("find"));
@@ -1278,7 +1278,7 @@
String p = "<p style=\"height:" + dimension + "px;\">" +
"Find all instances of a word on the page and highlight them.</p>";
- mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", null);
waitForLoadComplete();
// highlight all the strings found
@@ -1369,7 +1369,7 @@
runTestOnUiThread(new Runnable() {
public void run() {
mWebView.loadData("<html><body><img src=\"" + imgUrl + "\"/></body></html>",
- "text/html", "UTF-8");
+ "text/html", null);
waitForLoadComplete();
Message response = new Message();
response.setTarget(handler);
@@ -1425,7 +1425,7 @@
int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
String p = "<p style=\"height:" + dimension + "px;\">" +
"Scroll by half the size of the page.</p>";
- mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1508,7 +1508,7 @@
runTestOnUiThread(new Runnable() {
public void run() {
- mWebView.loadData("<html><body></body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body></body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1521,7 +1521,7 @@
runTestOnUiThread(new Runnable() {
public void run() {
assertEquals(mWebView.getHeight(), mWebView.getContentHeight() * mWebView.getScale(), 2f);
- mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1534,7 +1534,7 @@
runTestOnUiThread(new Runnable() {
public void run() {
- mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + p + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1647,7 +1647,7 @@
int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
String p = "<p style=\"height:" + dimension + "px;" +
"width:" + dimension + "px\">Test fling scroll.</p>";
- mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1696,7 +1696,7 @@
+ "\">HTML_URL2</A></DL><p>";
runTestOnUiThread(new Runnable() {
public void run() {
- mWebView.loadData("<html><body>" + links + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + links + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1774,7 +1774,7 @@
runTestOnUiThread(new Runnable() {
public void run() {
mWebView.loadData("<html><title>Title</title><body><img src=\"" + imgUrl
- + "\"/></body></html>", "text/html", "UTF-8");
+ + "\"/></body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1912,7 +1912,7 @@
runTestOnUiThread(new Runnable() {
public void run() {
- mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1924,7 +1924,7 @@
mWebView.setInitialScale(0);
// modify content to fool WebKit into re-loading
- mWebView.loadData("<html><body>" + p + "2" + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "2" + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1935,7 +1935,7 @@
assertEquals(defaultScale, mWebView.getScale(), .01f);
mWebView.setInitialScale(50);
- mWebView.loadData("<html><body>" + p + "3" + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "3" + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -1946,7 +1946,7 @@
assertEquals(0.5f, mWebView.getScale(), .02f);
mWebView.setInitialScale(0);
- mWebView.loadData("<html><body>" + p + "4" + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "4" + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -2432,7 +2432,7 @@
DisplayMetrics metrics = mWebView.getContext().getResources().getDisplayMetrics();
final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
String p = "<p style=\"height:" + dimension + "px;width:" + dimension + "px\"> </p>";
- mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", "UTF-8");
+ mWebView.loadData("<html><body>" + p + "</body></html>", "text/html", null);
waitForLoadComplete();
}
});
@@ -2501,7 +2501,7 @@
mWebView.setWebViewClient(new WebViewClient());
mWebView.setDownloadListener(listener);
mWebView.loadData("<html><body><a href=\"" + url + "\">link</a></body></html>",
- "text/html", "UTF-8");
+ "text/html", null);
waitForLoadComplete();
assertTrue(mWebView.requestFocus(View.FOCUS_DOWN, null));
}
diff --git a/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java b/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java
index 59b940c..291f886 100644
--- a/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AlphabetIndexerTest.java
@@ -16,19 +16,16 @@
package android.widget.cts;
-import com.android.common.ArrayListCursor;
-
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetClass;
import dalvik.annotation.TestTargetNew;
import dalvik.annotation.TestTargets;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.test.AndroidTestCase;
import android.widget.AlphabetIndexer;
-import java.util.ArrayList;
-
@TestTargetClass(AlphabetIndexer.class)
public class AlphabetIndexerTest extends AndroidTestCase {
private static final String[] COUNTRIES_LIST = new String[]
@@ -36,7 +33,7 @@
private static final String[] NAMES_LIST = new String[]
{"Andy", "Bergkamp", "David", "Jacky", "Kevin", "Messi", "Michael", "Steven"};
private static final String ALPHABET = " ABCDEFGHIJKLMNOPQRSTUVWXYZ";
- private static final int SORTED_COLUMN_INDEX = 0;
+ private static final int SORTED_COLUMN_INDEX = 1;
private static final int INDEX_OF_ARGENTINA = 0;
private static final int INDEX_OF_CHINA = 2;
@@ -151,16 +148,13 @@
@SuppressWarnings("unchecked")
private Cursor createCursor(String listName, String[] listData) {
- String[] columns = { listName };
+ String[] columns = { "_id", listName };
- ArrayList<ArrayList> list = new ArrayList<ArrayList>();
- for (String cell : listData) {
- ArrayList<String> row = new ArrayList<String>();
- row.add(cell);
- list.add(row);
+ MatrixCursor cursor = new MatrixCursor(columns, listData.length);
+ for (int i = 0; i < listData.length; i++) {
+ cursor.addRow(new Object[] { i, listData[i] });
}
-
- return new ArrayListCursor(columns, list);
+ return cursor;
}
/**
diff --git a/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
index 38ec995..64935c5 100644
--- a/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
@@ -17,7 +17,6 @@
package android.widget.cts;
import com.android.cts.stub.R;
-import com.android.common.ArrayListCursor;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetClass;
@@ -26,14 +25,13 @@
import android.content.Context;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.test.InstrumentationTestCase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ResourceCursorAdapter;
-import java.util.ArrayList;
-
/**
* Test {@link ResourceCursorAdapter}.
*/
@@ -185,21 +183,22 @@
*/
@SuppressWarnings("unchecked")
private Cursor createTestCursor(int colCount, int rowCount) {
- ArrayList<ArrayList> list = new ArrayList<ArrayList>();
- String[] columns = new String[colCount];
+ String[] columns = new String[colCount + 1];
for (int i = 0; i < colCount; i++) {
columns[i] = "column" + i;
}
+ columns[colCount] = "_id";
+ MatrixCursor cursor = new MatrixCursor(columns, rowCount);
+ Object[] row = new Object[colCount + 1];
for (int i = 0; i < rowCount; i++) {
- ArrayList<String> row = new ArrayList<String>();
for (int j = 0; j < colCount; j++) {
- row.add("" + i + "" + j);
+ row[j] = "" + i + "" + j;
}
- list.add(row);
+ row[colCount] = i;
+ cursor.addRow(row);
}
-
- return new ArrayListCursor(columns, list);
+ return cursor;
}
private static class MockResourceCursorAdapter extends ResourceCursorAdapter {
diff --git a/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java
index 716104d..62f0709 100644
--- a/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ResourceCursorTreeAdapterTest.java
@@ -17,7 +17,6 @@
package android.widget.cts;
import com.android.cts.stub.R;
-import com.android.common.ArrayListCursor;
import dalvik.annotation.TestTargets;
import dalvik.annotation.TestLevel;
@@ -26,14 +25,13 @@
import android.content.Context;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.test.InstrumentationTestCase;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ResourceCursorTreeAdapter;
-import java.util.ArrayList;
-
/**
* Test {@link ResourceCursorTreeAdapter}.
*/
@@ -186,21 +184,22 @@
*/
@SuppressWarnings("unchecked")
private Cursor createTestCursor(int colCount, int rowCount) {
- ArrayList<ArrayList> list = new ArrayList<ArrayList>();
- String[] columns = new String[colCount];
+ String[] columns = new String[colCount + 1];
for (int i = 0; i < colCount; i++) {
columns[i] = "column" + i;
}
+ columns[colCount] = "_id";
+ MatrixCursor cursor = new MatrixCursor(columns, rowCount);
+ Object[] row = new Object[colCount + 1];
for (int i = 0; i < rowCount; i++) {
- ArrayList<String> row = new ArrayList<String>();
for (int j = 0; j < colCount; j++) {
- row.add("" + rowCount + "" + colCount);
+ row[j] = "" + rowCount + "" + colCount;
}
- list.add(row);
+ row[colCount] = i;
+ cursor.addRow(row);
}
-
- return new ArrayListCursor(columns, list);
+ return cursor;
}
private class MockResourceCursorTreeAdapter extends ResourceCursorTreeAdapter {
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
index 08e425a..e6e9d47 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleCursorAdapterTest.java
@@ -17,7 +17,6 @@
package android.widget.cts;
import com.android.cts.stub.R;
-import com.android.common.ArrayListCursor;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetClass;
@@ -27,6 +26,7 @@
import android.content.Context;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.test.InstrumentationTestCase;
@@ -43,7 +43,6 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.ArrayList;
/**
* Test {@link SimpleCursorAdapter}.
@@ -470,21 +469,22 @@
*/
@SuppressWarnings("unchecked")
private Cursor createTestCursor(int colCount, int rowCount) {
- ArrayList<ArrayList> list = new ArrayList<ArrayList>();
- String[] columns = new String[colCount];
+ String[] columns = new String[colCount + 1];
for (int i = 0; i < colCount; i++) {
columns[i] = "column" + i;
}
+ columns[colCount] = "_id";
+ MatrixCursor cursor = new MatrixCursor(columns, rowCount);
+ Object[] row = new Object[colCount + 1];
for (int i = 0; i < rowCount; i++) {
- ArrayList<String> row = new ArrayList<String>();
for (int j = 0; j < colCount; j++) {
- row.add("" + i + "" + j);
+ row[j] = "" + i + "" + j;
}
- list.add(row);
+ row[colCount] = i;
+ cursor.addRow(row);
}
-
- return new ArrayListCursor(columns, list);
+ return cursor;
}
private static class MockViewBinder implements ViewBinder {
diff --git a/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java b/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
index 8e6f0f3..bbb96c4 100644
--- a/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/SimpleCursorTreeAdapterTest.java
@@ -17,7 +17,6 @@
package android.widget.cts;
import com.android.cts.stub.R;
-import com.android.common.ArrayListCursor;
import dalvik.annotation.TestLevel;
import dalvik.annotation.TestTargetClass;
@@ -27,6 +26,7 @@
import android.content.Context;
import android.database.Cursor;
+import android.database.MatrixCursor;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.test.InstrumentationTestCase;
@@ -35,8 +35,6 @@
import android.widget.SimpleCursorTreeAdapter;
import android.widget.TextView;
-import java.util.ArrayList;
-
/**
* Test {@link SimpleCursorTreeAdapter}.
*/
@@ -232,21 +230,22 @@
*/
@SuppressWarnings("unchecked")
private Cursor createTestCursor(int colCount, int rowCount, String prefix) {
- ArrayList<ArrayList> list = new ArrayList<ArrayList>();
- String[] columns = new String[colCount];
+ String[] columns = new String[colCount + 1];
for (int i = 0; i < colCount; i++) {
columns[i] = "column" + i;
}
+ columns[colCount] = "_id";
+ MatrixCursor cursor = new MatrixCursor(columns, rowCount);
+ Object[] row = new Object[colCount + 1];
for (int i = 0; i < rowCount; i++) {
- ArrayList<String> row = new ArrayList<String>();
for (int j = 0; j < colCount; j++) {
- row.add(prefix + i + "" + j);
+ row[j] = prefix + i + "" + j;
}
- list.add(row);
+ row[colCount] = i;
+ cursor.addRow(row);
}
-
- return new ArrayListCursor(columns, list);
+ return cursor;
}
private class MockSimpleCursorTreeAdapter extends SimpleCursorTreeAdapter {