1 /* 2 * Copyright (C) 2019 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 17 package com.android.car.trust; 18 19 import static com.android.car.trust.EventLog.BLUETOOTH_STATE_CHANGED; 20 import static com.android.car.trust.EventLog.USER_UNLOCKED; 21 import static com.android.car.trust.EventLog.logUnlockEvent; 22 23 import android.app.ActivityManager; 24 import android.bluetooth.BluetoothAdapter; 25 import android.car.trust.TrustedDeviceInfo; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.service.trust.TrustAgentService; 33 import android.util.Log; 34 35 import com.android.car.CarLocalServices; 36 import com.android.car.Utils; 37 import com.android.car.trust.CarTrustAgentEnrollmentService.CarTrustAgentEnrollmentRequestDelegate; 38 import com.android.car.trust.CarTrustAgentUnlockService.CarTrustAgentUnlockDelegate; 39 40 import java.util.List; 41 42 /** 43 * A BluetoothLE (BLE) based {@link TrustAgentService} that uses the escrow token unlock APIs. 44 * <p> 45 * This trust agent runs during direct boot and interacts with {@link CarTrustedDeviceService} 46 * to listen for remote devices to trigger an unlock. 47 * <p> 48 * The system {@link com.android.server.trust.TrustManagerService} binds to this agent and uses 49 * the data it receives from this agent to authorize a user in lieu of the PIN/Pattern/Password 50 * credentials. 51 */ 52 public class CarBleTrustAgent extends TrustAgentService { 53 private static final String TAG = CarBleTrustAgent.class.getSimpleName(); 54 private boolean mIsDeviceLocked; 55 private CarTrustedDeviceService mCarTrustedDeviceService; 56 private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService; 57 private CarTrustAgentUnlockService mCarTrustAgentUnlockService; 58 59 @Override onCreate()60 public void onCreate() { 61 if (Log.isLoggable(TAG, Log.DEBUG)) { 62 Log.d(TAG, "onCreate()"); 63 } 64 super.onCreate(); 65 // Registering for more granular BLE specific state changes as against Bluetooth state 66 // changes, helps with reducing latency in getting notified. 67 IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED); 68 registerReceiver(mBluetoothBroadcastReceiver, intentFilter); 69 70 // TODO(b/129144535) handle scenarios where CarService crashed. Maybe retrieve this 71 // every time we need instead of caching. 72 mCarTrustedDeviceService = CarLocalServices.getService(CarTrustedDeviceService.class); 73 if (mCarTrustedDeviceService == null) { 74 Log.e(TAG, "Cannot retrieve the Trusted device Service"); 75 return; 76 } 77 mCarTrustAgentEnrollmentService = 78 mCarTrustedDeviceService.getCarTrustAgentEnrollmentService(); 79 setEnrollmentRequestDelegate(); 80 mCarTrustAgentUnlockService = mCarTrustedDeviceService.getCarTrustAgentUnlockService(); 81 setUnlockRequestDelegate(); 82 setManagingTrust(true); 83 } 84 85 @Override onDestroy()86 public void onDestroy() { 87 if (Log.isLoggable(TAG, Log.DEBUG)) { 88 Log.d(TAG, "Car Trust agent shutting down"); 89 } 90 super.onDestroy(); 91 mCarTrustAgentEnrollmentService = null; 92 if (mBluetoothBroadcastReceiver != null) { 93 unregisterReceiver(mBluetoothBroadcastReceiver); 94 } 95 } 96 97 // Overriding TrustAgentService methods 98 @Override onDeviceLocked()99 public void onDeviceLocked() { 100 int uid = ActivityManager.getCurrentUser(); 101 if (Log.isLoggable(TAG, Log.DEBUG)) { 102 Log.d(TAG, "onDeviceLocked Current user: " + uid); 103 } 104 super.onDeviceLocked(); 105 mIsDeviceLocked = true; 106 107 if (!hasTrustedDevice(uid)) { 108 if (Log.isLoggable(TAG, Log.DEBUG)) { 109 Log.d(TAG, "Not starting Unlock Advertising yet, since current user: " 110 + uid + "has no trusted device"); 111 } 112 return; 113 } 114 if (isBluetoothAvailable() && mCarTrustAgentUnlockService != null) { 115 mCarTrustAgentUnlockService.startUnlockAdvertising(); 116 } 117 } 118 119 @Override onDeviceUnlocked()120 public void onDeviceUnlocked() { 121 if (Log.isLoggable(TAG, Log.DEBUG)) { 122 Log.d(TAG, "onDeviceUnlocked Current user: " + ActivityManager.getCurrentUser()); 123 } 124 super.onDeviceUnlocked(); 125 mIsDeviceLocked = false; 126 127 if (isBluetoothAvailable() && mCarTrustAgentUnlockService != null) { 128 mCarTrustAgentUnlockService.stopUnlockAdvertising(); 129 } 130 } 131 isBluetoothAvailable()132 private boolean isBluetoothAvailable() { 133 BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter(); 134 if (defaultAdapter == null) { 135 if (Log.isLoggable(TAG, Log.DEBUG)) { 136 Log.d(TAG, "Bluetooth Adapter null."); 137 } 138 return false; 139 } 140 if (defaultAdapter.getState() == BluetoothAdapter.STATE_OFF) { 141 if (Log.isLoggable(TAG, Log.DEBUG)) { 142 Log.d(TAG, "Bluetooth Adapter is off"); 143 } 144 return false; 145 } 146 return true; 147 } 148 149 @Override onEscrowTokenRemoved(long handle, boolean successful)150 public void onEscrowTokenRemoved(long handle, boolean successful) { 151 if (Log.isLoggable(TAG, Log.DEBUG)) { 152 Log.d(TAG, "onEscrowTokenRemoved handle: " + Long.toHexString(handle)); 153 } 154 if (mCarTrustAgentEnrollmentService == null) { 155 return; 156 } 157 if (successful) { 158 mCarTrustAgentEnrollmentService.onEscrowTokenRemoved(handle, 159 ActivityManager.getCurrentUser()); 160 } 161 } 162 163 @Override onEscrowTokenStateReceived(long handle, int tokenState)164 public void onEscrowTokenStateReceived(long handle, int tokenState) { 165 if (Log.isLoggable(TAG, Log.DEBUG)) { 166 Log.d(TAG, "onEscrowTokenStateReceived: " + Long.toHexString(handle) + " state: " 167 + tokenState); 168 } 169 if (mCarTrustAgentEnrollmentService == null) { 170 return; 171 } 172 mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(handle, 173 tokenState == TOKEN_STATE_ACTIVE, ActivityManager.getCurrentUser()); 174 } 175 176 @Override onEscrowTokenAdded(byte[] token, long handle, UserHandle user)177 public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) { 178 if (Log.isLoggable(TAG, Log.DEBUG)) { 179 Log.d(TAG, "onEscrowTokenAdded handle: " + Long.toHexString(handle) + " token: " 180 + Utils.byteArrayToHexString(token)); 181 } 182 if (mCarTrustAgentEnrollmentService == null) { 183 return; 184 } 185 mCarTrustAgentEnrollmentService.onEscrowTokenAdded(token, handle, user.getIdentifier()); 186 } 187 setEnrollmentRequestDelegate()188 private void setEnrollmentRequestDelegate() { 189 if (mCarTrustAgentEnrollmentService == null) { 190 return; 191 } 192 mCarTrustAgentEnrollmentService.setEnrollmentRequestDelegate(mEnrollDelegate); 193 } 194 setUnlockRequestDelegate()195 private void setUnlockRequestDelegate() { 196 if (mCarTrustAgentUnlockService == null) { 197 return; 198 } 199 mCarTrustAgentUnlockService.setUnlockRequestDelegate(mUnlockDelegate); 200 } 201 202 /** 203 * 204 * @param uid User id 205 * @return if the user has trusted device 206 */ hasTrustedDevice(int uid)207 private boolean hasTrustedDevice(int uid) { 208 if (mCarTrustAgentEnrollmentService == null) { 209 return false; 210 } 211 List<TrustedDeviceInfo> trustedDeviceInfos = mCarTrustAgentEnrollmentService 212 .getEnrolledDeviceInfosForUser(uid); 213 return trustedDeviceInfos != null && trustedDeviceInfos.size() > 0; 214 } 215 unlockUserInternally(int uid, byte[] token, long handle)216 private void unlockUserInternally(int uid, byte[] token, long handle) { 217 if (Log.isLoggable(TAG, Log.DEBUG)) { 218 Log.d(TAG, "About to unlock user: " + uid); 219 UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 220 if (um.isUserUnlocked(UserHandle.of(uid))) { 221 Log.d(TAG, "User currently unlocked"); 222 } else { 223 Log.d(TAG, "User currently locked"); 224 } 225 } 226 unlockUserWithToken(handle, token, UserHandle.of(uid)); 227 grantTrust("Granting trust from escrow token", 228 0, FLAG_GRANT_TRUST_DISMISS_KEYGUARD); 229 } 230 231 private final BroadcastReceiver mBluetoothBroadcastReceiver = new BroadcastReceiver() { 232 @Override 233 public void onReceive(Context context, Intent intent) { 234 if (intent.getAction() != null && BluetoothAdapter.ACTION_BLE_STATE_CHANGED.equals( 235 intent.getAction())) { 236 onBluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)); 237 } 238 } 239 }; 240 onBluetoothStateChanged(int state)241 private void onBluetoothStateChanged(int state) { 242 if (Log.isLoggable(TAG, Log.DEBUG)) { 243 Log.d(TAG, "onBluetoothStateChanged: " + state); 244 } 245 if (!mIsDeviceLocked) { 246 return; 247 } 248 logUnlockEvent(BLUETOOTH_STATE_CHANGED, state); 249 switch (state) { 250 case BluetoothAdapter.STATE_BLE_ON: 251 int uid = ActivityManager.getCurrentUser(); 252 if (mCarTrustAgentUnlockService != null && hasTrustedDevice(uid)) { 253 mCarTrustAgentUnlockService.startUnlockAdvertising(); 254 } 255 break; 256 case BluetoothAdapter.STATE_OFF: 257 Log.e(TAG, "Bluetooth Adapter Off in lock screen"); 258 if (mCarTrustedDeviceService != null) { 259 mCarTrustedDeviceService.cleanupBleService(); 260 } 261 break; 262 default: 263 break; 264 } 265 } 266 267 // Implementing Delegates for Enrollment and Unlock. The CarBleTrustAgent acts as the interface 268 // between the Trust Agent framework and the Car Service. The Car service handles communicating 269 // with the peer device part and the framework handles the actual authentication. The 270 // CarBleTrustAgent abstracts these 2 pieces from each other. 271 /** 272 * Implementation of the {@link CarTrustAgentEnrollmentRequestDelegate} 273 */ 274 private final CarTrustAgentEnrollmentRequestDelegate mEnrollDelegate = 275 new CarTrustAgentEnrollmentRequestDelegate() { 276 @Override 277 public void addEscrowToken(byte[] token, int uid) { 278 if (Log.isLoggable(TAG, Log.DEBUG)) { 279 Log.d(TAG, 280 "addEscrowToken. uid: " + uid + " token: " 281 + Utils.byteArrayToHexString( 282 token)); 283 } 284 CarBleTrustAgent.this.addEscrowToken(token, UserHandle.of(uid)); 285 } 286 287 @Override 288 public void removeEscrowToken(long handle, int uid) { 289 if (Log.isLoggable(TAG, Log.DEBUG)) { 290 Log.d(TAG, 291 "removeEscrowToken. uid: " + ActivityManager.getCurrentUser() 292 + " handle: " + handle); 293 } 294 CarBleTrustAgent.this.removeEscrowToken(handle, 295 UserHandle.of(uid)); 296 } 297 298 @Override 299 public void isEscrowTokenActive(long handle, int uid) { 300 CarBleTrustAgent.this.isEscrowTokenActive(handle, UserHandle.of(uid)); 301 } 302 }; 303 304 /** 305 * Implementation of the {@link CarTrustAgentUnlockDelegate} 306 */ 307 private final CarTrustAgentUnlockDelegate mUnlockDelegate = new CarTrustAgentUnlockDelegate() { 308 /** 309 * Pass the user and token credentials to authenticate with the LockSettingsService. 310 * 311 * @param user user being authorized 312 * @param token escrow token for the user 313 * @param handle the handle corresponding to the escrow token 314 */ 315 @Override 316 public void onUnlockDataReceived(int user, byte[] token, long handle) { 317 if (Log.isLoggable(TAG, Log.DEBUG)) { 318 Log.d(TAG, "onUnlockDataReceived:" + user + " token: " + Long.toHexString( 319 Utils.bytesToLong(token)) + " handle: " + Long.toHexString(handle)); 320 } 321 if (ActivityManager.getCurrentUser() != user) { 322 // Current behavior is to only authenticate the user we have booted into. 323 // TODO(b/129029418) Make identification & Auth vs Auth-only a 324 // configurable option 325 Log.e(TAG, "Expected User: " + ActivityManager.getCurrentUser() 326 + " Presented User: " + user); 327 return; 328 } else { 329 unlockUserInternally(user, token, handle); 330 logUnlockEvent(USER_UNLOCKED); 331 } 332 333 } 334 }; 335 } 336