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