1 /*
2  * Copyright (C) 2016 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.Service;
20 import android.bluetooth.BluetoothDevice;
21 import android.bluetooth.BluetoothGatt;
22 import android.bluetooth.BluetoothGattCharacteristic;
23 import android.bluetooth.BluetoothGattDescriptor;
24 import android.bluetooth.BluetoothGattServer;
25 import android.bluetooth.BluetoothGattServerCallback;
26 import android.bluetooth.BluetoothGattService;
27 import android.bluetooth.BluetoothManager;
28 import android.bluetooth.BluetoothProfile;
29 import android.bluetooth.le.AdvertiseCallback;
30 import android.bluetooth.le.AdvertiseData;
31 import android.bluetooth.le.AdvertiseSettings;
32 import android.bluetooth.le.BluetoothLeAdvertiser;
33 import android.content.Context;
34 import android.content.Intent;
35 import android.os.Handler;
36 import android.os.IBinder;
37 import android.os.ParcelUuid;
38 import android.util.Log;
39 import android.widget.Toast;
40 
41 import java.util.Timer;
42 import java.util.TimerTask;
43 import java.util.UUID;
44 
45 public class BleConnectionPriorityServerService extends Service {
46     public static final boolean DEBUG = true;
47     public static final String TAG = "BlePriorityServer";
48 
49     public static final String ACTION_BLUETOOTH_DISABLED =
50             "com.android.cts.verifier.bluetooth.action.BLUETOOTH_DISABLED";
51 
52     public static final String ACTION_CONNECTION_PRIORITY_FINISH =
53             "com.android.cts.verifier.bluetooth.action.ACTION_CONNECTION_PRIORITY_FINISH";
54 
55     public static final String ACTION_START_CONNECTION_PRIORITY_TEST =
56             "com.android.cts.verifier.bluetooth.action.ACTION_START_CONNECTION_PRIORITY_TEST";
57 
58     public static final UUID ADV_SERVICE_UUID=
59             UUID.fromString("00002222-0000-1000-8000-00805f9b34fb");
60 
61     private BluetoothManager mBluetoothManager;
62     private BluetoothGattServer mGattServer;
63     private BluetoothGattService mService;
64     private BluetoothDevice mDevice;
65     private Handler mHandler;
66     private BluetoothLeAdvertiser mAdvertiser;
67     private long mReceiveWriteCount;
68 
69     private int interval_low = 0;
70     private int interval_balanced = 0;
71     private int interval_high = 0;
72 
73     @Override
onCreate()74     public void onCreate() {
75         super.onCreate();
76 
77         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
78         mAdvertiser = mBluetoothManager.getAdapter().getBluetoothLeAdvertiser();
79         mGattServer = mBluetoothManager.openGattServer(this, mCallbacks);
80         mDevice = null;
81         mHandler = new Handler();
82 
83         if (!mBluetoothManager.getAdapter().isEnabled()) {
84             notifyBluetoothDisabled();
85         } else if (mGattServer == null) {
86             notifyOpenFail();
87         } else if (mAdvertiser == null) {
88             notifyAdvertiseUnsupported();
89         } else {
90             startAdvertise();
91         }
92     }
93 
94     @Override
onDestroy()95     public void onDestroy() {
96         super.onDestroy();
97 
98         stopAdvertise();
99         if (mGattServer == null) {
100             return;
101         }
102         if (mDevice != null) {
103             mGattServer.cancelConnection(mDevice);
104         }
105         mGattServer.clearServices();
106         mGattServer.close();
107     }
108 
109     @Override
onBind(Intent intent)110     public IBinder onBind(Intent intent) {
111         return null;
112     }
113 
114     @Override
onStartCommand(Intent intent, int flags, int startId)115     public int onStartCommand(Intent intent, int flags, int startId) {
116         return START_NOT_STICKY;
117     }
118 
notifyBluetoothDisabled()119     private void notifyBluetoothDisabled() {
120         if (DEBUG) {
121             Log.d(TAG, "notifyBluetoothDisabled");
122         }
123         Intent intent = new Intent(ACTION_BLUETOOTH_DISABLED);
124         sendBroadcast(intent);
125     }
126 
notifyTestStart()127     private void notifyTestStart() {
128         Intent intent = new Intent(BleConnectionPriorityServerService.ACTION_START_CONNECTION_PRIORITY_TEST);
129         sendBroadcast(intent);
130     }
131 
notifyOpenFail()132     private void notifyOpenFail() {
133         if (DEBUG) {
134             Log.d(TAG, "notifyOpenFail");
135         }
136         Intent intent = new Intent(BleServerService.BLE_OPEN_FAIL);
137         sendBroadcast(intent);
138     }
139 
notifyAdvertiseUnsupported()140     private void notifyAdvertiseUnsupported() {
141         if (DEBUG) {
142             Log.d(TAG, "notifyAdvertiseUnsupported");
143         }
144         Intent intent = new Intent(BleServerService.BLE_ADVERTISE_UNSUPPORTED);
145         sendBroadcast(intent);
146     }
147 
notifyConnected()148     private void notifyConnected() {
149         if (DEBUG) {
150             Log.d(TAG, "notifyConnected");
151         }
152     }
153 
notifyDisconnected()154     private void notifyDisconnected() {
155         if (DEBUG) {
156             Log.d(TAG, "notifyDisconnected");
157         }
158     }
159 
showMessage(final String msg)160     private void showMessage(final String msg) {
161         mHandler.post(new Runnable() {
162             @Override
163             public void run() {
164                 Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
165             }
166         });
167     }
168 
169     private final BluetoothGattServerCallback mCallbacks = new BluetoothGattServerCallback() {
170         @Override
171         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
172             if (DEBUG) {
173                 Log.d(TAG, "onConnectionStateChange: newState=" + newState);
174             }
175             if (status == BluetoothGatt.GATT_SUCCESS) {
176                 if (newState == BluetoothProfile.STATE_CONNECTED) {
177                     mDevice = device;
178                     notifyConnected();
179                 } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
180                     notifyDisconnected();
181                     mDevice = null;
182                 }
183             }
184 
185             //onConnectionUpdated is hidden callback, can't be marked as @Override.
186             // We must have a call to it, otherwise compiler will delete it during optimization.
187             if (status == 0xFFEFFEE) {
188                 // This should never execute, but will make compiler not remove onConnectionUpdated
189                 onConnectionUpdated(null, 0, 0, 0, 0);
190                 throw new IllegalStateException("This should never happen!");
191             }
192 
193         }
194 
195         // @Override uncomment once this becomes public API
196         public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout, int status) {
197             Log.i(TAG, "onConnectionUpdated() status=" + status + ", interval=" + interval);
198 
199             if (status != 0) {
200                interval_low = interval_balanced = interval_high = 0;
201                return;
202             }
203 
204             // since we don't know when the test started, wait for three descending interval values.
205             // Even though conneciton is updated by service discovery, it never happen three times
206             // descending in any scenario.
207 
208             // shift all values
209             interval_low = interval_balanced;
210             interval_balanced = interval_high;
211             interval_high = interval;
212 
213             // If we end up with three descending values, test is passed.
214             if (interval_low > interval_balanced && interval_balanced > interval_high) {
215                 showMessage("intervals: " + interval_low +" > " + interval_balanced + " > " + interval_high);
216                 notifyMeasurementFinished();
217             }
218         }
219     };
220 
notifyMeasurementFinished()221     private void notifyMeasurementFinished() {
222         Intent intent = new Intent();
223         intent.setAction(ACTION_CONNECTION_PRIORITY_FINISH);
224         sendBroadcast(intent);
225     }
226 
227     private final AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback() {
228         @Override
229         public void onStartFailure(int errorCode) {
230             super.onStartFailure(errorCode);
231             if (errorCode == ADVERTISE_FAILED_FEATURE_UNSUPPORTED) {
232                 notifyAdvertiseUnsupported();
233             } else {
234                 notifyOpenFail();
235             }
236         }
237     };
238 
startAdvertise()239     private void startAdvertise() {
240         if (DEBUG) {
241             Log.d(TAG, "startAdvertise");
242         }
243         AdvertiseData data = new AdvertiseData.Builder()
244                 .addServiceData(new ParcelUuid(ADV_SERVICE_UUID), new byte[]{1, 2, 3})
245                 .addServiceUuid(new ParcelUuid(ADV_SERVICE_UUID))
246                 .build();
247         AdvertiseSettings setting = new AdvertiseSettings.Builder()
248                 .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
249                 .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
250                 .setConnectable(true)
251                 .build();
252         mAdvertiser.startAdvertising(setting, data, mAdvertiseCallback);
253     }
254 
stopAdvertise()255     private void stopAdvertise() {
256         if (DEBUG) {
257             Log.d(TAG, "stopAdvertise");
258         }
259         if (mAdvertiser != null) {
260             mAdvertiser.stopAdvertising(mAdvertiseCallback);
261         }
262     }
263 
264 }
265