PhoneEnrolmentController.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.BluetoothGatt; 20import android.bluetooth.BluetoothGattCharacteristic; 21import android.bluetooth.BluetoothGattService; 22import android.content.Context; 23import android.content.SharedPreferences; 24import android.os.Handler; 25import android.os.ParcelUuid; 26import android.preference.PreferenceManager; 27import android.util.Base64; 28import android.util.Log; 29import android.view.View; 30import android.widget.Button; 31import android.widget.TextView; 32import com.android.car.trust.comms.SimpleBleClient; 33 34import java.nio.ByteBuffer; 35import java.util.Random; 36import java.util.UUID; 37 38/** 39 * A controller that sets up a {@link SimpleBleClient} to connect to the BLE enrolment service. 40 * It also binds the UI components to control the enrolment process. 41 */ 42public class PhoneEnrolmentController { 43 private static final String TAG = "PhoneEnrolmentCtlr"; 44 private String mTokenHandleKey; 45 private String mEscrowTokenKey; 46 47 // BLE characteristics associated with the enrolment/add escrow token service. 48 private BluetoothGattCharacteristic mEnrolmentTokenHandle; 49 private BluetoothGattCharacteristic mEnrolmentEscrowToken; 50 51 private ParcelUuid mEnrolmentServiceUuid; 52 53 private SimpleBleClient mClient; 54 private Context mContext; 55 56 private TextView mTextView; 57 private Handler mHandler; 58 59 private Button mScanButton; 60 private Button mEnrolButton; 61 62 public PhoneEnrolmentController(Context context) { 63 mContext = context; 64 65 mTokenHandleKey = context.getString(R.string.pref_key_token_handle); 66 mEscrowTokenKey = context.getString(R.string.pref_key_escrow_token); 67 68 mClient = new SimpleBleClient(context); 69 mEnrolmentServiceUuid = new ParcelUuid( 70 UUID.fromString(mContext.getString(R.string.enrolment_service_uuid))); 71 mClient.addCallback(mCallback /* callback */); 72 73 mHandler = new Handler(mContext.getMainLooper()); 74 } 75 76 /** 77 * Binds the views to the actions that can be performed by this controller. 78 * 79 * @param textView A text view used to display results from various BLE actions 80 * @param scanButton Button used to start scanning for available BLE devices. 81 * @param enrolButton Button used to send new escrow token to remote device. 82 */ 83 public void bind(TextView textView, Button scanButton, Button enrolButton) { 84 mTextView = textView; 85 mScanButton = scanButton; 86 mEnrolButton = enrolButton; 87 88 mScanButton.setOnClickListener(new View.OnClickListener() { 89 @Override 90 public void onClick(View v) { 91 mClient.start(mEnrolmentServiceUuid); 92 } 93 }); 94 95 mEnrolButton.setEnabled(false); 96 mEnrolButton.setAlpha(0.3f); 97 mEnrolButton.setOnClickListener(new View.OnClickListener() { 98 @Override 99 public void onClick(View v) { 100 appendOutputText("Sending new escrow token to remote device"); 101 102 byte[] token = generateEscrowToken(); 103 sendEnrolmentRequest(token); 104 105 // WARNING: Store the token so it can be used later for unlocking. This token 106 // should NEVER be stored on the device that is being unlocked. It should 107 // always be securely stored on a remote device that will trigger the unlock. 108 storeToken(token); 109 } 110 }); 111 } 112 113 /** 114 * @return A random byte array that is used as the escrow token for remote device unlock. 115 */ 116 private byte[] generateEscrowToken() { 117 Random random = new Random(); 118 ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); 119 buffer.putLong(0, random.nextLong()); 120 return buffer.array(); 121 } 122 123 private void sendEnrolmentRequest(byte[] token) { 124 mEnrolmentEscrowToken.setValue(token); 125 mClient.writeCharacteristic(mEnrolmentEscrowToken); 126 storeToken(token); 127 } 128 129 private SimpleBleClient.ClientCallback mCallback = new SimpleBleClient.ClientCallback() { 130 @Override 131 public void onDeviceConnected(BluetoothDevice device) { 132 appendOutputText("Device connected: " + device.getName() 133 + " addr: " + device.getAddress()); 134 } 135 136 @Override 137 public void onDeviceDisconnected() { 138 appendOutputText("Device disconnected"); 139 } 140 141 @Override 142 public void onCharacteristicChanged(BluetoothGatt gatt, 143 BluetoothGattCharacteristic characteristic) { 144 Log.d(TAG, "onCharacteristicChanged: " + Utils.getLong(characteristic.getValue())); 145 146 if (characteristic.getUuid().equals(mEnrolmentTokenHandle.getUuid())) { 147 // Store the new token handle that the BLE server is sending us. This required 148 // to unlock the device. 149 long handle = Utils.getLong(characteristic.getValue()); 150 storeHandle(handle); 151 appendOutputText("Token handle received: " + handle); 152 } 153 } 154 155 @Override 156 public void onServiceDiscovered(BluetoothGattService service) { 157 if (!service.getUuid().equals(mEnrolmentServiceUuid.getUuid())) { 158 Log.d(TAG, "Service UUID: " + service.getUuid() + " does not match Enrolment UUID " 159 + mEnrolmentServiceUuid.getUuid()); 160 return; 161 } 162 163 Log.d(TAG, "Enrolment Service # characteristics: " + service.getCharacteristics().size()); 164 mEnrolmentEscrowToken 165 = Utils.getCharacteristic(R.string.enrolment_token_uuid, service, mContext); 166 mEnrolmentTokenHandle 167 = Utils.getCharacteristic(R.string.enrolment_handle_uuid, service, mContext); 168 mClient.setCharacteristicNotification(mEnrolmentTokenHandle, true /* enable */); 169 appendOutputText("Enrolment BLE client successfully connected"); 170 171 mHandler.post(new Runnable() { 172 @Override 173 public void run() { 174 // Services are now set up, allow users to enrol new escrow tokens. 175 mEnrolButton.setEnabled(true); 176 mEnrolButton.setAlpha(1.0f); 177 } 178 }); 179 } 180 }; 181 182 private void storeHandle(long handle) { 183 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); 184 prefs.edit().putLong(mTokenHandleKey, handle).apply(); 185 } 186 187 private void storeToken(byte[] token) { 188 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); 189 String byteArray = Base64.encodeToString(token, Base64.DEFAULT); 190 prefs.edit().putString(mEscrowTokenKey, byteArray).apply(); 191 } 192 193 private void appendOutputText(final String text) { 194 mHandler.post(new Runnable() { 195 @Override 196 public void run() { 197 mTextView.append("\n" + text); 198 } 199 }); 200 } 201} 202