Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.verifier.bluetooth;
     18 
     19 import com.android.cts.verifier.PassFailButtons;
     20 import com.android.cts.verifier.R;
     21 import com.android.cts.verifier.TestResult;
     22 
     23 import android.app.AlertDialog;
     24 import android.bluetooth.BluetoothAdapter;
     25 import android.bluetooth.BluetoothDevice;
     26 import android.content.BroadcastReceiver;
     27 import android.content.Context;
     28 import android.content.DialogInterface;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.os.Bundle;
     32 import android.os.Handler;
     33 import android.os.Message;
     34 import android.view.View;
     35 import android.view.View.OnClickListener;
     36 import android.widget.ArrayAdapter;
     37 import android.widget.Button;
     38 import android.widget.ListView;
     39 import android.widget.ProgressBar;
     40 import android.widget.TextView;
     41 import android.widget.Toast;
     42 
     43 import java.util.UUID;
     44 import java.util.regex.Matcher;
     45 import java.util.regex.Pattern;
     46 
     47 class MessageTestActivity extends PassFailButtons.Activity {
     48 
     49     /** Broadcast action that should only be fired when pairing for a secure connection. */
     50     private static final String ACTION_PAIRING_REQUEST =
     51             "android.bluetooth.device.action.PAIRING_REQUEST";
     52 
     53     private static final int ENABLE_BLUETOOTH_REQUEST = 1;
     54     private static final int PICK_SERVER_DEVICE_REQUEST = 2;
     55 
     56     private static final String MESSAGE_DELIMITER = "\n";
     57     private static final Pattern MESSAGE_PATTERN = Pattern.compile("Message (\\d+) to .*");
     58 
     59     private BluetoothAdapter mBluetoothAdapter;
     60     private PairingActionReceiver mPairingActionReceiver;
     61     private BluetoothChatService mChatService;
     62 
     63     private ArrayAdapter<String> mReceivedMessagesAdapter;
     64     private ArrayAdapter<String> mSentMessagesAdapter;
     65 
     66     private ListView mReceivedMessages;
     67     private ListView mSentMessages;
     68 
     69     private TextView mEmptyReceivedView;
     70     private TextView mEmptySentView;
     71 
     72     private AlertDialog mInstructionsDialog;
     73 
     74     private ProgressBar mProgressBar;
     75 
     76     private String mDeviceAddress;
     77 
     78     private final boolean mSecure;
     79     private final boolean mServer;
     80     private final UUID mUuid;
     81 
     82     private String mRemoteDeviceName = "";
     83     private StringBuilder mMessageBuffer = new StringBuilder();
     84 
     85     MessageTestActivity(boolean secure, boolean server, UUID uuid) {
     86         mSecure = secure;
     87         mServer = server;
     88         mUuid = uuid;
     89     }
     90 
     91     @Override
     92     protected void onCreate(Bundle savedInstanceState) {
     93         super.onCreate(savedInstanceState);
     94         setContentView(R.layout.bt_messages);
     95         setPassFailButtonClickListeners();
     96 
     97         mProgressBar = (ProgressBar) findViewById(R.id.bt_progress_bar);
     98 
     99         if (mServer) {
    100             setTitle(mSecure ? R.string.bt_secure_server : R.string.bt_insecure_server);
    101         } else {
    102             setTitle(mSecure ? R.string.bt_secure_client : R.string.bt_insecure_client);
    103         }
    104 
    105         mReceivedMessages = (ListView) findViewById(R.id.bt_received_messages);
    106         mReceivedMessagesAdapter = new ArrayAdapter<String>(this, R.layout.bt_message_row);
    107         mReceivedMessages.setAdapter(mReceivedMessagesAdapter);
    108 
    109         mSentMessages = (ListView) findViewById(R.id.bt_sent_messages);
    110         mSentMessagesAdapter = new ArrayAdapter<String>(this, R.layout.bt_message_row);
    111         mSentMessages.setAdapter(mSentMessagesAdapter);
    112 
    113         mEmptyReceivedView = (TextView) findViewById(R.id.bt_empty_received_messages);
    114         mReceivedMessages.setEmptyView(mEmptyReceivedView);
    115 
    116         mEmptySentView = (TextView) findViewById(R.id.bt_empty_sent_messages);
    117         mSentMessages.setEmptyView(mEmptySentView);
    118 
    119         setEmptyViewText(R.string.bt_no_messages);
    120 
    121         Button makeDiscoverableButton = (Button) findViewById(R.id.bt_make_discoverable_button);
    122         makeDiscoverableButton.setVisibility(mServer ? View.VISIBLE : View.GONE);
    123         makeDiscoverableButton.setOnClickListener(new OnClickListener() {
    124             @Override
    125             public void onClick(View v) {
    126                 makeDiscoverable();
    127             }
    128         });
    129 
    130         getPassButton().setEnabled(false);
    131 
    132         mPairingActionReceiver = new PairingActionReceiver();
    133         IntentFilter intentFilter = new IntentFilter(ACTION_PAIRING_REQUEST);
    134         registerReceiver(mPairingActionReceiver, intentFilter);
    135 
    136         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    137         if (!mServer) {
    138             Intent intent = new Intent(this, DevicePickerActivity.class);
    139             startActivityForResult(intent, PICK_SERVER_DEVICE_REQUEST);
    140         } else {
    141             if (mBluetoothAdapter.isEnabled()) {
    142                 startChatService();
    143             } else {
    144                 Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    145                 startActivityForResult(intent, ENABLE_BLUETOOTH_REQUEST);
    146             }
    147         }
    148     }
    149 
    150     @Override
    151     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    152         super.onActivityResult(requestCode, resultCode, data);
    153         switch (requestCode) {
    154             case ENABLE_BLUETOOTH_REQUEST:
    155                 if (resultCode == RESULT_OK) {
    156                     startChatService();
    157                 } else {
    158                     setResult(RESULT_CANCELED);
    159                     finish();
    160                 }
    161                 break;
    162 
    163             case PICK_SERVER_DEVICE_REQUEST:
    164                 if (resultCode == RESULT_OK) {
    165                     mDeviceAddress = data.getStringExtra(DevicePickerActivity.EXTRA_DEVICE_ADDRESS);
    166                     startChatService();
    167                 } else {
    168                     setResult(RESULT_CANCELED);
    169                     finish();
    170                 }
    171                 break;
    172         }
    173     }
    174 
    175     private void startChatService() {
    176         mChatService = new BluetoothChatService(this, new ChatHandler(), mUuid);
    177         if (mServer) {
    178             mChatService.start(mSecure);
    179         } else {
    180             BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mDeviceAddress);
    181             mChatService.connect(device, mSecure);
    182         }
    183     }
    184 
    185     private void makeDiscoverable() {
    186         if (mBluetoothAdapter.getScanMode() !=
    187                 BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
    188             Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
    189             intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 30);
    190             startActivity(intent);
    191         }
    192     }
    193 
    194     private class ChatHandler extends Handler {
    195         @Override
    196         public void handleMessage(Message msg) {
    197             super.handleMessage(msg);
    198             switch (msg.what) {
    199                 case BluetoothChatService.MESSAGE_STATE_CHANGE:
    200                     handleStateChange(msg);
    201                     break;
    202                 case BluetoothChatService.MESSAGE_READ:
    203                     handleMessageRead(msg);
    204                     break;
    205                 case BluetoothChatService.MESSAGE_WRITE:
    206                     handleMessageWrite(msg);
    207                     break;
    208                 case BluetoothChatService.MESSAGE_DEVICE_NAME:
    209                     handleDeviceName(msg);
    210                     break;
    211                 case BluetoothChatService.MESSAGE_TOAST:
    212                     handleToast(msg);
    213                     break;
    214             }
    215         }
    216     }
    217 
    218     private void handleStateChange(Message msg) {
    219         int state = msg.arg1;
    220         switch (state) {
    221             case BluetoothChatService.STATE_LISTEN:
    222                 setEmptyViewText(R.string.bt_waiting);
    223                 mProgressBar.setVisibility(View.VISIBLE);
    224                 showInstructionsDialog();
    225                 break;
    226 
    227             case BluetoothChatService.STATE_CONNECTING:
    228                 setEmptyViewText(R.string.bt_connecting);
    229                 mProgressBar.setVisibility(View.VISIBLE);
    230                 break;
    231 
    232             case BluetoothChatService.STATE_CONNECTED:
    233                 setEmptyViewText(R.string.bt_no_messages);
    234                 mProgressBar.setVisibility(View.INVISIBLE);
    235 
    236                 hideInstructionsDialog();
    237                 sendInitialMessageFromClient();
    238                 break;
    239 
    240             case BluetoothChatService.STATE_NONE:
    241                 setEmptyViewText(R.string.bt_no_messages);
    242                 mProgressBar.setVisibility(View.INVISIBLE);
    243                 break;
    244         }
    245     }
    246 
    247     private void setEmptyViewText(int textId) {
    248         mEmptyReceivedView.setText(textId);
    249         mEmptySentView.setText(textId);
    250     }
    251 
    252     private void showInstructionsDialog() {
    253         if (mInstructionsDialog == null) {
    254             mInstructionsDialog = new AlertDialog.Builder(this)
    255                     .setIcon(android.R.drawable.ic_dialog_info)
    256                     .setTitle(getString(R.string.bt_waiting))
    257                     .setMessage(getString(mSecure
    258                             ? R.string.bt_secure_server_instructions
    259                             : R.string.bt_insecure_server_instructions))
    260                     .setPositiveButton(android.R.string.ok, null)
    261                     .create();
    262         }
    263         mInstructionsDialog.show();
    264     }
    265 
    266     private void hideInstructionsDialog() {
    267         if (mInstructionsDialog != null) {
    268             mInstructionsDialog.hide();
    269         }
    270     }
    271 
    272     private void sendInitialMessageFromClient() {
    273         if (!mServer) {
    274             sendMessage(0);
    275         }
    276     }
    277 
    278     private void sendMessage(int number) {
    279         String message = "Message " + number + " to "
    280                 + (mRemoteDeviceName != null ? mRemoteDeviceName : "")
    281                 + MESSAGE_DELIMITER;
    282         mChatService.write(message.getBytes());
    283     }
    284 
    285     private void handleMessageRead(Message msg) {
    286         String chunk = new String((byte[]) msg.obj, 0, msg.arg1);
    287         mMessageBuffer.append(chunk);
    288 
    289         int delimiterIndex = mMessageBuffer.indexOf(MESSAGE_DELIMITER);
    290         if (delimiterIndex != -1) {
    291             String message = mMessageBuffer.substring(0, delimiterIndex); // Chop off delimiter
    292             mMessageBuffer.delete(0, delimiterIndex + 1);
    293             addNewMessage(message);
    294         }
    295     }
    296 
    297     private void addNewMessage(String msg) {
    298         mReceivedMessagesAdapter.add(msg);
    299         Matcher matcher = MESSAGE_PATTERN.matcher(msg);
    300         if (matcher.matches()) {
    301             int number = Integer.valueOf(matcher.group(1));
    302             if (mServer && number == 10 || !mServer && number == 11) {
    303                 getPassButton().setEnabled(true);
    304             }
    305             if (number <= 10) {
    306                 sendMessage(number + 1);
    307             }
    308         }
    309     }
    310 
    311     private void handleMessageWrite(Message msg) {
    312         String sentMessage = new String((byte[]) msg.obj).trim(); // Chop off delimiter
    313         mSentMessagesAdapter.add(sentMessage);
    314     }
    315 
    316     private void handleDeviceName(Message msg) {
    317         mRemoteDeviceName = msg.getData().getString(BluetoothChatService.DEVICE_NAME);
    318     }
    319 
    320     private void handleToast(Message msg) {
    321         String toast = msg.getData().getString(BluetoothChatService.TOAST);
    322         Toast.makeText(this, toast, Toast.LENGTH_LONG).show();
    323     }
    324 
    325     class PairingActionReceiver extends BroadcastReceiver {
    326         @Override
    327         public void onReceive(Context context, Intent intent) {
    328             if (!mSecure && ACTION_PAIRING_REQUEST.equals(intent.getAction())) {
    329                 runOnUiThread(new Runnable() {
    330                     @Override
    331                     public void run() {
    332                         showPairingErrorDialog();
    333                     }
    334                 });
    335             }
    336         }
    337     }
    338 
    339     private void showPairingErrorDialog() {
    340         new AlertDialog.Builder(MessageTestActivity.this)
    341             .setIcon(android.R.drawable.ic_dialog_alert)
    342             .setTitle(R.string.bt_insecure_pairing_error_title)
    343             .setMessage(R.string.bt_insecure_pairing_error_message)
    344             .setPositiveButton(android.R.string.ok,
    345                     new DialogInterface.OnClickListener() {
    346                 @Override
    347                 public void onClick(DialogInterface dialog, int which) {
    348                     TestResult.setFailedResult(MessageTestActivity.this, getTestId(), null);
    349                     finish();
    350                 }
    351             })
    352             .setCancelable(false)
    353             .show();
    354     }
    355 
    356     @Override
    357     protected void onDestroy() {
    358         super.onDestroy();
    359         if (mChatService != null) {
    360             mChatService.stop();
    361         }
    362         unregisterReceiver(mPairingActionReceiver);
    363     }
    364 }
    365