• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MessageTestActivity(boolean secure, boolean server, UUID uuid)85     MessageTestActivity(boolean secure, boolean server, UUID uuid) {
86         mSecure = secure;
87         mServer = server;
88         mUuid = uuid;
89     }
90 
91     @Override
onCreate(Bundle savedInstanceState)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
onActivityResult(int requestCode, int resultCode, Intent data)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 
startChatService()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 
makeDiscoverable()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
handleMessage(Message msg)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 
handleStateChange(Message msg)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 
setEmptyViewText(int textId)247     private void setEmptyViewText(int textId) {
248         mEmptyReceivedView.setText(textId);
249         mEmptySentView.setText(textId);
250     }
251 
showInstructionsDialog()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 
hideInstructionsDialog()266     private void hideInstructionsDialog() {
267         if (mInstructionsDialog != null) {
268             mInstructionsDialog.hide();
269         }
270     }
271 
sendInitialMessageFromClient()272     private void sendInitialMessageFromClient() {
273         if (!mServer) {
274             sendMessage(0);
275         }
276     }
277 
sendMessage(int number)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 
handleMessageRead(Message msg)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 
addNewMessage(String msg)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 
handleMessageWrite(Message msg)311     private void handleMessageWrite(Message msg) {
312         String sentMessage = new String((byte[]) msg.obj).trim(); // Chop off delimiter
313         mSentMessagesAdapter.add(sentMessage);
314     }
315 
handleDeviceName(Message msg)316     private void handleDeviceName(Message msg) {
317         mRemoteDeviceName = msg.getData().getString(BluetoothChatService.DEVICE_NAME);
318     }
319 
handleToast(Message msg)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
onReceive(Context context, Intent intent)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 
showPairingErrorDialog()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
onDestroy()357     protected void onDestroy() {
358         super.onDestroy();
359         if (mChatService != null) {
360             mChatService.stop();
361         }
362         unregisterReceiver(mPairingActionReceiver);
363     }
364 }
365