1 /*
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 
17 package com.android.car.trust;
18 
19 import android.annotation.IntDef;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothGattCharacteristic;
23 import android.bluetooth.BluetoothGattDescriptor;
24 import android.bluetooth.BluetoothGattService;
25 import android.bluetooth.le.AdvertiseCallback;
26 import android.bluetooth.le.AdvertiseData;
27 import android.bluetooth.le.AdvertiseSettings;
28 import android.content.Context;
29 import android.os.Handler;
30 import android.os.Looper;
31 import android.os.ParcelUuid;
32 import android.util.Log;
33 
34 import androidx.annotation.Nullable;
35 
36 import com.android.car.BLEStreamProtos.BLEMessageProto.BLEMessage;
37 import com.android.car.BLEStreamProtos.BLEOperationProto.OperationType;
38 import com.android.car.BLEStreamProtos.VersionExchangeProto.BLEVersionExchange;
39 import com.android.car.CarLocalServices;
40 import com.android.car.R;
41 import com.android.car.Utils;
42 import com.android.car.protobuf.InvalidProtocolBufferException;
43 
44 import java.io.IOException;
45 import java.lang.annotation.Retention;
46 import java.lang.annotation.RetentionPolicy;
47 import java.util.LinkedList;
48 import java.util.List;
49 import java.util.Queue;
50 import java.util.UUID;
51 import java.util.concurrent.TimeUnit;
52 
53 /**
54  * A BLE Service that is used for communicating with the trusted peer device. This extends from a
55  * more generic {@link BleManager} and has more context on the BLE requirements for the Trusted
56  * device feature. It has knowledge on the GATT services and characteristics that are specific to
57  * the Trusted Device feature.
58  */
59 class CarTrustAgentBleManager extends BleManager {
60     private static final String TAG = "CarTrustBLEManager";
61 
62     /**
63      * The UUID of the Client Characteristic Configuration Descriptor. This descriptor is
64      * responsible for specifying if a characteristic can be subscribed to for notifications.
65      *
66      * @see <a href="https://www.bluetooth.com/specifications/gatt/descriptors/">
67      * GATT Descriptors</a>
68      */
69     private static final UUID CLIENT_CHARACTERISTIC_CONFIG =
70             UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
71 
72     /**
73      * Reserved bytes for an ATT write request payload.
74      *
75      * <p>The attribute protocol uses 3 bytes to encode the command type and attribute ID. These
76      * bytes need to be subtracted from the reported MTU size and the resulting value will
77      * represent the total amount of bytes that can be sent in a write.
78      */
79     private static final int ATT_PAYLOAD_RESERVED_BYTES = 3;
80 
81     /** @hide */
82     @IntDef(prefix = {"TRUSTED_DEVICE_OPERATION_"}, value = {
83             TRUSTED_DEVICE_OPERATION_NONE,
84             TRUSTED_DEVICE_OPERATION_ENROLLMENT,
85             TRUSTED_DEVICE_OPERATION_UNLOCK
86     })
87     @Retention(RetentionPolicy.SOURCE)
88     public @interface TrustedDeviceOperation {
89     }
90 
91     private static final int TRUSTED_DEVICE_OPERATION_NONE = 0;
92     private static final int TRUSTED_DEVICE_OPERATION_ENROLLMENT = 1;
93     private static final int TRUSTED_DEVICE_OPERATION_UNLOCK = 2;
94     private static final long BLE_MESSAGE_RETRY_DELAY_MS = TimeUnit.SECONDS.toMillis(2);
95     private static final int BLE_MESSAGE_RETRY_LIMIT = 20;
96 
97     @TrustedDeviceOperation
98     private int mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_NONE;
99     private CarTrustedDeviceService mCarTrustedDeviceService;
100     private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
101     private CarTrustAgentUnlockService mCarTrustAgentUnlockService;
102     private String mOriginalBluetoothName;
103     private byte[] mUniqueId;
104     private String mEnrollmentDeviceName;
105 
106     /**
107      * The maximum amount of bytes that can be written over BLE.
108      *
109      * <p>This initial value is 20 because BLE has a default write of 23 bytes. However, 3 bytes
110      * are subtracted due to bytes being reserved for the command type and attribute ID.
111      *
112      * @see #ATT_PAYLOAD_RESERVED_BYTES
113      */
114     private int mMaxWriteSize = 20;
115 
116     // Enrollment Service and Characteristic UUIDs
117     private UUID mEnrollmentServiceUuid;
118     private UUID mEnrollmentClientWriteUuid;
119     private UUID mEnrollmentServerWriteUuid;
120     private BluetoothGattService mEnrollmentGattService;
121 
122     // Unlock Service and Characteristic UUIDs
123     private UUID mUnlockServiceUuid;
124     private UUID mUnlockClientWriteUuid;
125     private UUID mUnlockServerWriteUuid;
126     private BluetoothGattService mUnlockGattService;
127 
128     private Queue<BLEMessage> mMessageQueue = new LinkedList<>();
129     private BLEMessagePayloadStream mBleMessagePayloadStream = new BLEMessagePayloadStream();
130 
131     // This is a boolean because there's only one supported version.
132     private boolean mIsVersionExchanged;
133     private int mBleMessageRetryStartCount;
134     private Handler mHandler = new Handler(Looper.getMainLooper());
135     private Runnable mSendRepeatedBleMessage;
136 
CarTrustAgentBleManager(Context context)137     CarTrustAgentBleManager(Context context) {
138         super(context);
139     }
140 
141     // Overriding some of the {@link BLEManager} methods to be specific for Trusted Device feature.
142     @Override
onRemoteDeviceConnected(BluetoothDevice device)143     public void onRemoteDeviceConnected(BluetoothDevice device) {
144         if (getTrustedDeviceService() == null) {
145             return;
146         }
147 
148         // Retrieving device name only happens in enrollment, the retrieved device name will be
149         // stored in sharedPreference for further use.
150         if (mCurrentTrustedDeviceOperation == TRUSTED_DEVICE_OPERATION_ENROLLMENT
151                 && device.getName() == null) {
152             retrieveDeviceName(device);
153         }
154 
155         mMessageQueue.clear();
156         mIsVersionExchanged = false;
157         getTrustedDeviceService().onRemoteDeviceConnected(device);
158         if (mSendRepeatedBleMessage != null) {
159             mHandler.removeCallbacks(mSendRepeatedBleMessage);
160             mSendRepeatedBleMessage = null;
161         }
162     }
163 
164     @Override
onRemoteDeviceDisconnected(BluetoothDevice device)165     public void onRemoteDeviceDisconnected(BluetoothDevice device) {
166         if (getTrustedDeviceService() != null) {
167             getTrustedDeviceService().onRemoteDeviceDisconnected(device);
168         }
169 
170         mMessageQueue.clear();
171         mIsVersionExchanged = false;
172         mBleMessagePayloadStream.reset();
173 
174         if (mSendRepeatedBleMessage != null) {
175             mHandler.removeCallbacks(mSendRepeatedBleMessage);
176         }
177         mSendRepeatedBleMessage = null;
178     }
179 
180     @Override
onDeviceNameRetrieved(@ullable String deviceName)181     protected void onDeviceNameRetrieved(@Nullable String deviceName) {
182         if (getTrustedDeviceService() != null) {
183             getTrustedDeviceService().onDeviceNameRetrieved(deviceName);
184         }
185     }
186 
187     @Override
onMtuSizeChanged(int size)188     protected void onMtuSizeChanged(int size) {
189         mMaxWriteSize = size - ATT_PAYLOAD_RESERVED_BYTES;
190 
191         if (Log.isLoggable(TAG, Log.DEBUG)) {
192             Log.d(TAG, "MTU size changed to: " + size
193                     + "; setting max payload size to: " + mMaxWriteSize);
194         }
195     }
196 
197     @Override
onCharacteristicWrite(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)198     public void onCharacteristicWrite(BluetoothDevice device, int requestId,
199             BluetoothGattCharacteristic characteristic, boolean preparedWrite,
200             boolean responseNeeded, int offset, byte[] value) {
201         UUID uuid = characteristic.getUuid();
202         if (Log.isLoggable(TAG, Log.DEBUG)) {
203             Log.d(TAG, "onCharacteristicWrite received uuid: " + uuid);
204         }
205 
206         if (!mIsVersionExchanged) {
207             resolveBLEVersion(device, value, uuid);
208             return;
209         }
210 
211         BLEMessage message;
212         try {
213             message = BLEMessage.parseFrom(value);
214         } catch (InvalidProtocolBufferException e) {
215             Log.e(TAG, "Can not parse BLE message", e);
216             return;
217         }
218 
219         if (message.getOperation() == OperationType.ACK) {
220             handleClientAckMessage(device, uuid);
221             return;
222         }
223 
224         // This write operation is not thread safe individually, but is guarded by the callback
225         // here.
226         try {
227             mBleMessagePayloadStream.write(message);
228         } catch (IOException e) {
229             Log.e(TAG, "Can write the BLE message's payload", e);
230             return;
231         }
232 
233         if (!mBleMessagePayloadStream.isComplete()) {
234             // If it's not complete, make sure the client knows that this message was received.
235             sendAcknowledgmentMessage(device, uuid);
236             return;
237         }
238 
239         if (uuid.equals(mEnrollmentClientWriteUuid)) {
240             if (getEnrollmentService() != null) {
241                 getEnrollmentService().onEnrollmentDataReceived(
242                         mBleMessagePayloadStream.toByteArray());
243             }
244         } else if (uuid.equals(mUnlockClientWriteUuid)) {
245             if (getUnlockService() != null) {
246                 getUnlockService().onUnlockDataReceived(mBleMessagePayloadStream.toByteArray());
247             }
248         }
249 
250         mBleMessagePayloadStream.reset();
251     }
252 
253     @Override
onCharacteristicRead(BluetoothDevice device, int requestId, int offset, final BluetoothGattCharacteristic characteristic)254     public void onCharacteristicRead(BluetoothDevice device, int requestId, int offset,
255             final BluetoothGattCharacteristic characteristic) {
256         // Ignored read requests.
257     }
258 
259     @Nullable
getTrustedDeviceService()260     private CarTrustedDeviceService getTrustedDeviceService() {
261         if (mCarTrustedDeviceService == null) {
262             mCarTrustedDeviceService = CarLocalServices.getService(CarTrustedDeviceService.class);
263         }
264         return mCarTrustedDeviceService;
265     }
266 
267     @Nullable
getEnrollmentService()268     private CarTrustAgentEnrollmentService getEnrollmentService() {
269         if (mCarTrustAgentEnrollmentService != null) {
270             return mCarTrustAgentEnrollmentService;
271         }
272 
273         if (getTrustedDeviceService() != null) {
274             mCarTrustAgentEnrollmentService =
275                     getTrustedDeviceService().getCarTrustAgentEnrollmentService();
276         }
277         return mCarTrustAgentEnrollmentService;
278     }
279 
280     @Nullable
getUnlockService()281     private CarTrustAgentUnlockService getUnlockService() {
282         if (mCarTrustAgentUnlockService != null) {
283             return mCarTrustAgentUnlockService;
284         }
285 
286         if (getTrustedDeviceService() != null) {
287             mCarTrustAgentUnlockService = getTrustedDeviceService().getCarTrustAgentUnlockService();
288         }
289         return mCarTrustAgentUnlockService;
290     }
291 
292     @Nullable
getUniqueId()293     private byte[] getUniqueId() {
294         if (mUniqueId != null) {
295             return mUniqueId;
296         }
297 
298         if (getTrustedDeviceService() != null && getTrustedDeviceService().getUniqueId() != null) {
299             mUniqueId = Utils.uuidToBytes(getTrustedDeviceService().getUniqueId());
300         }
301         return mUniqueId;
302     }
303 
304     @Nullable
getEnrollmentDeviceName()305     private String getEnrollmentDeviceName() {
306         if (mEnrollmentDeviceName != null) {
307             return mEnrollmentDeviceName;
308         }
309 
310         if (getTrustedDeviceService() != null) {
311             mEnrollmentDeviceName = getTrustedDeviceService().getEnrollmentDeviceName();
312         }
313         return mEnrollmentDeviceName;
314     }
315 
resolveBLEVersion(BluetoothDevice device, byte[] value, UUID clientCharacteristicUUID)316     private void resolveBLEVersion(BluetoothDevice device, byte[] value,
317             UUID clientCharacteristicUUID) {
318         BluetoothGattCharacteristic characteristic =
319                 getCharacteristicForWrite(clientCharacteristicUUID);
320 
321         if (characteristic == null) {
322             Log.e(TAG, "Invalid UUID (" + clientCharacteristicUUID
323                     + ") during version exchange; disconnecting from remote device.");
324             disconnectRemoteDevice();
325             return;
326         }
327 
328         BLEVersionExchange deviceVersion;
329         try {
330             deviceVersion = BLEVersionExchange.parseFrom(value);
331         } catch (InvalidProtocolBufferException e) {
332             disconnectRemoteDevice();
333             Log.e(TAG, "Could not parse version exchange message", e);
334             return;
335         }
336 
337         if (!BLEVersionExchangeResolver.hasSupportedVersion(deviceVersion)) {
338             Log.e(TAG, "No supported version found during version exchange.");
339             disconnectRemoteDevice();
340             return;
341         }
342 
343         BLEVersionExchange headunitVersion = BLEVersionExchangeResolver.makeVersionExchange();
344         setValueOnCharacteristicAndNotify(device, headunitVersion.toByteArray(), characteristic);
345 
346         if (Log.isLoggable(TAG, Log.DEBUG)) {
347             Log.d(TAG, "Sent supported version to the phone.");
348         }
349 
350         mIsVersionExchanged = true;
351     }
352 
353     /**
354      * Setup the BLE GATT server for Enrollment. The GATT server for Enrollment comprises of one
355      * GATT Service and 2 characteristics - one for the phone to write to and one for the head unit
356      * to write to.
357      */
setupEnrollmentBleServer()358     void setupEnrollmentBleServer() {
359         mEnrollmentServiceUuid = UUID.fromString(
360                 getContext().getString(R.string.enrollment_service_uuid));
361         mEnrollmentClientWriteUuid = UUID.fromString(
362                 getContext().getString(R.string.enrollment_client_write_uuid));
363         mEnrollmentServerWriteUuid = UUID.fromString(
364                 getContext().getString(R.string.enrollment_server_write_uuid));
365 
366         mEnrollmentGattService = new BluetoothGattService(mEnrollmentServiceUuid,
367                 BluetoothGattService.SERVICE_TYPE_PRIMARY);
368 
369         // Characteristic the connected bluetooth device will write to.
370         BluetoothGattCharacteristic clientCharacteristic =
371                 new BluetoothGattCharacteristic(mEnrollmentClientWriteUuid,
372                         BluetoothGattCharacteristic.PROPERTY_WRITE
373                                 | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
374                         BluetoothGattCharacteristic.PERMISSION_WRITE);
375 
376         // Characteristic that this manager will write to.
377         BluetoothGattCharacteristic serverCharacteristic =
378                 new BluetoothGattCharacteristic(mEnrollmentServerWriteUuid,
379                         BluetoothGattCharacteristic.PROPERTY_NOTIFY,
380                         BluetoothGattCharacteristic.PERMISSION_READ);
381 
382         addDescriptorToCharacteristic(serverCharacteristic);
383 
384         mEnrollmentGattService.addCharacteristic(clientCharacteristic);
385         mEnrollmentGattService.addCharacteristic(serverCharacteristic);
386     }
387 
388     /**
389      * Setup the BLE GATT server for Unlocking the Head unit. The GATT server for this phase also
390      * comprises of 1 Service and 2 characteristics. However both the token and the handle are sent
391      * from the phone to the head unit.
392      */
setupUnlockBleServer()393     void setupUnlockBleServer() {
394         mUnlockServiceUuid = UUID.fromString(getContext().getString(R.string.unlock_service_uuid));
395         mUnlockClientWriteUuid = UUID
396                 .fromString(getContext().getString(R.string.unlock_client_write_uuid));
397         mUnlockServerWriteUuid = UUID
398                 .fromString(getContext().getString(R.string.unlock_server_write_uuid));
399 
400         mUnlockGattService = new BluetoothGattService(mUnlockServiceUuid,
401                 BluetoothGattService.SERVICE_TYPE_PRIMARY);
402 
403         // Characteristic the connected bluetooth device will write to.
404         BluetoothGattCharacteristic clientCharacteristic = new BluetoothGattCharacteristic(
405                 mUnlockClientWriteUuid,
406                 BluetoothGattCharacteristic.PROPERTY_WRITE
407                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE,
408                 BluetoothGattCharacteristic.PERMISSION_WRITE);
409 
410         // Characteristic that this manager will write to.
411         BluetoothGattCharacteristic serverCharacteristic = new BluetoothGattCharacteristic(
412                 mUnlockServerWriteUuid,
413                 BluetoothGattCharacteristic.PROPERTY_NOTIFY,
414                 BluetoothGattCharacteristic.PERMISSION_READ);
415 
416         addDescriptorToCharacteristic(serverCharacteristic);
417 
418         mUnlockGattService.addCharacteristic(clientCharacteristic);
419         mUnlockGattService.addCharacteristic(serverCharacteristic);
420     }
421 
addDescriptorToCharacteristic(BluetoothGattCharacteristic characteristic)422     private void addDescriptorToCharacteristic(BluetoothGattCharacteristic characteristic) {
423         BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(
424                 CLIENT_CHARACTERISTIC_CONFIG,
425                 BluetoothGattDescriptor.PERMISSION_READ | BluetoothGattDescriptor.PERMISSION_WRITE);
426         descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
427         characteristic.addDescriptor(descriptor);
428     }
429 
startEnrollmentAdvertising()430     void startEnrollmentAdvertising() {
431         mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_ENROLLMENT;
432         // Replace name to ensure it is small enough to be advertised
433         String name = getEnrollmentDeviceName();
434         if (name != null) {
435             BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
436             if (mOriginalBluetoothName == null) {
437                 mOriginalBluetoothName = adapter.getName();
438             }
439             adapter.setName(name);
440             if (Log.isLoggable(TAG, Log.DEBUG)) {
441                 Log.d(TAG, "Changing bluetooth adapter name from "
442                         + mOriginalBluetoothName + " to " + name);
443             }
444         }
445         startAdvertising(mEnrollmentGattService,
446                 new AdvertiseData.Builder()
447                         .setIncludeDeviceName(true)
448                         .addServiceUuid(new ParcelUuid(mEnrollmentServiceUuid))
449                         .build(),
450                 mEnrollmentAdvertisingCallback);
451     }
452 
stopEnrollmentAdvertising()453     void stopEnrollmentAdvertising() {
454         if (mOriginalBluetoothName != null) {
455             if (Log.isLoggable(TAG, Log.DEBUG)) {
456                 Log.d(TAG, "Changing bluetooth adapter name back to "
457                         + mOriginalBluetoothName);
458             }
459             BluetoothAdapter.getDefaultAdapter().setName(mOriginalBluetoothName);
460         }
461         stopAdvertising(mEnrollmentAdvertisingCallback);
462     }
463 
startUnlockAdvertising()464     void startUnlockAdvertising() {
465         mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_UNLOCK;
466         startAdvertising(mUnlockGattService,
467                 new AdvertiseData.Builder()
468                         .setIncludeDeviceName(false)
469                         .addServiceData(new ParcelUuid(mUnlockServiceUuid), getUniqueId())
470                         .addServiceUuid(new ParcelUuid(mUnlockServiceUuid))
471                         .build(),
472                 mUnlockAdvertisingCallback);
473     }
474 
stopUnlockAdvertising()475     void stopUnlockAdvertising() {
476         mCurrentTrustedDeviceOperation = TRUSTED_DEVICE_OPERATION_NONE;
477         stopAdvertising(mUnlockAdvertisingCallback);
478     }
479 
disconnectRemoteDevice()480     void disconnectRemoteDevice() {
481         stopGattServer();
482     }
483 
sendUnlockMessage(BluetoothDevice device, byte[] message, OperationType operation, boolean isPayloadEncrypted)484     void sendUnlockMessage(BluetoothDevice device, byte[] message, OperationType operation,
485             boolean isPayloadEncrypted) {
486         BluetoothGattCharacteristic writeCharacteristic = mUnlockGattService
487                 .getCharacteristic(mUnlockServerWriteUuid);
488 
489         sendMessage(device, writeCharacteristic, message, operation, isPayloadEncrypted);
490     }
491 
sendEnrollmentMessage(BluetoothDevice device, byte[] message, OperationType operation, boolean isPayloadEncrypted)492     void sendEnrollmentMessage(BluetoothDevice device, byte[] message, OperationType operation,
493             boolean isPayloadEncrypted) {
494         BluetoothGattCharacteristic writeCharacteristic = mEnrollmentGattService
495                 .getCharacteristic(mEnrollmentServerWriteUuid);
496 
497         sendMessage(device, writeCharacteristic, message, operation, isPayloadEncrypted);
498     }
499 
500     /**
501      * Handles an ACK from the client.
502      *
503      * <p>An ACK means that the client has successfully received a partial BLEMessage, meaning the
504      * next part of the message can be sent.
505      *
506      * @param device                   The client device.
507      * @param clientCharacteristicUUID The UUID of the characteristic on the device that the ACK
508      *                                 was written to.
509      */
handleClientAckMessage(BluetoothDevice device, UUID clientCharacteristicUUID)510     private void handleClientAckMessage(BluetoothDevice device, UUID clientCharacteristicUUID) {
511         if (Log.isLoggable(TAG, Log.DEBUG)) {
512             Log.d(TAG, "Received ACK from client. Attempting to write next message in queue. "
513                     + "UUID: " + clientCharacteristicUUID);
514         }
515 
516         BluetoothGattCharacteristic writeCharacteristic =
517                 getCharacteristicForWrite(clientCharacteristicUUID);
518 
519         if (writeCharacteristic == null) {
520             Log.e(TAG, "No corresponding write characteristic found for writing next message in"
521                     + " queue. UUID: " + clientCharacteristicUUID);
522             return;
523         }
524         if (mSendRepeatedBleMessage != null) {
525             mHandler.removeCallbacks(mSendRepeatedBleMessage);
526             mSendRepeatedBleMessage = null;
527         }
528         // Previous message has been sent successfully so we can start the next message.
529         mMessageQueue.remove();
530         writeNextMessageInQueue(device, writeCharacteristic);
531     }
532 
533     /**
534      * Sends the given message to the specified device and characteristic.
535      * The message will be splited into multiple messages wrapped in BLEMessage proto.
536      *
537      * @param device             The device to send the message to.
538      * @param characteristic     The characteristic to write to.
539      * @param message            A message to send.
540      * @param operation          The type of operation this message represents.
541      * @param isPayloadEncrypted {@code true} if the message is encrypted.
542      */
sendMessage(BluetoothDevice device, BluetoothGattCharacteristic characteristic, byte[] message, OperationType operation, boolean isPayloadEncrypted)543     private void sendMessage(BluetoothDevice device, BluetoothGattCharacteristic characteristic,
544             byte[] message, OperationType operation, boolean isPayloadEncrypted) {
545         if (Log.isLoggable(TAG, Log.DEBUG)) {
546             Log.d(TAG, "sendMessage to: " + device.getAddress() + "; and characteristic UUID: "
547                     + characteristic.getUuid());
548         }
549 
550         List<BLEMessage> bleMessages = BLEMessageV1Factory.makeBLEMessages(message, operation,
551                 mMaxWriteSize, isPayloadEncrypted);
552 
553         if (Log.isLoggable(TAG, Log.DEBUG)) {
554             Log.d(TAG, "sending " + bleMessages.size() + " messages to device");
555         }
556 
557         mMessageQueue.addAll(bleMessages);
558         writeNextMessageInQueue(device, characteristic);
559     }
560 
561     /**
562      * Writes the next message in {@link #mMessageQueue} to the given characteristic.
563      *
564      * <p>If the message queue is empty, then this method will do nothing.
565      */
writeNextMessageInQueue(BluetoothDevice device, BluetoothGattCharacteristic characteristic)566     private void writeNextMessageInQueue(BluetoothDevice device,
567             BluetoothGattCharacteristic characteristic) {
568         if (mMessageQueue.isEmpty()) {
569             Log.e(TAG, "Call to write next message in queue, but the message queue is empty");
570             return;
571         }
572         // When there is only one message, no ACKs are sent, so we no need to retry based on ACKs.
573         if (mMessageQueue.size() == 1) {
574             setValueOnCharacteristicAndNotify(device, mMessageQueue.remove().toByteArray(),
575                     characteristic);
576             return;
577         }
578         mBleMessageRetryStartCount = 0;
579         mSendRepeatedBleMessage = new Runnable() {
580             @Override
581             public void run() {
582                 if (Log.isLoggable(TAG, Log.DEBUG)) {
583                     Log.d(TAG, "BLE message sending... " + "retry count: "
584                             + mBleMessageRetryStartCount);
585                 }
586                 if (mBleMessageRetryStartCount < BLE_MESSAGE_RETRY_LIMIT) {
587                     setValueOnCharacteristicAndNotify(device, mMessageQueue.peek().toByteArray(),
588                             characteristic);
589                     mBleMessageRetryStartCount++;
590                     mHandler.postDelayed(this, BLE_MESSAGE_RETRY_DELAY_MS);
591                 } else {
592                     Log.e(TAG, "Error during BLE message sending - exceeded retry limit.");
593                     mHandler.removeCallbacks(this);
594                     mCarTrustAgentEnrollmentService.terminateEnrollmentHandshake();
595                     mSendRepeatedBleMessage = null;
596                 }
597             }
598         };
599         mHandler.post(mSendRepeatedBleMessage);
600     }
601 
sendAcknowledgmentMessage(BluetoothDevice device, UUID clientCharacteristicUUID)602     private void sendAcknowledgmentMessage(BluetoothDevice device, UUID clientCharacteristicUUID) {
603         BluetoothGattCharacteristic writeCharacteristic =
604                 getCharacteristicForWrite(clientCharacteristicUUID);
605 
606         if (writeCharacteristic == null) {
607             Log.e(TAG, "No corresponding write characteristic found for sending ACK. UUID: "
608                     + clientCharacteristicUUID);
609             return;
610         }
611 
612         setValueOnCharacteristicAndNotify(device,
613                 BLEMessageV1Factory.makeAcknowledgementMessage().toByteArray(),
614                 writeCharacteristic);
615     }
616 
617     /**
618      * Sets the given message on the specified characteristic.
619      *
620      * <p>Upon successfully setting of the value, any listeners on the characteristic will be
621      * notified that its value has changed.
622      *
623      * @param device         The device has own the given characteristic.
624      * @param message        The message to set as the characteristic's value.
625      * @param characteristic The characteristic to set the value on.
626      */
setValueOnCharacteristicAndNotify(BluetoothDevice device, byte[] message, BluetoothGattCharacteristic characteristic)627     private void setValueOnCharacteristicAndNotify(BluetoothDevice device, byte[] message,
628             BluetoothGattCharacteristic characteristic) {
629         characteristic.setValue(message);
630         notifyCharacteristicChanged(device, characteristic, false);
631     }
632 
633     /**
634      * Returns the characteristic that can be written to based on the given UUID.
635      *
636      * <p>The UUID will be one that corresponds to either enrollment or unlock. This method will
637      * return the write characteristic for enrollment or unlock respectively.
638      *
639      * @return The write characteristic or {@code null} if the UUID is invalid.
640      */
641     @Nullable
getCharacteristicForWrite(UUID uuid)642     private BluetoothGattCharacteristic getCharacteristicForWrite(UUID uuid) {
643         if (uuid.equals(mEnrollmentClientWriteUuid)) {
644             return mEnrollmentGattService.getCharacteristic(mEnrollmentServerWriteUuid);
645         }
646 
647         if (uuid.equals(mUnlockClientWriteUuid)) {
648             return mUnlockGattService.getCharacteristic(mUnlockServerWriteUuid);
649         }
650 
651         return null;
652     }
653 
654     private final AdvertiseCallback mEnrollmentAdvertisingCallback = new AdvertiseCallback() {
655         @Override
656         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
657             super.onStartSuccess(settingsInEffect);
658             if (getEnrollmentService() != null) {
659                 getEnrollmentService().onEnrollmentAdvertiseStartSuccess();
660             }
661             if (Log.isLoggable(TAG, Log.DEBUG)) {
662                 Log.d(TAG, "Successfully started advertising service");
663             }
664         }
665 
666         @Override
667         public void onStartFailure(int errorCode) {
668             Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
669 
670             super.onStartFailure(errorCode);
671             if (getEnrollmentService() != null) {
672                 getEnrollmentService().onEnrollmentAdvertiseStartFailure();
673             }
674         }
675     };
676 
677     private final AdvertiseCallback mUnlockAdvertisingCallback = new AdvertiseCallback() {
678         @Override
679         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
680             super.onStartSuccess(settingsInEffect);
681             if (Log.isLoggable(TAG, Log.DEBUG)) {
682                 Log.d(TAG, "Unlock Advertising onStartSuccess");
683             }
684         }
685 
686         @Override
687         public void onStartFailure(int errorCode) {
688             Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
689             super.onStartFailure(errorCode);
690             if (errorCode == AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED) {
691                 return;
692             }
693             if (Log.isLoggable(TAG, Log.DEBUG)) {
694                 Log.d(TAG, "Start unlock advertising fail, retry to advertising..");
695             }
696             setupUnlockBleServer();
697             startUnlockAdvertising();
698         }
699     };
700 }
701