1 /*
2  * Copyright (C) 2014 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 android.app.AlertDialog;
20 import android.app.Dialog;
21 import android.app.ProgressDialog;
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothManager;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.widget.ListView;
32 
33 import com.android.cts.verifier.PassFailButtons;
34 import com.android.cts.verifier.R;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 public class BleClientTestBaseActivity extends PassFailButtons.Activity {
40     public static final String TAG = "BleClientTestBase";
41 
42     private static final boolean STEP_EXECUTION = false;
43 
44     private static final int PASS_FLAG_CONNECT = 0x1;
45     private static final int PASS_FLAG_DISCOVER = 0x2;
46     private static final int PASS_FLAG_READ_CHARACTERISTIC = 0x4;
47     private static final int PASS_FLAG_WRITE_CHARACTERISTIC = 0x8;
48     private static final int PASS_FLAG_RELIABLE_WRITE = 0x10;
49     private static final int PASS_FLAG_NOTIFY_CHARACTERISTIC = 0x20;
50     private static final int PASS_FLAG_READ_DESCRIPTOR = 0x40;
51     private static final int PASS_FLAG_WRITE_DESCRIPTOR = 0x80;
52     private static final int PASS_FLAG_READ_RSSI = 0x100;
53     private static final int PASS_FLAG_DISCONNECT = 0x200;
54     private static final int PASS_FLAG_READ_CHARACTERISTIC_NO_PERMISSION = 0x400;
55     private static final int PASS_FLAG_WRITE_CHARACTERISTIC_NO_PERMISSION = 0x800;
56     private static final int PASS_FLAG_READ_DESCRIPTOR_NO_PERMISSION = 0x1000;
57     private static final int PASS_FLAG_WRITE_DESCRIPTOR_NO_PERMISSION = 0x2000;
58     private static final int PASS_FLAG_MTU_CHANGE_23BYTES = 0x4000;
59     private static final int PASS_FLAG_INDICATE_CHARACTERISTIC = 0x8000;
60     private static final int PASS_FLAG_MTU_CHANGE_512BYTES = 0x10000;
61     private static final int PASS_FLAG_RELIABLE_WRITE_BAD_RESP = 0x20000;
62     private static final int PASS_FLAG_ALL = 0x3FFFF;
63 
64     private final int BLE_CLIENT_CONNECT = 0;
65     private final int BLE_BLE_DISVOCER_SERVICE = 1;
66     private final int BLE_READ_CHARACTERISTIC = 2;
67     private final int BLE_WRITE_CHARACTERISTIC = 3;
68     private final int BLE_REQUEST_MTU_23BYTES = 4;
69     private final int BLE_REQUEST_MTU_512BYTES = 5;
70     private final int BLE_READ_CHARACTERISTIC_NO_PERMISSION = 6;
71     private final int BLE_WRITE_CHARACTERISTIC_NO_PERMISSION = 7;
72     private final int BLE_RELIABLE_WRITE = 8;
73     private final int BLE_RELIABLE_WRITE_BAD_RESP = 9;
74     private final int BLE_NOTIFY_CHARACTERISTIC = 9;    //10;
75     private final int BLE_INDICATE_CHARACTERISTIC = 10;  //11;
76     private final int BLE_READ_DESCRIPTOR = 11; //12;
77     private final int BLE_WRITE_DESCRIPTOR = 12;    //13;
78     private final int BLE_READ_DESCRIPTOR_NO_PERMISSION = 13;   //14;
79     private final int BLE_WRITE_DESCRIPTOR_NO_PERMISSION = 14;  //15;
80     private final int BLE_READ_RSSI = 15;   //16;
81     private final int BLE_CLIENT_DISCONNECT = 16;   //17;
82 
83     private TestAdapter mTestAdapter;
84     private long mPassed;
85     private Dialog mDialog;
86     private Handler mHandler;
87 
88     @Override
onCreate(Bundle savedInstanceState)89     public void onCreate(Bundle savedInstanceState) {
90         super.onCreate(savedInstanceState);
91         setContentView(R.layout.ble_server_start);
92         setPassFailButtonClickListeners();
93         setInfoResources(R.string.ble_client_test_name,
94                 R.string.ble_client_test_info, -1);
95         getPassButton().setEnabled(false);
96 
97         mTestAdapter = new TestAdapter(this, setupTestList());
98         ListView listView = (ListView) findViewById(R.id.ble_server_tests);
99         listView.setAdapter(mTestAdapter);
100 
101         mPassed = 0;
102         mHandler = new Handler();
103     }
104 
105     @Override
onResume()106     public void onResume() {
107         super.onResume();
108 
109         IntentFilter filter = new IntentFilter();
110         filter.addAction(BleClientService.BLE_BLUETOOTH_CONNECTED);
111         filter.addAction(BleClientService.BLE_BLUETOOTH_DISCONNECTED);
112         filter.addAction(BleClientService.BLE_SERVICES_DISCOVERED);
113         filter.addAction(BleClientService.BLE_MTU_CHANGED_23BYTES);
114         filter.addAction(BleClientService.BLE_MTU_CHANGED_512BYTES);
115         filter.addAction(BleClientService.BLE_CHARACTERISTIC_READ);
116         filter.addAction(BleClientService.BLE_CHARACTERISTIC_WRITE);
117         filter.addAction(BleClientService.BLE_CHARACTERISTIC_CHANGED);
118         filter.addAction(BleClientService.BLE_DESCRIPTOR_READ);
119         filter.addAction(BleClientService.BLE_DESCRIPTOR_WRITE);
120         filter.addAction(BleClientService.BLE_RELIABLE_WRITE_COMPLETED);
121         filter.addAction(BleClientService.BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED);
122         filter.addAction(BleClientService.BLE_READ_REMOTE_RSSI);
123         filter.addAction(BleClientService.BLE_CHARACTERISTIC_READ_NOPERMISSION);
124         filter.addAction(BleClientService.BLE_CHARACTERISTIC_WRITE_NOPERMISSION);
125         filter.addAction(BleClientService.BLE_DESCRIPTOR_READ_NOPERMISSION);
126         filter.addAction(BleClientService.BLE_DESCRIPTOR_WRITE_NOPERMISSION);
127         filter.addAction(BleClientService.BLE_CHARACTERISTIC_INDICATED);
128         filter.addAction(BleClientService.BLE_BLUETOOTH_DISABLED);
129         filter.addAction(BleClientService.BLE_BLUETOOTH_MISMATCH_SECURE);
130         filter.addAction(BleClientService.BLE_BLUETOOTH_MISMATCH_INSECURE);
131         filter.addAction(BleClientService.BLE_CLIENT_ERROR);
132 
133         registerReceiver(mBroadcast, filter);
134     }
135 
136     @Override
onPause()137     public void onPause() {
138         super.onPause();
139         unregisterReceiver(mBroadcast);
140         closeDialog();
141     }
142 
closeDialog()143     private synchronized void closeDialog() {
144         if (mDialog != null) {
145             mDialog.dismiss();
146             mDialog = null;
147         }
148     }
149 
showProgressDialog()150     private synchronized void showProgressDialog() {
151         closeDialog();
152 
153         ProgressDialog dialog = new ProgressDialog(this);
154         dialog.setTitle(R.string.ble_test_running);
155         dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
156         dialog.setMessage(getString(R.string.ble_test_running_message));
157         dialog.setCanceledOnTouchOutside(false);
158         mDialog = dialog;
159         mDialog.show();
160     }
161 
setupTestList()162     private List<Integer> setupTestList() {
163         ArrayList<Integer> testList = new ArrayList<Integer>();
164         testList.add(R.string.ble_client_connect_name);
165         testList.add(R.string.ble_discover_service_name);
166         testList.add(R.string.ble_read_characteristic_name);
167         testList.add(R.string.ble_write_characteristic_name);
168         testList.add(R.string.ble_mtu_23_name);
169         testList.add(R.string.ble_mtu_512_name);
170         testList.add(R.string.ble_read_characteristic_nopermission_name);
171         testList.add(R.string.ble_write_characteristic_nopermission_name);
172         testList.add(R.string.ble_reliable_write_name);
173 //        testList.add(R.string.ble_reliable_write_bad_resp_name);
174         testList.add(R.string.ble_notify_characteristic_name);
175         testList.add(R.string.ble_indicate_characteristic_name);
176         testList.add(R.string.ble_read_descriptor_name);
177         testList.add(R.string.ble_write_descriptor_name);
178         testList.add(R.string.ble_read_descriptor_nopermission_name);
179         testList.add(R.string.ble_write_descriptor_nopermission_name);
180         testList.add(R.string.ble_read_rssi_name);
181         testList.add(R.string.ble_client_disconnect_name);
182 
183         return testList;
184     }
185 
showErrorDialog(int titleId, int messageId, boolean finish)186     private void showErrorDialog(int titleId, int messageId, boolean finish) {
187         AlertDialog.Builder builder = new AlertDialog.Builder(this)
188                 .setTitle(titleId)
189                 .setMessage(messageId);
190         if (finish) {
191             builder.setOnCancelListener(new Dialog.OnCancelListener() {
192                 @Override
193                 public void onCancel(DialogInterface dialog) {
194                     finish();
195                 }
196             });
197         }
198         builder.create().show();
199     }
200 
shouldRebootBluetoothAfterTest()201     public boolean shouldRebootBluetoothAfterTest() {
202         return false;
203     }
204 
205     private BroadcastReceiver mBroadcast = new BroadcastReceiver() {
206         @Override
207         public void onReceive(Context context, Intent intent) {
208             boolean showProgressDialog = false;
209             closeDialog();
210 
211             String action = intent.getAction();
212             String newAction = null;
213             String actionName = null;
214             final Intent startIntent = new Intent(BleClientTestBaseActivity.this, BleClientService.class);
215             switch (action) {
216             case BleClientService.BLE_BLUETOOTH_DISABLED:
217                 showErrorDialog(R.string.ble_bluetooth_disable_title, R.string.ble_bluetooth_disable_message, true);
218                 break;
219             case BleClientService.BLE_BLUETOOTH_CONNECTED:
220                 actionName = getString(R.string.ble_client_connect_name);
221                 mTestAdapter.setTestPass(BLE_CLIENT_CONNECT);
222                 mPassed |= PASS_FLAG_CONNECT;
223                 // execute service discovery test
224                 newAction = BleClientService.BLE_CLIENT_ACTION_BLE_DISVOCER_SERVICE;
225                 break;
226             case BleClientService.BLE_SERVICES_DISCOVERED:
227                 actionName = getString(R.string.ble_discover_service_name);
228                 mTestAdapter.setTestPass(BLE_BLE_DISVOCER_SERVICE);
229                 mPassed |= PASS_FLAG_DISCOVER;
230                 // execute MTU requesting test (23bytes)
231                 newAction = BleClientService.BLE_CLIENT_ACTION_READ_CHARACTERISTIC;
232                 break;
233             case BleClientService.BLE_MTU_CHANGED_23BYTES:
234                 actionName = getString(R.string.ble_mtu_23_name);
235                 mTestAdapter.setTestPass(BLE_REQUEST_MTU_23BYTES);
236                 mPassed |= PASS_FLAG_MTU_CHANGE_23BYTES;
237                 // execute MTU requesting test (512bytes)
238                 newAction = BleClientService.BLE_CLIENT_ACTION_REQUEST_MTU_512;
239                 showProgressDialog = true;
240                 break;
241             case BleClientService.BLE_MTU_CHANGED_512BYTES:
242                 actionName = getString(R.string.ble_mtu_512_name);
243                 mTestAdapter.setTestPass(BLE_REQUEST_MTU_512BYTES);
244                 mPassed |= PASS_FLAG_MTU_CHANGE_512BYTES;
245                 // execute characteristic reading test
246                 newAction = BleClientService.BLE_CLIENT_ACTION_READ_CHARACTERISTIC_NO_PERMISSION;
247                 break;
248             case BleClientService.BLE_CHARACTERISTIC_READ:
249                 actionName = getString(R.string.ble_read_characteristic_name);
250                 mTestAdapter.setTestPass(BLE_READ_CHARACTERISTIC);
251                 mPassed |= PASS_FLAG_READ_CHARACTERISTIC;
252                 // execute characteristic writing test
253                 newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC;
254                 break;
255             case BleClientService.BLE_CHARACTERISTIC_WRITE:
256                 actionName = getString(R.string.ble_write_characteristic_name);
257                 mTestAdapter.setTestPass(BLE_WRITE_CHARACTERISTIC);
258                 mPassed |= PASS_FLAG_WRITE_CHARACTERISTIC;
259                 newAction = BleClientService.BLE_CLIENT_ACTION_REQUEST_MTU_23;
260                 showProgressDialog = true;
261                 break;
262             case BleClientService.BLE_CHARACTERISTIC_READ_NOPERMISSION:
263                 actionName = getString(R.string.ble_read_characteristic_nopermission_name);
264                 mTestAdapter.setTestPass(BLE_READ_CHARACTERISTIC_NO_PERMISSION);
265                 mPassed |= PASS_FLAG_READ_CHARACTERISTIC_NO_PERMISSION;
266                 // execute unpermitted characteristic writing test
267                 newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_CHARACTERISTIC_NO_PERMISSION;
268                 break;
269             case BleClientService.BLE_CHARACTERISTIC_WRITE_NOPERMISSION:
270                 actionName = getString(R.string.ble_write_characteristic_nopermission_name);
271                 mTestAdapter.setTestPass(BLE_WRITE_CHARACTERISTIC_NO_PERMISSION);
272                 mPassed |= PASS_FLAG_WRITE_CHARACTERISTIC_NO_PERMISSION;
273                 // execute reliable write test
274                 newAction = BleClientService.BLE_CLIENT_ACTION_RELIABLE_WRITE;
275                 showProgressDialog = true;
276                 break;
277             case BleClientService.BLE_RELIABLE_WRITE_COMPLETED:
278                 actionName = getString(R.string.ble_reliable_write_name);
279                 mTestAdapter.setTestPass(BLE_RELIABLE_WRITE);
280                 mPassed |= PASS_FLAG_RELIABLE_WRITE;
281 //                newAction = BleClientService.BLE_CLIENT_ACTION_RELIABLE_WRITE_BAD_RESP;
282 
283                 // skip Reliable write (bad response) test
284                 mPassed |= PASS_FLAG_RELIABLE_WRITE_BAD_RESP;
285                 newAction = BleClientService.BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC;
286                 showProgressDialog = true;
287                 break;
288             case BleClientService.BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED: {
289                 actionName = getString(R.string.ble_reliable_write_bad_resp_name);
290                 if(!intent.hasExtra(BleClientService.EXTRA_ERROR_MESSAGE)) {
291                     mPassed |= PASS_FLAG_RELIABLE_WRITE_BAD_RESP;
292                     mTestAdapter.setTestPass(BLE_RELIABLE_WRITE_BAD_RESP);
293                 }
294                 // execute notification test
295                 newAction = BleClientService.BLE_CLIENT_ACTION_NOTIFY_CHARACTERISTIC;
296                 showProgressDialog = true;
297             }
298                 break;
299             case BleClientService.BLE_CHARACTERISTIC_CHANGED:
300                 actionName = getString(R.string.ble_notify_characteristic_name);
301                 mTestAdapter.setTestPass(BLE_NOTIFY_CHARACTERISTIC);
302                 mPassed |= PASS_FLAG_NOTIFY_CHARACTERISTIC;
303                 // execute indication test
304                 newAction = BleClientService.BLE_CLIENT_ACTION_INDICATE_CHARACTERISTIC;
305                 showProgressDialog = true;
306                 break;
307             case BleClientService.BLE_CHARACTERISTIC_INDICATED:
308                 actionName = getString(R.string.ble_indicate_characteristic_name);
309                 mTestAdapter.setTestPass(BLE_INDICATE_CHARACTERISTIC);
310                 mPassed |= PASS_FLAG_INDICATE_CHARACTERISTIC;
311                 // execute descriptor reading test
312                 newAction = BleClientService.BLE_CLIENT_ACTION_READ_DESCRIPTOR;
313                 break;
314             case BleClientService.BLE_DESCRIPTOR_READ:
315                 actionName = getString(R.string.ble_read_descriptor_name);
316                 mTestAdapter.setTestPass(BLE_READ_DESCRIPTOR);
317                 mPassed |= PASS_FLAG_READ_DESCRIPTOR;
318                 // execute descriptor writing test
319                 newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR;
320                 break;
321             case BleClientService.BLE_DESCRIPTOR_WRITE:
322                 actionName = getString(R.string.ble_write_descriptor_name);
323                 mTestAdapter.setTestPass(BLE_WRITE_DESCRIPTOR);
324                 mPassed |= PASS_FLAG_WRITE_DESCRIPTOR;
325                 // execute unpermitted descriptor reading test
326                 newAction = BleClientService.BLE_CLIENT_ACTION_READ_DESCRIPTOR_NO_PERMISSION;
327                 break;
328             case BleClientService.BLE_DESCRIPTOR_READ_NOPERMISSION:
329                 actionName = getString(R.string.ble_read_descriptor_nopermission_name);
330                 mTestAdapter.setTestPass(BLE_READ_DESCRIPTOR_NO_PERMISSION);
331                 mPassed |= PASS_FLAG_READ_DESCRIPTOR_NO_PERMISSION;
332                 // execute unpermitted descriptor writing test
333                 newAction = BleClientService.BLE_CLIENT_ACTION_WRITE_DESCRIPTOR_NO_PERMISSION;
334                 break;
335             case BleClientService.BLE_DESCRIPTOR_WRITE_NOPERMISSION:
336                 actionName = getString(R.string.ble_write_descriptor_nopermission_name);
337                 mTestAdapter.setTestPass(BLE_WRITE_DESCRIPTOR_NO_PERMISSION);
338                 mPassed |= PASS_FLAG_WRITE_DESCRIPTOR_NO_PERMISSION;
339                 // execute RSSI requesting test
340                 newAction = BleClientService.BLE_CLIENT_ACTION_READ_RSSI;
341                 break;
342             case BleClientService.BLE_READ_REMOTE_RSSI:
343                 actionName = getString(R.string.ble_read_rssi_name);
344                 mTestAdapter.setTestPass(BLE_READ_RSSI);
345                 mPassed |= PASS_FLAG_READ_RSSI;
346                 // execute disconnection test
347                 newAction = BleClientService.BLE_CLIENT_ACTION_CLIENT_DISCONNECT;
348                 break;
349             case BleClientService.BLE_BLUETOOTH_DISCONNECTED:
350                 mTestAdapter.setTestPass(BLE_CLIENT_DISCONNECT);
351                 mPassed |= PASS_FLAG_DISCONNECT;
352                 // all test done
353                 newAction = null;
354                 break;
355             case BleClientService.BLE_BLUETOOTH_MISMATCH_SECURE:
356                 showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_secure_message, true);
357                 break;
358             case BleClientService.BLE_BLUETOOTH_MISMATCH_INSECURE:
359                 showErrorDialog(R.string.ble_bluetooth_mismatch_title, R.string.ble_bluetooth_mismatch_insecure_message, true);
360                 break;
361             }
362 
363             mTestAdapter.notifyDataSetChanged();
364 
365             if (newAction != null) {
366                 startIntent.setAction(newAction);
367                 if (STEP_EXECUTION) {
368                     closeDialog();
369                     final boolean showProgressDialogValue = showProgressDialog;
370                     mDialog = new AlertDialog.Builder(BleClientTestBaseActivity.this)
371                             .setTitle(actionName)
372                             .setMessage(R.string.ble_test_finished)
373                             .setCancelable(false)
374                             .setPositiveButton(R.string.ble_test_next,
375                                     new DialogInterface.OnClickListener() {
376                                         @Override
377                                         public void onClick(DialogInterface dialog, int which) {
378                                             closeDialog();
379                                             if (showProgressDialogValue) {
380                                                 showProgressDialog();
381                                             }
382                                             startService(startIntent);
383                                         }
384                                     })
385                             .show();
386                 } else {
387                     if (showProgressDialog) {
388                         showProgressDialog();
389                     }
390                     startService(startIntent);
391                 }
392             } else {
393                 closeDialog();
394             }
395 
396             if (mPassed == PASS_FLAG_ALL) {
397                 if (shouldRebootBluetoothAfterTest()) {
398                     mBtPowerSwitcher.executeSwitching();
399                 } else {
400                     getPassButton().setEnabled(true);
401                 }
402             }
403         }
404     };
405 
406     private static final long BT_ON_DELAY = 10000;
407     private final BluetoothPowerSwitcher mBtPowerSwitcher = new BluetoothPowerSwitcher();
408     private class BluetoothPowerSwitcher extends BroadcastReceiver {
409 
410         private boolean mIsSwitching = false;
411         private BluetoothAdapter mAdapter;
412 
executeSwitching()413         public void executeSwitching() {
414             if (mAdapter == null) {
415                 BluetoothManager btMgr = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
416                 mAdapter = btMgr.getAdapter();
417             }
418 
419             if (!mIsSwitching) {
420                 IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
421                 registerReceiver(this, filter);
422                 mIsSwitching = true;
423                 mHandler.postDelayed(new Runnable() {
424                     @Override
425                     public void run() {
426                         mAdapter.disable();
427                     }
428                 }, 1000);
429                 showProgressDialog();
430             }
431         }
432 
433         @Override
onReceive(Context context, Intent intent)434         public void onReceive(Context context, Intent intent) {
435             if (intent.getAction().equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
436                 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
437                 if (state == BluetoothAdapter.STATE_OFF) {
438                     mHandler.postDelayed(new Runnable() {
439                         @Override
440                         public void run() {
441                             mAdapter.enable();
442                         }
443                     }, BT_ON_DELAY);
444                 } else if (state == BluetoothAdapter.STATE_ON) {
445                     mIsSwitching = false;
446                     unregisterReceiver(this);
447                     getPassButton().setEnabled(true);
448                     closeDialog();
449                 }
450             }
451         }
452     }
453 
454 }
455