CarBleTrustAgent.java revision 7e816c5f1a3bdca5ea8a675857303d06fbc4a155
1/* 2 * Copyright (C) 2016 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 17package com.android.car.trust; 18 19import android.bluetooth.BluetoothDevice; 20import android.bluetooth.BluetoothGattServer; 21import android.bluetooth.BluetoothGattServerCallback; 22import android.bluetooth.BluetoothManager; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.Context; 26import android.content.Intent; 27import android.content.IntentFilter; 28import android.content.ServiceConnection; 29import android.os.Handler; 30import android.os.IBinder; 31import android.os.UserHandle; 32import android.os.UserManager; 33import android.service.trust.TrustAgentService; 34import android.support.v4.content.LocalBroadcastManager; 35import android.util.Log; 36import com.android.car.trust.comms.SimpleBleServer; 37 38import java.util.concurrent.TimeUnit; 39 40/** 41 * A sample trust agent that demonstrates how to use the escrow token unlock APIs. </p> 42 * 43 * This trust agent runs during direct boot and binds to a BLE service that listens for remote 44 * devices to trigger an unlock. <p/> 45 * 46 * The permissions for this agent must be enabled as priv-app permissions for it to start. 47 */ 48public class CarBleTrustAgent extends TrustAgentService { 49 public static final String ACTION_REVOKE_TRUST = "revoke-trust-action"; 50 public static final String ACTION_ADD_TOKEN = "add-token-action"; 51 public static final String ACTION_IS_TOKEN_ACTIVE = "is-token-active-action"; 52 public static final String ACTION_REMOVE_TOKEN = "remove-token-action"; 53 public static final String ACTION_UNLOCK_DEVICE = "unlock-device-action"; 54 55 public static final String ACTION_TOKEN_STATUS_RESULT = "token-status-result-action"; 56 public static final String ACTION_ADD_TOKEN_RESULT = "add-token-result-action"; 57 58 public static final String INTENT_EXTRA_ESCROW_TOKEN = "extra-escrow-token"; 59 public static final String INTENT_EXTRA_TOKEN_HANDLE = "extra-token-handle"; 60 public static final String INTENT_EXTRA_TOKEN_STATUS = "extra-token-status"; 61 62 63 private static final String TAG = "CarBleTrustAgent"; 64 65 private static final long TRUST_DURATION_MS = TimeUnit.MINUTES.toMicros(5); 66 private static final long BLE_RETRY_MS = TimeUnit.SECONDS.toMillis(1); 67 68 private CarUnlockService mCarUnlockService; 69 private LocalBroadcastManager mLocalBroadcastManager; 70 71 private boolean mBleServiceBound; 72 73 // We cannot directly bind to TrustAgentService since the onBind method is final. 74 // As a result, we communicate with the various UI components using a LocalBroadcastManager. 75 private final BroadcastReceiver mTrustEventReceiver = new BroadcastReceiver() { 76 @Override 77 public void onReceive(Context context, Intent intent) { 78 String action = intent.getAction(); 79 if (Log.isLoggable(TAG, Log.DEBUG)) { 80 Log.d(TAG, "Received broadcast: " + action); 81 } 82 if (ACTION_REVOKE_TRUST.equals(action)) { 83 revokeTrust(); 84 } else if (ACTION_ADD_TOKEN.equals(action)) { 85 byte[] token = intent.getByteArrayExtra(INTENT_EXTRA_ESCROW_TOKEN); 86 addEscrowToken(token, getCurrentUserHandle()); 87 } else if (ACTION_IS_TOKEN_ACTIVE.equals(action)) { 88 long handle = intent.getLongExtra(INTENT_EXTRA_TOKEN_HANDLE, -1); 89 isEscrowTokenActive(handle, getCurrentUserHandle()); 90 } else if (ACTION_REMOVE_TOKEN.equals(action)) { 91 long handle = intent.getLongExtra(INTENT_EXTRA_TOKEN_HANDLE, -1); 92 removeEscrowToken(handle, getCurrentUserHandle()); 93 } 94 } 95 }; 96 97 @Override 98 public void onTrustTimeout() { 99 super.onTrustTimeout(); 100 if (Log.isLoggable(TAG, Log.DEBUG)) { 101 Log.d(TAG, "onTrustTimeout(): timeout expired"); 102 } 103 } 104 105 @Override 106 public void onCreate() { 107 super.onCreate(); 108 109 if (Log.isLoggable(TAG, Log.DEBUG)) { 110 Log.d(TAG, "Bluetooth trust agent starting up"); 111 } 112 IntentFilter filter = new IntentFilter(); 113 filter.addAction(ACTION_REVOKE_TRUST); 114 filter.addAction(ACTION_ADD_TOKEN); 115 filter.addAction(ACTION_IS_TOKEN_ACTIVE); 116 filter.addAction(ACTION_REMOVE_TOKEN); 117 118 mLocalBroadcastManager = LocalBroadcastManager.getInstance(this /* context */); 119 mLocalBroadcastManager.registerReceiver(mTrustEventReceiver, filter); 120 121 // If the user is already unlocked, don't bother starting the BLE service. 122 UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 123 if (!um.isUserUnlocked()) { 124 if (Log.isLoggable(TAG, Log.DEBUG)) { 125 Log.d(TAG, "User locked, will now bind CarUnlockService"); 126 } 127 Intent intent = new Intent(this, CarUnlockService.class); 128 129 bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); 130 } else { 131 setManagingTrust(true); 132 } 133 } 134 135 @Override 136 public void onDestroy() { 137 if (Log.isLoggable(TAG, Log.DEBUG)) { 138 Log.d(TAG, "Car Trust agent shutting down"); 139 } 140 mLocalBroadcastManager.unregisterReceiver(mTrustEventReceiver); 141 142 // Unbind the service to avoid leaks from BLE stack. 143 if (mBleServiceBound) { 144 unbindService(mServiceConnection); 145 } 146 super.onDestroy(); 147 } 148 149 private SimpleBleServer.ConnectionListener mConnectionListener 150 = new SimpleBleServer.ConnectionListener() { 151 @Override 152 public void onServerStarted() { 153 if (Log.isLoggable(TAG, Log.DEBUG)) { 154 Log.d(TAG, "BLE server started"); 155 } 156 } 157 158 @Override 159 public void onServerStartFailed(int errorCode) { 160 Log.w(TAG, "BLE server failed to start. Error Code: " + errorCode); 161 } 162 163 @Override 164 public void onDeviceConnected(BluetoothDevice device) { 165 if (Log.isLoggable(TAG, Log.DEBUG)) { 166 Log.d(TAG, "BLE device connected. Name: " + device.getName() 167 + " Address: " + device.getAddress()); 168 } 169 } 170 }; 171 172 private CarUnlockService.UnlockServiceCallback mUnlockCallback 173 = new CarUnlockService.UnlockServiceCallback() { 174 @Override 175 public void unlockDevice(byte[] token, long handle) { 176 unlock(token, handle); 177 } 178 }; 179 180 private ServiceConnection mServiceConnection = new ServiceConnection() { 181 182 public void onServiceConnected(ComponentName className, IBinder service) { 183 if (Log.isLoggable(TAG, Log.DEBUG)) { 184 Log.d(TAG, "CarUnlockService connected"); 185 } 186 187 mBleServiceBound = true; 188 CarUnlockService.UnlockServiceBinder binder 189 = (CarUnlockService.UnlockServiceBinder) service; 190 mCarUnlockService = binder.getService(); 191 mCarUnlockService.addUnlockServiceCallback(mUnlockCallback); 192 mCarUnlockService.addConnectionListener(mConnectionListener); 193 maybeStartBleUnlockService(); 194 } 195 196 public void onServiceDisconnected(ComponentName arg0) { 197 mCarUnlockService = null; 198 mBleServiceBound = false; 199 } 200 201 }; 202 203 private void maybeStartBleUnlockService() { 204 if (Log.isLoggable(TAG, Log.DEBUG)) { 205 Log.d(TAG, "Trying to open a Ble GATT server"); 206 } 207 208 BluetoothManager btManager = 209 (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); 210 BluetoothGattServer mGattServer 211 = btManager.openGattServer(this, new BluetoothGattServerCallback() { 212 @Override 213 public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { 214 super.onConnectionStateChange(device, status, newState); 215 } 216 }); 217 218 // The BLE stack is started up before the trust agent service, however Gatt capabilities 219 // might not be ready just yet. Keep trying until a GattServer can open up before proceeding 220 // to start the rest of the BLE services. 221 if (mGattServer == null) { 222 Log.e(TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms"); 223 224 Handler handler = new Handler(); 225 handler.postDelayed(new Runnable() { 226 @Override 227 public void run() { 228 maybeStartBleUnlockService(); 229 } 230 }, BLE_RETRY_MS); 231 } else { 232 mGattServer.close(); 233 if (Log.isLoggable(TAG, Log.DEBUG)) { 234 Log.d(TAG, "GATT available, starting up UnlockService"); 235 } 236 mCarUnlockService.start(); 237 } 238 } 239 240 private void unlock(byte[] token, long handle) { 241 UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); 242 243 if (Log.isLoggable(TAG, Log.DEBUG)) { 244 Log.d(TAG, "About to unlock user. Current handle: " + handle 245 + " Time: " + System.currentTimeMillis()); 246 } 247 unlockUserWithToken(handle, token, getCurrentUserHandle()); 248 249 if (Log.isLoggable(TAG, Log.DEBUG)) { 250 Log.d(TAG, "Attempted to unlock user, is user unlocked? " + um.isUserUnlocked() 251 + " Time: " + System.currentTimeMillis()); 252 } 253 setManagingTrust(true); 254 255 if (um.isUserUnlocked()) { 256 if (Log.isLoggable(TAG, Log.DEBUG)) { 257 Log.d(TAG, getString(R.string.trust_granted_explanation)); 258 } 259 grantTrust("Granting trust from escrow token", 260 TRUST_DURATION_MS, FLAG_GRANT_TRUST_DISMISS_KEYGUARD); 261 // Trust has been granted, disable the BLE server. This trust agent service does 262 // not need to receive additional BLE data. 263 unbindService(mServiceConnection); 264 } 265 } 266 267 @Override 268 public void onEscrowTokenRemoved(long handle, boolean successful) { 269 if (Log.isLoggable(TAG, Log.DEBUG)) { 270 Log.d(TAG, "onEscrowTokenRemoved. Handle: " + handle + " successful? " + successful); 271 } 272 } 273 274 @Override 275 public void onEscrowTokenStateReceived(long handle, int tokenState) { 276 boolean isActive = tokenState == TOKEN_STATE_ACTIVE; 277 if (Log.isLoggable(TAG, Log.DEBUG)) { 278 Log.d(TAG, "Token handle: " + handle + " isActive: " + isActive); 279 } 280 281 Intent intent = new Intent(); 282 intent.setAction(ACTION_TOKEN_STATUS_RESULT); 283 intent.putExtra(INTENT_EXTRA_TOKEN_STATUS, isActive); 284 285 mLocalBroadcastManager.sendBroadcast(intent); 286 } 287 288 @Override 289 public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) { 290 if (Log.isLoggable(TAG, Log.DEBUG)) { 291 Log.d(TAG, "onEscrowTokenAdded, handle: " + handle); 292 } 293 294 Intent intent = new Intent(); 295 intent.setAction(ACTION_ADD_TOKEN_RESULT); 296 intent.putExtra(INTENT_EXTRA_TOKEN_HANDLE, handle); 297 298 mLocalBroadcastManager.sendBroadcast(intent); 299 } 300 301 private UserHandle getCurrentUserHandle() { 302 return UserHandle.of(UserHandle.myUserId()); 303 } 304} 305