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