Merge remote branch 'goog/honeycomb-mr2' into ics-mr0

Conflicts:
	apps/CtsVerifier/AndroidManifest.xml
	tests/expectations/knownfailures.txt

Change-Id: I1b918333883e0f3d651e9f4d65918974eaa3b72f
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1da4cb9..900bc77 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" />
     
@@ -170,28 +172,6 @@
             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
         </activity>
 
-        <activity android:name=".bluetooth.ConnectionAccessServerActivity"
-                android:label="@string/bt_connection_access_server"
-                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/bt_device_communication" />
-            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
-        </activity>
-        
-        <activity android:name=".bluetooth.ConnectionAccessClientActivity"
-                android:label="@string/bt_connection_access_client"
-                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/bt_device_communication" />
-            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
-        </activity>
-
         <activity android:name=".bluetooth.DevicePickerActivity"
                 android:label="@string/bt_device_picker"
                 android:configChanges="keyboardHidden|orientation" />
@@ -229,6 +209,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..687a7d7 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -764,7 +764,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/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 1ddcdd5..a07c704 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -47,6 +47,9 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        if (!hasMicrophone()) {
+            return;
+        }
         /*
          * InstrumentationTestRunner.onStart() calls Looper.prepare(), which creates a looper
          * for the current thread. However, since we don't actually call loop() in the test,
@@ -80,8 +83,10 @@
 
     @Override
     protected void tearDown() throws Exception {
-        mAudioRecord.release();
-        mLooper.quit();
+        if (hasMicrophone()) {
+            mAudioRecord.release();
+            mLooper.quit();
+        }
         super.tearDown();
     }
 
diff --git a/tests/tests/security/src/android/security/cts/VoldExploitTest.java b/tests/tests/security/src/android/security/cts/VoldExploitTest.java
index 1f0929f..8fbf874 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,125 @@
 import java.util.Scanner;
 import java.util.Set;
 
-public class VoldExploitTest extends TestCase {
+public class VoldExploitTest extends AndroidTestCase {
 
     /**
-     * Try to crash the vold program.
+     * 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/PhoneNumberUtilsTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
index 23891e0..1c45735 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
@@ -455,6 +455,11 @@
 
         // Test isWellFormedSmsAddress
         assertTrue(PhoneNumberUtils.isWellFormedSmsAddress("+17005554141"));
-        assertFalse(PhoneNumberUtils.isWellFormedSmsAddress("android"));
+        // KT allow a to be a dialable character, the network portion of 'android' is 'a'
+        if (TelephonyUtils.isKt(tm)) {
+            assertTrue(PhoneNumberUtils.isWellFormedSmsAddress("android"));
+        } else {
+            assertFalse(PhoneNumberUtils.isWellFormedSmsAddress("android"));
+        }
     }
 }
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 7ea9612..c0c26ef 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -27,12 +27,12 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.os.SystemClock;
 import android.telephony.SmsManager;
+import android.telephony.SmsMessage;
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
-import android.telephony.SmsMessage;
-import android.os.Bundle;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -46,7 +46,6 @@
 @TestTargetClass(SmsManager.class)
 public class SmsManagerTest extends AndroidTestCase {
 
-    private static final int NUM_TEXT_PARTS = 3;
     private static final String LONG_TEXT =
         "This is a very long text. This text should be broken into three " +
         "separate messages.This is a very long text. This text should be broken into " +
@@ -96,8 +95,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
@@ -155,9 +156,21 @@
     public void testDivideMessage() {
         ArrayList<String> dividedMessages = divideMessage(LONG_TEXT);
         assertNotNull(dividedMessages);
-        assertEquals(NUM_TEXT_PARTS, dividedMessages.size());
-        assertEquals(LONG_TEXT,
-                dividedMessages.get(0) + dividedMessages.get(1) + dividedMessages.get(2));
+        int numParts;
+        if (TelephonyUtils.isSkt(mTelephonyManager)) {
+            numParts = 5;
+        } else if (TelephonyUtils.isKt(mTelephonyManager)) {
+            numParts = 4;
+        } else {
+            numParts = 3;
+        }
+        assertEquals(numParts, dividedMessages.size());
+
+        String actualMessage = "";
+        for (int i = 0; i < numParts; i++) {
+            actualMessage += dividedMessages.get(i);
+        }
+        assertEquals(LONG_TEXT, actualMessage);
     }
 
     @TestTargets({
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
index 8c6ad01..27f290b 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
@@ -188,7 +188,7 @@
         int[] result = SmsMessage.calculateLength(sms.getMessageBody(), true);
         assertEquals(SMS_NUMBER1, result[0]);
         assertEquals(sms.getMessageBody().length(), result[1]);
-        assertEquals(SmsMessage.MAX_USER_DATA_SEPTETS - sms.getMessageBody().length(), result[2]);
+        assertEquals(getNumSeptets() - sms.getMessageBody().length(), result[2]);
         assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
         assertEquals(pdu, toHexString(sms.getPdu()));
 
@@ -220,7 +220,7 @@
         result = SmsMessage.calculateLength(msgBody, false);
         assertEquals(SMS_NUMBER2, result[0]);
         assertEquals(sms.getMessageBody().length(), result[1]);
-        assertEquals(SmsMessage.MAX_USER_DATA_SEPTETS - sms.getMessageBody().length(), result[2]);
+        assertEquals(getNumSeptets() - sms.getMessageBody().length(), result[2]);
         assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
 
         // Test createFromPdu Ucs to Sms
@@ -231,10 +231,20 @@
         result = SmsMessage.calculateLength(sms.getMessageBody(), true);
         assertEquals(SMS_NUMBER3, result[0]);
         assertEquals(sms.getMessageBody().length(), result[1]);
-        assertEquals(SmsMessage.MAX_USER_DATA_SEPTETS - sms.getMessageBody().length(), result[2]);
+        assertEquals(getNumSeptets() - sms.getMessageBody().length(), result[2]);
         assertEquals(SmsMessage.ENCODING_7BIT, result[3]);
     }
 
+    private int getNumSeptets() {
+        if (TelephonyUtils.isSkt(mTelephonyManager)) {
+            return 80;
+        } else if (TelephonyUtils.isKt(mTelephonyManager)) {
+            return 90;
+        } else {
+            return SmsMessage.MAX_USER_DATA_SEPTETS;
+        }
+    }
+
     @TestTargets({
         @TestTargetNew(
             level = TestLevel.COMPLETE,
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyUtils.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyUtils.java
new file mode 100644
index 0000000..c2ca833
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.telephony.cts;
+
+import android.telephony.TelephonyManager;
+
+class TelephonyUtils {
+
+    public static boolean isSkt(TelephonyManager telephonyManager) {
+        return isOperator(telephonyManager, "45005");
+    }
+
+    public static boolean isKt(TelephonyManager telephonyManager) {
+        return isOperator(telephonyManager, "45002")
+                || isOperator(telephonyManager, "45004")
+                || isOperator(telephonyManager, "45008");
+    }
+
+    private static boolean isOperator(TelephonyManager telephonyManager, String operator) {
+        String simOperator = telephonyManager.getSimOperator();
+        return simOperator != null && simOperator.equals(operator);
+    }
+
+    private TelephonyUtils() {
+    }
+}
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/tools/host/src/com/android/cts/DeviceManager.java b/tools/host/src/com/android/cts/DeviceManager.java
index 410ac7b..c794332 100644
--- a/tools/host/src/com/android/cts/DeviceManager.java
+++ b/tools/host/src/com/android/cts/DeviceManager.java
@@ -215,31 +215,28 @@
 
         @Override
         public void run() {
-            while (true) {
-                try {
-                    if ((mDevice.getSyncService() != null) && (mDevice.getPropertyCount() != 0)) {
-                        break;
+            try {
+               while (mDevice.getSyncService() == null || mDevice.getPropertyCount() == 0) {
+                    try {
+                        Thread.sleep(100);
+                    } catch (InterruptedException e) {
+                        Log.d("polling for device sync service interrupted");
                     }
-                } catch (IOException e) {
-                    Log.e("Failed to connect to device", e);
-                } catch (TimeoutException e) {
-                    Log.e("Failed to connect to device", e);
-                } catch (AdbCommandRejectedException e) {
-                    Log.e("Failed to connect to device", e);
                 }
-                try {
-                    Thread.sleep(100);
-                } catch (InterruptedException e) {
-                    Log.d("polling for device sync service interrupted");
+                CUIOutputStream.println("Device(" + mDevice + ") connected");
+                if (!TestSession.isADBServerRestartedMode()) {
+                    CUIOutputStream.printPrompt();
                 }
+                appendDevice(mDevice);
+                // increment the counter semaphore to unblock threads waiting for devices
+                mSemaphore.release();
+            } catch (IOException e) {
+                // FIXME: handle failed connection to device.
+            } catch (TimeoutException e) {
+                // FIXME: handle failed connection to device.
+            } catch (AdbCommandRejectedException e) {
+                // FIXME: handle failed connection to device.
             }
-            CUIOutputStream.println("Device(" + mDevice + ") connected");
-            if (!TestSession.isADBServerRestartedMode()) {
-                CUIOutputStream.printPrompt();
-            }
-            appendDevice(mDevice);
-            // increment the counter semaphore to unblock threads waiting for devices
-            mSemaphore.release();
         }
     }
 
@@ -313,62 +310,58 @@
             // TODO: this is flaky, no guarantee device has actually rebooted, host should wait till
             // device goes offline
             Thread.sleep(REBOOT_DELAY);
-        } else {
-            executeCommand("killall emulator");
-            executeCommand("killall emulator-x86");
-            executeCommand("killall emulator-arm");
-        }
 
-        int attempts = 0;
-        boolean deviceConnected = false;
-        while (!deviceConnected && (attempts < MAX_ADB_RESTART_ATTEMPTS)) {
-            AndroidDebugBridge.disconnectBridge();
+            int attempts = 0;
+            boolean deviceConnected = false;
+            while (!deviceConnected && (attempts < MAX_ADB_RESTART_ATTEMPTS)) {
+                AndroidDebugBridge.disconnectBridge();
 
-            // kill the server while the device is rebooting
-            executeCommand("adb kill-server");
+                // kill the server while the device is rebooting
+                executeCommand("adb kill-server");
 
-            // Reset the device counter semaphore. We will wait below until at least one device
-            // has come online. This can happen any time during or after the call to
-            // createBridge(). The counter gets increased by the DeviceServiceMonitor when a
-            // device is added.
-            mSemaphore.drainPermits();
-            AndroidDebugBridge.createBridge(getAdbLocation(), true);
+                // Reset the device counter semaphore. We will wait below until at least one device
+                // has come online. This can happen any time during or after the call to
+                // createBridge(). The counter gets increased by the DeviceServiceMonitor when a
+                // device is added.
+                mSemaphore.drainPermits();
+                AndroidDebugBridge.createBridge(getAdbLocation(), true);
 
-            boolean deviceFound = false;
-            while (!deviceFound) {
-                // wait until at least one device has been added
-                mSemaphore.tryAcquire(LONG_DELAY, TimeUnit.MILLISECONDS);
-                TestDevice device = searchTestDevice(deviceSerialNumber);
-                if (device != null) {
-                    ts.setTestDevice(device);
-                    deviceFound = true;
-                    deviceConnected = device.waitForBootComplete();
-                    // After boot is complete, the ADB connection sometimes drops
-                    // for a short time. Wait for things to stabilize.
-                    try {
-                        Thread.sleep(POST_BOOT_DELAY);
-                    } catch (InterruptedException ignored) {
-                        // ignore
-                    }
-                    // If the connection dropped during the sleep above, the TestDevice
-                    // instance is no longer valid.
-                    TestDevice newDevice = searchTestDevice(deviceSerialNumber);
-                    if (newDevice != null) {
-                        ts.setTestDevice(newDevice);
-                        if (newDevice != device) {
-                            // the connection was dropped or a second reboot occurred
-                            // TODO: replace the hardcoded /sdcard
-                            String cmd = String.format("adb -s %s shell bugreport -o " +
-                                        "/sdcard/bugreports/doubleReboot", deviceSerialNumber);
-                            executeCommand(cmd);
+                boolean deviceFound = false;
+                while (!deviceFound) {
+                    // wait until at least one device has been added
+                    mSemaphore.tryAcquire(LONG_DELAY, TimeUnit.MILLISECONDS);
+                    TestDevice device = searchTestDevice(deviceSerialNumber);
+                    if (device != null) {
+                        ts.setTestDevice(device);
+                        deviceFound = true;
+                        deviceConnected = device.waitForBootComplete();
+                        // After boot is complete, the ADB connection sometimes drops
+                        // for a short time. Wait for things to stabilize.
+                        try {
+                            Thread.sleep(POST_BOOT_DELAY);
+                        } catch (InterruptedException ignored) {
+                            // ignore
                         }
-                    } else {
-                        // connection dropped and has not come back up
-                        deviceFound = false; // go wait for next semaphore permit
+                        // If the connection dropped during the sleep above, the TestDevice
+                        // instance is no longer valid.
+                        TestDevice newDevice = searchTestDevice(deviceSerialNumber);
+                        if (newDevice != null) {
+                            ts.setTestDevice(newDevice);
+                            if (newDevice != device) {
+                                // the connection was dropped or a second reboot occurred
+                                // TODO: replace the hardcoded /sdcard
+                                String cmd = String.format("adb -s %s shell bugreport -o " +
+                                            "/sdcard/bugreports/doubleReboot", deviceSerialNumber);
+                                executeCommand(cmd);
+                            }
+                        } else {
+                            // connection dropped and has not come back up
+                            deviceFound = false; // go wait for next semaphore permit
+                        }
                     }
                 }
+                attempts += 1;
             }
-            attempts += 1;
         }
     }
 
diff --git a/tools/utils/host_config.xml b/tools/utils/host_config.xml
index 661b1a3..1a99e75 100644
--- a/tools/utils/host_config.xml
+++ b/tools/utils/host_config.xml
@@ -34,7 +34,7 @@
     <!-- Max time [ms] from start of package in batch mode and the first test status update. -->
     <IntValue name="batchStartTimeoutMs" value="1800000" />
     <!-- Max time [ms] from start of test in individual mode to the first test status update. -->
-    <IntValue name="individualStartTimeoutMs" value="300000" />
+    <IntValue name="individualStartTimeoutMs" value="600000" />
     <!-- Timeout [ms] for the signature check. -->
     <IntValue name="signatureTestTimeoutMs" value="600000" />
     <!-- Timeout [ms] for package installations. -->