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