/* * 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.bluetooth; import com.android.cts.verifier.PassFailButtons; import com.android.cts.verifier.R; import com.android.cts.verifier.TestResult; import android.app.AlertDialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; class MessageTestActivity extends PassFailButtons.Activity { /** Broadcast action that should only be fired when pairing for a secure connection. */ private static final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST"; private static final int ENABLE_BLUETOOTH_REQUEST = 1; private static final int PICK_SERVER_DEVICE_REQUEST = 2; private static final String MESSAGE_DELIMITER = "\n"; private static final Pattern MESSAGE_PATTERN = Pattern.compile("Message (\\d+) to .*"); private BluetoothAdapter mBluetoothAdapter; private PairingActionReceiver mPairingActionReceiver; private BluetoothChatService mChatService; private ArrayAdapter mReceivedMessagesAdapter; private ArrayAdapter mSentMessagesAdapter; private ListView mReceivedMessages; private ListView mSentMessages; private TextView mEmptyReceivedView; private TextView mEmptySentView; private AlertDialog mInstructionsDialog; private ProgressBar mProgressBar; private String mDeviceAddress; private final boolean mSecure; private final boolean mServer; private final UUID mUuid; private String mRemoteDeviceName = ""; private StringBuilder mMessageBuffer = new StringBuilder(); MessageTestActivity(boolean secure, boolean server, UUID uuid) { mSecure = secure; mServer = server; mUuid = uuid; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.bt_messages); setPassFailButtonClickListeners(); mProgressBar = (ProgressBar) findViewById(R.id.bt_progress_bar); if (mServer) { setTitle(mSecure ? R.string.bt_secure_server : R.string.bt_insecure_server); } else { setTitle(mSecure ? R.string.bt_secure_client : R.string.bt_insecure_client); } mReceivedMessages = (ListView) findViewById(R.id.bt_received_messages); mReceivedMessagesAdapter = new ArrayAdapter(this, R.layout.bt_message_row); mReceivedMessages.setAdapter(mReceivedMessagesAdapter); mSentMessages = (ListView) findViewById(R.id.bt_sent_messages); mSentMessagesAdapter = new ArrayAdapter(this, R.layout.bt_message_row); mSentMessages.setAdapter(mSentMessagesAdapter); mEmptyReceivedView = (TextView) findViewById(R.id.bt_empty_received_messages); mReceivedMessages.setEmptyView(mEmptyReceivedView); mEmptySentView = (TextView) findViewById(R.id.bt_empty_sent_messages); mSentMessages.setEmptyView(mEmptySentView); setEmptyViewText(R.string.bt_no_messages); Button makeDiscoverableButton = (Button) findViewById(R.id.bt_make_discoverable_button); makeDiscoverableButton.setVisibility(mServer ? View.VISIBLE : View.GONE); makeDiscoverableButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { makeDiscoverable(); } }); getPassButton().setEnabled(false); mPairingActionReceiver = new PairingActionReceiver(); IntentFilter intentFilter = new IntentFilter(ACTION_PAIRING_REQUEST); registerReceiver(mPairingActionReceiver, intentFilter); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (!mServer) { Intent intent = new Intent(this, DevicePickerActivity.class); startActivityForResult(intent, PICK_SERVER_DEVICE_REQUEST); } else { if (mBluetoothAdapter.isEnabled()) { startChatService(); } else { Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(intent, ENABLE_BLUETOOTH_REQUEST); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case ENABLE_BLUETOOTH_REQUEST: if (resultCode == RESULT_OK) { startChatService(); } else { setResult(RESULT_CANCELED); finish(); } break; case PICK_SERVER_DEVICE_REQUEST: if (resultCode == RESULT_OK) { mDeviceAddress = data.getStringExtra(DevicePickerActivity.EXTRA_DEVICE_ADDRESS); startChatService(); } else { setResult(RESULT_CANCELED); finish(); } break; } } private void startChatService() { mChatService = new BluetoothChatService(this, new ChatHandler(), mUuid); if (mServer) { mChatService.start(mSecure); } else { BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mDeviceAddress); mChatService.connect(device, mSecure); } } private void makeDiscoverable() { if (mBluetoothAdapter.getScanMode() != BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 30); startActivity(intent); } } private class ChatHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case BluetoothChatService.MESSAGE_STATE_CHANGE: handleStateChange(msg); break; case BluetoothChatService.MESSAGE_READ: handleMessageRead(msg); break; case BluetoothChatService.MESSAGE_WRITE: handleMessageWrite(msg); break; case BluetoothChatService.MESSAGE_DEVICE_NAME: handleDeviceName(msg); break; case BluetoothChatService.MESSAGE_TOAST: handleToast(msg); break; } } } private void handleStateChange(Message msg) { int state = msg.arg1; switch (state) { case BluetoothChatService.STATE_LISTEN: setEmptyViewText(R.string.bt_waiting); mProgressBar.setVisibility(View.VISIBLE); showInstructionsDialog(); break; case BluetoothChatService.STATE_CONNECTING: setEmptyViewText(R.string.bt_connecting); mProgressBar.setVisibility(View.VISIBLE); break; case BluetoothChatService.STATE_CONNECTED: setEmptyViewText(R.string.bt_no_messages); mProgressBar.setVisibility(View.INVISIBLE); hideInstructionsDialog(); sendInitialMessageFromClient(); break; case BluetoothChatService.STATE_NONE: setEmptyViewText(R.string.bt_no_messages); mProgressBar.setVisibility(View.INVISIBLE); break; } } private void setEmptyViewText(int textId) { mEmptyReceivedView.setText(textId); mEmptySentView.setText(textId); } private void showInstructionsDialog() { if (mInstructionsDialog == null) { mInstructionsDialog = new AlertDialog.Builder(this) .setIcon(android.R.drawable.ic_dialog_info) .setTitle(getString(R.string.bt_waiting)) .setMessage(getString(mSecure ? R.string.bt_secure_server_instructions : R.string.bt_insecure_server_instructions)) .setPositiveButton(android.R.string.ok, null) .create(); } mInstructionsDialog.show(); } private void hideInstructionsDialog() { if (mInstructionsDialog != null) { mInstructionsDialog.hide(); } } private void sendInitialMessageFromClient() { if (!mServer) { sendMessage(0); } } private void sendMessage(int number) { String message = "Message " + number + " to " + (mRemoteDeviceName != null ? mRemoteDeviceName : "") + MESSAGE_DELIMITER; mChatService.write(message.getBytes()); } private void handleMessageRead(Message msg) { String chunk = new String((byte[]) msg.obj, 0, msg.arg1); mMessageBuffer.append(chunk); int delimiterIndex = mMessageBuffer.indexOf(MESSAGE_DELIMITER); if (delimiterIndex != -1) { String message = mMessageBuffer.substring(0, delimiterIndex); // Chop off delimiter mMessageBuffer.delete(0, delimiterIndex + 1); addNewMessage(message); } } private void addNewMessage(String msg) { mReceivedMessagesAdapter.add(msg); Matcher matcher = MESSAGE_PATTERN.matcher(msg); if (matcher.matches()) { int number = Integer.valueOf(matcher.group(1)); if (mServer && number == 10 || !mServer && number == 11) { getPassButton().setEnabled(true); } if (number <= 10) { sendMessage(number + 1); } } } private void handleMessageWrite(Message msg) { String sentMessage = new String((byte[]) msg.obj).trim(); // Chop off delimiter mSentMessagesAdapter.add(sentMessage); } private void handleDeviceName(Message msg) { mRemoteDeviceName = msg.getData().getString(BluetoothChatService.DEVICE_NAME); } private void handleToast(Message msg) { String toast = msg.getData().getString(BluetoothChatService.TOAST); Toast.makeText(this, toast, Toast.LENGTH_LONG).show(); } class PairingActionReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if (!mSecure && ACTION_PAIRING_REQUEST.equals(intent.getAction())) { runOnUiThread(new Runnable() { @Override public void run() { showPairingErrorDialog(); } }); } } } private void showPairingErrorDialog() { new AlertDialog.Builder(MessageTestActivity.this) .setIcon(android.R.drawable.ic_dialog_alert) .setTitle(R.string.bt_insecure_pairing_error_title) .setMessage(R.string.bt_insecure_pairing_error_message) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { TestResult.setFailedResult(MessageTestActivity.this, getTestId(), null); finish(); } }) .setCancelable(false) .show(); } @Override protected void onDestroy() { super.onDestroy(); if (mChatService != null) { mChatService.stop(); } unregisterReceiver(mPairingActionReceiver); } }