blob: d649d108dfccd03b17c597a7476feb4ff9ea3393 [file] [log] [blame]
Dan Harms27139432020-01-06 16:21:11 -08001/*
2 * Copyright (C) 2019 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
17package com.android.car.connecteddevice.ble;
18
19import static com.android.car.connecteddevice.util.SafeLog.logd;
20import static com.android.car.connecteddevice.util.SafeLog.logw;
21
22import android.annotation.NonNull;
23import android.annotation.Nullable;
24import android.bluetooth.BluetoothDevice;
25import android.bluetooth.BluetoothGatt;
26
27import com.android.car.connecteddevice.storage.ConnectedDeviceStorage;
28import com.android.car.connecteddevice.util.ThreadSafeCallbacks;
29
30import java.util.concurrent.CopyOnWriteArraySet;
31import java.util.concurrent.Executor;
32
33/**
34 * Generic BLE manager for a car that keeps track of connected devices and their associated
35 * callbacks.
36 */
37public abstract class CarBleManager {
38
39 private static final String TAG = "CarBleManager";
40
41 final ConnectedDeviceStorage mStorage;
42
43 final CopyOnWriteArraySet<BleDevice> mConnectedDevices = new CopyOnWriteArraySet<>();
44
45 final ThreadSafeCallbacks<Callback> mCallbacks = new ThreadSafeCallbacks<>();
46
47 protected CarBleManager(@NonNull ConnectedDeviceStorage connectedDeviceStorage) {
48 mStorage = connectedDeviceStorage;
49 }
50
51 /**
52 * Initialize and start the manager.
53 */
54 public void start() {
55 }
56
57 /**
58 * Stop the manager and clean up.
59 */
60 public void stop() {
61 for (BleDevice device : mConnectedDevices) {
62 if (device.mGatt != null) {
63 device.mGatt.close();
64 }
65 }
66 mConnectedDevices.clear();
67 mCallbacks.clear();
68 }
69
70 /**
71 * Register a {@link Callback} to be notified on the {@link Executor}.
72 */
73 public void registerCallback(@NonNull Callback callback, @NonNull Executor executor) {
74 mCallbacks.add(callback, executor);
75 }
76
77 /**
78 * Unregister a callback.
79 *
80 * @param callback The {@link Callback} to unregister.
81 */
82 public void unregisterCallback(@NonNull Callback callback) {
83 mCallbacks.remove(callback);
84 }
85
86 /**
87 * Send a message to a connected device.
88 *
89 * @param deviceId Id of connected device.
90 * @param message {@link DeviceMessage} to send.
91 */
92 public void sendMessage(@NonNull String deviceId, @NonNull DeviceMessage message) {
93 BleDevice device = getConnectedDevice(deviceId);
94 if (device == null) {
95 logw(TAG, "Attempted to send message to unknown device $deviceId. Ignored.");
96 return;
97 }
98
99 sendMessage(device, message);
100 }
101
102 /**
103 * Send a message to a connected device.
104 *
105 * @param device The connected {@link BleDevice}.
106 * @param message {@link DeviceMessage} to send.
107 */
108 public void sendMessage(@NonNull BleDevice device, @NonNull DeviceMessage message) {
109 String deviceId = device.mDeviceId;
110 if (deviceId == null) {
111 deviceId = "Unidentified device";
112 }
113
114 logd(TAG, "Writing " + message.getMessage().length + " bytes to " + deviceId + ".");
115
116
117 if (message.isMessageEncrypted()) {
118 device.mSecureChannel.sendEncryptedMessage(message);
119 } else {
120 device.mSecureChannel.getStream().writeMessage(message);
121 }
122 }
123
124 /**
125 * Get the {@link BleDevice} with matching {@link BluetoothGatt} if available. Returns
126 * {@code null} if no matches are found.
127 */
128 @Nullable
129 BleDevice getConnectedDevice(@NonNull BluetoothGatt gatt) {
130 for (BleDevice device : mConnectedDevices) {
131 if (device.mGatt == gatt) {
132 return device;
133 }
134 }
135
136 return null;
137 }
138
139 /**
140 * Get the {@link BleDevice} with matching {@link BluetoothDevice} if available. Returns
141 * {@code null} if no matches are found.
142 */
143 @Nullable
144 BleDevice getConnectedDevice(@NonNull BluetoothDevice device) {
145 for (BleDevice connectedDevice : mConnectedDevices) {
146 if (device.equals(connectedDevice.mDevice)) {
147 return connectedDevice;
148 }
149 }
150
151 return null;
152 }
153
154 /**
155 * Get the {@link BleDevice} with matching device id if available. Returns {@code null} if
156 * no matches are found.
157 */
158 @Nullable
159 BleDevice getConnectedDevice(@NonNull String deviceId) {
160 for (BleDevice device : mConnectedDevices) {
161 if (deviceId.equals(device.mDeviceId)) {
162 return device;
163 }
164 }
165
166 return null;
167 }
168
169 /** Add the {@link BleDevice} that has connected. */
170 void addConnectedDevice(@NonNull BleDevice device) {
171 mConnectedDevices.add(device);
172 }
173
174 /** Return the number of devices currently connected. */
175 int getConnectedDevicesCount() {
176 return mConnectedDevices.size();
177 }
178
179 /** Remove [@link BleDevice} that has been disconnected. */
180 void removeConnectedDevice(@NonNull BleDevice device) {
181 mConnectedDevices.remove(device);
182 }
183
184 /** State for a connected device. */
185 enum BleDeviceState {
186 CONNECTING,
187 PENDING_VERIFICATION,
188 CONNECTED,
189 UNKNOWN
190 }
191
192 /**
193 * Container class to hold information about a connected device.
194 */
195 static class BleDevice {
196
197 BluetoothDevice mDevice;
198 BluetoothGatt mGatt;
199 BleDeviceState mState;
200 String mDeviceId;
201 SecureBleChannel mSecureChannel;
202
203 BleDevice(@NonNull BluetoothDevice device, @Nullable BluetoothGatt gatt) {
204 mDevice = device;
205 mGatt = gatt;
206 mState = BleDeviceState.UNKNOWN;
207 }
208 }
209
210 /**
211 * Callback for triggered events from {@link CarBleManager}.
212 */
213 public interface Callback {
214 /**
215 * Triggered when device is connected and device id retrieved. Device is now ready to
216 * receive messages.
217 *
218 * @param deviceId Id of device that has connected.
219 */
220 void onDeviceConnected(@NonNull String deviceId);
221
222 /**
223 * Triggered when device is disconnected.
224 *
225 * @param deviceId Id of device that has disconnected.
226 */
227 void onDeviceDisconnected(@NonNull String deviceId);
228
229 /**
230 * Triggered when device has established encryption for secure communication.
231 *
232 * @param deviceId Id of device that has established encryption.
233 */
234 void onSecureChannelEstablished(@NonNull String deviceId);
235
236 /**
237 * Triggered when a new message is received.
238 *
239 * @param deviceId Id of the device that sent the message.
240 * @param message {@link DeviceMessage} received.
241 */
242 void onMessageReceived(@NonNull String deviceId, @NonNull DeviceMessage message);
243
244 /**
245 * Triggered when an error when establishing the secure channel.
246 *
247 * @param deviceId Id of the device that experienced the error.
248 */
249 void onSecureChannelError(@NonNull String deviceId);
250 }
251}