blob: 9b3db66927f80ea47cdab679061902a5df99b632 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.car.trust;
import android.annotation.Nullable;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseSettings;
import android.content.Context;
import android.util.Log;
import com.android.car.CarLocalServices;
import com.android.car.R;
import com.android.car.Utils;
import java.util.UUID;
/**
* A BLE Service that is used for communicating with the trusted peer device. This extends from a
* more generic {@link BLEManager} and has more context on the BLE requirements for the Trusted
* device feature. It has knowledge on the GATT services and characteristics that are specific to
* the Trusted Device feature.
*/
class CarTrustAgentBleManager extends BleManager {
private static final String TAG = "CarTrustBLEManager";
private static final String CONFIRMATION_SIGNAL = "True";
private CarTrustedDeviceService mCarTrustedDeviceService;
private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
private CarTrustAgentUnlockService mCarTrustAgentUnlockService;
// Enrollment Service and Characteristic UUIDs
private UUID mEnrollmentServiceUuid;
private UUID mEnrollmentEscrowTokenUuid;
private UUID mEnrollmentTokenHandleUuid;
private BluetoothGattService mEnrollmentGattService;
// Unlock Service and Characteristic UUIDs
private UUID mUnlockServiceUuid;
private UUID mUnlockEscrowTokenUuid;
private UUID mUnlockTokenHandleUuid;
private BluetoothGattService mUnlockGattService;
CarTrustAgentBleManager(Context context) {
super(context);
}
// Overriding some of the {@link BLEManager} methods to be specific for Trusted Device feature.
@Override
public void onRemoteDeviceConnected(BluetoothDevice device) {
if (getTrustedDeviceService() != null) {
getTrustedDeviceService().onRemoteDeviceConnected(device);
}
}
@Override
public void onRemoteDeviceDisconnected(BluetoothDevice device) {
if (getTrustedDeviceService() != null) {
getTrustedDeviceService().onRemoteDeviceDisconnected(device);
}
}
@Override
public void onCharacteristicWrite(BluetoothDevice device, int requestId,
BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
responseNeeded, int offset, byte[] value) {
UUID uuid = characteristic.getUuid();
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "onCharacteristicWrite received uuid: " + uuid);
}
if (uuid.equals(mEnrollmentEscrowTokenUuid)) {
if (getEnrollmentService() != null) {
getEnrollmentService().onEnrollmentDataReceived(value);
}
} else if (uuid.equals(mUnlockEscrowTokenUuid)) {
if (getUnlockService() != null) {
getUnlockService().onUnlockTokenReceived(value);
}
} else if (uuid.equals(mUnlockTokenHandleUuid)) {
if (getUnlockService() != null) {
getUnlockService().onUnlockHandleReceived(value);
}
}
}
@Override
public void onCharacteristicRead(BluetoothDevice device,
int requestId, int offset, final BluetoothGattCharacteristic characteristic) {
// Ignored read requests.
}
@Nullable
private CarTrustedDeviceService getTrustedDeviceService() {
if (mCarTrustedDeviceService == null) {
mCarTrustedDeviceService = CarLocalServices.getService(
CarTrustedDeviceService.class);
}
return mCarTrustedDeviceService;
}
@Nullable
private CarTrustAgentEnrollmentService getEnrollmentService() {
if (mCarTrustAgentEnrollmentService != null) {
return mCarTrustAgentEnrollmentService;
}
if (getTrustedDeviceService() != null) {
mCarTrustAgentEnrollmentService =
getTrustedDeviceService().getCarTrustAgentEnrollmentService();
}
return mCarTrustAgentEnrollmentService;
}
@Nullable
private CarTrustAgentUnlockService getUnlockService() {
if (mCarTrustAgentUnlockService != null) {
return mCarTrustAgentUnlockService;
}
if (getTrustedDeviceService() != null) {
mCarTrustAgentUnlockService =
getTrustedDeviceService().getCarTrustAgentUnlockService();
}
return mCarTrustAgentUnlockService;
}
/**
* Setup the BLE GATT server for Enrollment. The GATT server for Enrollment comprises of
* one GATT Service and 2 characteristics - one for the escrow token to be generated and sent
* from the phone and the other for the handle generated and sent by the Head unit.
*/
void setupEnrollmentBleServer() {
mEnrollmentServiceUuid = UUID.fromString(
getContext().getString(R.string.enrollment_service_uuid));
mEnrollmentEscrowTokenUuid = UUID.fromString(
getContext().getString(R.string.enrollment_token_uuid));
mEnrollmentTokenHandleUuid = UUID.fromString(
getContext().getString(R.string.enrollment_handle_uuid));
mEnrollmentGattService = new BluetoothGattService(mEnrollmentServiceUuid,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Characteristic to describe the escrow token being used for unlock
BluetoothGattCharacteristic tokenCharacteristic = new BluetoothGattCharacteristic(
mEnrollmentEscrowTokenUuid,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
// Characteristic to describe the handle being used for this escrow token
BluetoothGattCharacteristic handleCharacteristic = new BluetoothGattCharacteristic(
mEnrollmentTokenHandleUuid,
BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ);
mEnrollmentGattService.addCharacteristic(tokenCharacteristic);
mEnrollmentGattService.addCharacteristic(handleCharacteristic);
}
/**
* Setup the BLE GATT server for Unlocking the Head unit. The GATT server for this phase also
* comprises of 1 Service and 2 characteristics. However both the token and the handle are
* sent ftrom the phone to the head unit.
*/
void setupUnlockBleServer() {
mUnlockServiceUuid = UUID.fromString(
getContext().getString(R.string.unlock_service_uuid));
mUnlockEscrowTokenUuid = UUID.fromString(
getContext().getString(R.string.unlock_escrow_token_uuid));
mUnlockTokenHandleUuid = UUID.fromString(
getContext().getString(R.string.unlock_handle_uuid));
mUnlockGattService = new BluetoothGattService(mUnlockServiceUuid,
BluetoothGattService.SERVICE_TYPE_PRIMARY);
// Characteristic to describe the escrow token being used for unlock
BluetoothGattCharacteristic tokenCharacteristic = new BluetoothGattCharacteristic(
mUnlockEscrowTokenUuid,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
// Characteristic to describe the handle being used for this escrow token
BluetoothGattCharacteristic handleCharacteristic = new BluetoothGattCharacteristic(
mUnlockTokenHandleUuid,
BluetoothGattCharacteristic.PROPERTY_WRITE,
BluetoothGattCharacteristic.PERMISSION_WRITE);
mUnlockGattService.addCharacteristic(tokenCharacteristic);
mUnlockGattService.addCharacteristic(handleCharacteristic);
}
void startEnrollmentAdvertising() {
startAdvertising(mEnrollmentGattService, mEnrollmentAdvertisingCallback);
}
void stopEnrollmentAdvertising() {
stopAdvertising(mEnrollmentAdvertisingCallback);
}
void startUnlockAdvertising() {
startAdvertising(mUnlockGattService, mUnlockAdvertisingCallback);
}
void stopUnlockAdvertising() {
stopAdvertising(mUnlockAdvertisingCallback);
}
void disconnectRemoteDevice(BluetoothDevice device) {
// TODO(b/129029421) - is closing the GATT Server the right thing to do here?
}
/**
* Sends the handle corresponding to the escrow token that was generated by the phone to the
* phone.
*
* @param device the BLE peer device to send the handle to.
* @param handle the handle corresponding to the escrow token
*/
void sendEnrollmentHandle(BluetoothDevice device, long handle) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "sendEnrollmentHandle: " + Long.toHexString(handle) + " "
+ device.getAddress());
}
BluetoothGattCharacteristic enrollmentHandle = mEnrollmentGattService.getCharacteristic(
mEnrollmentTokenHandleUuid);
enrollmentHandle.setValue(Utils.longToBytes(handle));
notifyCharacteristicChanged(device, enrollmentHandle, false);
}
/**
* Sends the pairing code confirmation signal to the phone
*
* @param device the BLE peer device to send the signal to.
*/
void sendPairingCodeConfirmation(BluetoothDevice device) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "sendPairingCodeConfirmation: " + device.getAddress());
}
BluetoothGattCharacteristic confirmation = mEnrollmentGattService.getCharacteristic(
mEnrollmentTokenHandleUuid);
confirmation.setValue(CONFIRMATION_SIGNAL.getBytes());
notifyCharacteristicChanged(device, confirmation, false);
}
private final AdvertiseCallback mEnrollmentAdvertisingCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
if (getEnrollmentService() != null) {
getEnrollmentService().onEnrollmentAdvertiseStartSuccess();
}
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Successfully started advertising service");
}
}
@Override
public void onStartFailure(int errorCode) {
Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
super.onStartFailure(errorCode);
if (getEnrollmentService() != null) {
getEnrollmentService().onEnrollmentAdvertiseStartFailure(errorCode);
}
}
};
private final AdvertiseCallback mUnlockAdvertisingCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Unlock Advertising onStartSuccess");
}
}
@Override
public void onStartFailure(int errorCode) {
Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
super.onStartFailure(errorCode);
}
};
}