1 /* 2 * Copyright (C) 2017 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 package com.android.car.trust; 17 18 import android.bluetooth.BluetoothDevice; 19 import android.bluetooth.BluetoothGattCharacteristic; 20 import android.bluetooth.BluetoothGattService; 21 import android.content.Intent; 22 import android.os.Binder; 23 import android.os.IBinder; 24 import android.os.ParcelUuid; 25 import android.util.Log; 26 import com.android.car.trust.comms.SimpleBleServer; 27 28 import java.util.UUID; 29 30 /** 31 * A service that receives unlock requests from remote devices. 32 */ 33 public class CarUnlockService extends SimpleBleServer { 34 /** 35 * A callback to receives callback 36 */ 37 public interface UnlockServiceCallback { 38 void unlockDevice(byte[] token, long handle); 39 } 40 41 private static final String TAG = "CarUnlockService"; 42 43 private BluetoothGattService mUnlockService; 44 private BluetoothGattCharacteristic mUnlockEscrowToken; 45 private BluetoothGattCharacteristic mUnlockTokenHandle; 46 47 private UnlockServiceCallback mCallback; 48 49 private byte[] mCurrentToken; 50 private Long mCurrentHandle; 51 52 private final IBinder mBinder = new UnlockServiceBinder(); 53 54 public class UnlockServiceBinder extends Binder { 55 public CarUnlockService getService() { 56 return CarUnlockService.this; 57 } 58 } 59 60 @Override 61 public void onCreate() { 62 super.onCreate(); 63 if (Log.isLoggable(TAG, Log.DEBUG)) { 64 Log.d(TAG, "CarUnlockService starting up, creating BLE service"); 65 } 66 setupUnlockService(); 67 } 68 69 /** 70 * Start advertising the BLE unlock service 71 */ 72 public void start() { 73 ParcelUuid uuid = new ParcelUuid( 74 UUID.fromString(getString(R.string.unlock_service_uuid))); 75 start(uuid, mUnlockService); 76 } 77 78 public void addUnlockServiceCallback(UnlockServiceCallback callback) { 79 mCallback = callback; 80 } 81 82 @Override 83 public IBinder onBind(Intent intent) { 84 return mBinder; 85 } 86 87 @Override 88 public void onCharacteristicWrite(BluetoothDevice device, 89 int requestId, BluetoothGattCharacteristic characteristic, 90 boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { 91 UUID uuid = characteristic.getUuid(); 92 93 if (uuid.equals(mUnlockTokenHandle.getUuid())) { 94 if (Log.isLoggable(TAG, Log.DEBUG)) { 95 Log.d(TAG, "Unlock handle received, value: " + Utils.getLong(value)); 96 } 97 mCurrentHandle = Utils.getLong(value); 98 unlockDataReceived(); 99 } else if (uuid.equals(mUnlockEscrowToken.getUuid())) { 100 if (Log.isLoggable(TAG, Log.DEBUG)) { 101 Log.d(TAG, "Unlock escrow token received, value: " + Utils.getLong(value)); 102 } 103 mCurrentToken = value; 104 unlockDataReceived(); 105 } 106 } 107 108 @Override 109 public void onCharacteristicRead(BluetoothDevice device, 110 int requestId, int offset, BluetoothGattCharacteristic characteristic) { 111 // The BLE unlock service should not receive any read requests. 112 } 113 114 private synchronized void unlockDataReceived() { 115 // If any piece of the unlocking data is not received, then do not unlock. 116 if (mCurrentHandle == null || mCurrentToken == null) { 117 return; 118 } 119 120 if (Log.isLoggable(TAG, Log.DEBUG)) { 121 Log.d(TAG, "Handle and token both received, requesting unlock. Time: " 122 + System.currentTimeMillis()); 123 } 124 // Both the handle and token has been received, try to unlock the device. 125 126 127 mCallback.unlockDevice(mCurrentToken, mCurrentHandle); 128 129 // Once we've notified the client of the unlocking data, clear it out. 130 mCurrentToken = null; 131 mCurrentHandle = null; 132 } 133 134 135 // Create services and characteristics to receive tokens and handles for unlocking the device. 136 private void setupUnlockService() { 137 mUnlockService = new BluetoothGattService( 138 UUID.fromString(getString(R.string.unlock_service_uuid)), 139 BluetoothGattService.SERVICE_TYPE_PRIMARY); 140 141 // Characteristic to describe the escrow token being used for unlock 142 mUnlockEscrowToken = new BluetoothGattCharacteristic( 143 UUID.fromString(getString(R.string.unlock_escrow_token_uiid)), 144 BluetoothGattCharacteristic.PROPERTY_WRITE, 145 BluetoothGattCharacteristic.PERMISSION_WRITE); 146 147 // Characteristic to describe the handle being used for this escrow token 148 mUnlockTokenHandle = new BluetoothGattCharacteristic( 149 UUID.fromString(getString(R.string.unlock_handle_uiid)), 150 BluetoothGattCharacteristic.PROPERTY_WRITE, 151 BluetoothGattCharacteristic.PERMISSION_WRITE); 152 153 mUnlockService.addCharacteristic(mUnlockEscrowToken); 154 mUnlockService.addCharacteristic(mUnlockTokenHandle); 155 } 156 157 } 158