177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan/* 277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * Copyright (C) 2017 The Android Open Source Project 377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * 477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * Licensed under the Apache License, Version 2.0 (the "License"); 577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * you may not use this file except in compliance with the License. 677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * You may obtain a copy of the License at 777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * 877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * http://www.apache.org/licenses/LICENSE-2.0 977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * 1077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * Unless required by applicable law or agreed to in writing, software 1177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * distributed under the License is distributed on an "AS IS" BASIS, 1277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * See the License for the specific language governing permissions and 1477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * limitations under the License 1577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan */ 1677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanpackage com.android.car.trust; 1777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 1877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.bluetooth.BluetoothDevice; 1977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.bluetooth.BluetoothGattCharacteristic; 2077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.bluetooth.BluetoothGattService; 2177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.content.Intent; 2277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.os.Binder; 2377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.os.IBinder; 2477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.os.ParcelUuid; 2577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport android.util.Log; 2677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport com.android.car.trust.comms.SimpleBleServer; 2777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 2877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanimport java.util.UUID; 2977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 3077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan/** 3177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * A service that receives unlock requests from remote devices. 3277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan */ 3377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chanpublic class CarUnlockService extends SimpleBleServer { 3477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan /** 3577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * A callback to receives callback 3677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan */ 3777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public interface UnlockServiceCallback { 3877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan void unlockDevice(byte[] token, long handle); 3977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 4077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 4177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private static final String TAG = "CarUnlockService"; 4277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 4377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private BluetoothGattService mUnlockService; 4477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private BluetoothGattCharacteristic mUnlockEscrowToken; 4577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private BluetoothGattCharacteristic mUnlockTokenHandle; 4677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 4777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private UnlockServiceCallback mCallback; 4877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 4977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private byte[] mCurrentToken; 5077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private Long mCurrentHandle; 5177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 5277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private final IBinder mBinder = new UnlockServiceBinder(); 5377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 5477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public class UnlockServiceBinder extends Binder { 5577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public CarUnlockService getService() { 5677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan return CarUnlockService.this; 5777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 5877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 5977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 6077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan @Override 6177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public void onCreate() { 6277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan super.onCreate(); 63452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 64452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer Log.d(TAG, "CarUnlockService starting up, creating BLE service"); 65452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer } 6677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan setupUnlockService(); 6777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 6877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 6977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan /** 7077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan * Start advertising the BLE unlock service 7177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan */ 7277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public void start() { 7377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan ParcelUuid uuid = new ParcelUuid( 7477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan UUID.fromString(getString(R.string.unlock_service_uuid))); 7577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan start(uuid, mUnlockService); 7677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 7777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 7877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public void addUnlockServiceCallback(UnlockServiceCallback callback) { 7977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mCallback = callback; 8077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 8177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 8277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan @Override 8377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public IBinder onBind(Intent intent) { 8477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan return mBinder; 8577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 8677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 8777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan @Override 8877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public void onCharacteristicWrite(BluetoothDevice device, 8977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan int requestId, BluetoothGattCharacteristic characteristic, 9077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { 9177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan UUID uuid = characteristic.getUuid(); 9277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 9377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan if (uuid.equals(mUnlockTokenHandle.getUuid())) { 94452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 95452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer Log.d(TAG, "Unlock handle received, value: " + Utils.getLong(value)); 96452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer } 9777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mCurrentHandle = Utils.getLong(value); 9877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan unlockDataReceived(); 9977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } else if (uuid.equals(mUnlockEscrowToken.getUuid())) { 100452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 101452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer Log.d(TAG, "Unlock escrow token received, value: " + Utils.getLong(value)); 102452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer } 10377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mCurrentToken = value; 10477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan unlockDataReceived(); 10577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 10677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 10777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 10877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan @Override 10977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan public void onCharacteristicRead(BluetoothDevice device, 11077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan int requestId, int offset, BluetoothGattCharacteristic characteristic) { 11177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan // The BLE unlock service should not receive any read requests. 11277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 11377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 11477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private synchronized void unlockDataReceived() { 11577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan // If any piece of the unlocking data is not received, then do not unlock. 11677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan if (mCurrentHandle == null || mCurrentToken == null) { 11777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan return; 11877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 119452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer 120452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer if (Log.isLoggable(TAG, Log.DEBUG)) { 121452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer Log.d(TAG, "Handle and token both received, requesting unlock. Time: " 122452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer + System.currentTimeMillis()); 123452ad74f4c25e153c00d94c3db674deab8efb1acRakesh Iyer } 12477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan // Both the handle and token has been received, try to unlock the device. 12577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 12677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 12777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mCallback.unlockDevice(mCurrentToken, mCurrentHandle); 12877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 12977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan // Once we've notified the client of the unlocking data, clear it out. 13077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mCurrentToken = null; 13177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mCurrentHandle = null; 13277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 13377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 13477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 13577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan // Create services and characteristics to receive tokens and handles for unlocking the device. 13677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan private void setupUnlockService() { 13777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mUnlockService = new BluetoothGattService( 13877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan UUID.fromString(getString(R.string.unlock_service_uuid)), 13977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan BluetoothGattService.SERVICE_TYPE_PRIMARY); 14077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 14177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan // Characteristic to describe the escrow token being used for unlock 14277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mUnlockEscrowToken = new BluetoothGattCharacteristic( 14377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan UUID.fromString(getString(R.string.unlock_escrow_token_uiid)), 14477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan BluetoothGattCharacteristic.PROPERTY_WRITE, 14577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan BluetoothGattCharacteristic.PERMISSION_WRITE); 14677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 14777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan // Characteristic to describe the handle being used for this escrow token 14877e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mUnlockTokenHandle = new BluetoothGattCharacteristic( 14977e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan UUID.fromString(getString(R.string.unlock_handle_uiid)), 15077e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan BluetoothGattCharacteristic.PROPERTY_WRITE, 15177e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan BluetoothGattCharacteristic.PERMISSION_WRITE); 15277e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 15377e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mUnlockService.addCharacteristic(mUnlockEscrowToken); 15477e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan mUnlockService.addCharacteristic(mUnlockTokenHandle); 15577e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan } 15677e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan 15777e5e49cf9dcceb69b07510c380ae2a9285ebfeeVictor Chan} 158