blob: 2c6324b80de263c598218a01c9248d452cd33fd2 [file] [log] [blame]
/*
* 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.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
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<String> mReceivedMessagesAdapter;
private ArrayAdapter<String> mSentMessagesAdapter;
private ListView mReceivedMessages;
private ListView mSentMessages;
private TextView mEmptyReceivedView;
private TextView mEmptySentView;
private AlertDialog mInstructionsDialog;
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);
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.bt_messages);
setPassFailButtonClickListeners();
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<String>(this, R.layout.bt_message_row);
mReceivedMessages.setAdapter(mReceivedMessagesAdapter);
mSentMessages = (ListView) findViewById(R.id.bt_sent_messages);
mSentMessagesAdapter = new ArrayAdapter<String>(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);
setProgressBarIndeterminateVisibility(true);
showInstructionsDialog();
break;
case BluetoothChatService.STATE_CONNECTING:
setEmptyViewText(R.string.bt_connecting);
setProgressBarIndeterminateVisibility(true);
break;
case BluetoothChatService.STATE_CONNECTED:
setEmptyViewText(R.string.bt_no_messages);
setProgressBarIndeterminateVisibility(false);
hideInstructionsDialog();
sendInitialMessageFromClient();
break;
case BluetoothChatService.STATE_NONE:
setEmptyViewText(R.string.bt_no_messages);
setProgressBarIndeterminateVisibility(false);
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);
}
}