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