diff options
author | Hongwei Wang <hwwang@google.com> | 2018-06-05 16:42:06 -0700 |
---|---|---|
committer | Hongwei Wang <hwwang@google.com> | 2018-06-21 14:36:19 -0700 |
commit | 25fa6f2e47fc034cc6bef7be6d0fae6889194f1d (patch) | |
tree | 537e7b22f40dc18377baa765765d6221b53ca338 | |
parent | 12eecfd9600c0f48b3652733f60c29e70ad6650b (diff) | |
download | Car-25fa6f2e47fc034cc6bef7be6d0fae6889194f1d.tar.gz |
Associate user id with unlock token handle
Note: this assumes user-0 is always unlocked otherwise one would not
be able to access shared preference for user-0 when cold booted to lock
screen.
To test this CL, turn on the following system property
android.car.systemuser.headless=true
This build flag will make Android boot into a headless user-0 model and
user-0 will always be unlocked.
Bluetooth stack gets restarted every time switching user. This CL also
ensures the BLE trust agent service is started after bluetooth finishes
rebooting.
Current limitations:
- Unlock token works only if user sets password
- Unlock token is not activated till user finishes lock-unlock once
Bug: 78602296
Bug: 77854782
Test: unlock user by trust device on Mojave
Change-Id: Ifc2bf97593adbb77d518d6b11c06ea5234a4ebbd
5 files changed, 180 insertions, 106 deletions
diff --git a/TrustAgent/res/values/strings.xml b/TrustAgent/res/values/strings.xml index d077262102..192b7e4b99 100644 --- a/TrustAgent/res/values/strings.xml +++ b/TrustAgent/res/values/strings.xml @@ -36,4 +36,6 @@ <string name="start_advertising">Start Advertising</string> <string name="revoke_trust">Revoke Trust</string> + <string translatable="false" name="token_handle_shared_preferences">com.android.car.trust.TOKEN_HANDLE</string> + </resources> diff --git a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java index 227ea8919e..1dfbd504a1 100644 --- a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java +++ b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java @@ -16,27 +16,25 @@ package com.android.car.trust; -import android.bluetooth.BluetoothDevice; -import android.bluetooth.BluetoothGattServer; -import android.bluetooth.BluetoothGattServerCallback; -import android.bluetooth.BluetoothManager; +import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.UserSwitchObserver; +import android.bluetooth.BluetoothAdapter; import android.car.trust.ICarTrustAgentBleService; import android.car.trust.ICarTrustAgentTokenRequestDelegate; import android.car.trust.ICarTrustAgentUnlockCallback; +import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.ServiceConnection; -import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; -import android.os.UserManager; import android.service.trust.TrustAgentService; import android.util.Log; -import java.util.concurrent.TimeUnit; - /** * A BluetoothLE (BLE) based {@link TrustAgentService} that uses the escrow token unlock APIs. </p> * @@ -49,39 +47,44 @@ public class CarBleTrustAgent extends TrustAgentService { private static final String TAG = CarBleTrustAgent.class.getSimpleName(); - private static final long TRUST_DURATION_MS = TimeUnit.MINUTES.toMicros(5); - private static final long BLE_RETRY_MS = TimeUnit.SECONDS.toMillis(1); - - private Handler mHandler; - private BluetoothManager mBluetoothManager; - private ICarTrustAgentBleService mCarTrustAgentBleService; - private boolean mCarTrustAgentBleServiceBound; - + /** + * {@link CarTrustAgentBleService} will callback this function when it receives both + * handle and token. + */ private final ICarTrustAgentUnlockCallback mUnlockCallback = new ICarTrustAgentUnlockCallback.Stub() { @Override - public void onUnlockDataReceived(byte[] token, long handle) { - UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - // TODO(b/77854782): get the actual user to unlock by token - UserHandle userHandle = getForegroundUserHandle(); - - Log.d(TAG, "About to unlock user. Handle: " + handle - + " Time: " + System.currentTimeMillis()); - unlockUserWithToken(handle, token, userHandle); - - Log.d(TAG, "Attempted to unlock user, is user unlocked: " - + um.isUserUnlocked(userHandle) - + " Time: " + System.currentTimeMillis()); - setManagingTrust(true); + public void onUnlockDataReceived(byte[] token, long handle) throws RemoteException { + UserHandle userHandle = getUserHandleByTokenHandle(handle); + if (userHandle == null) { + Log.e(TAG, "Unable to find user by token handle " + handle); + return; + } - if (um.isUserUnlocked(userHandle)) { - Log.d(TAG, getString(R.string.trust_granted_explanation)); - grantTrust("Granting trust from escrow token", - TRUST_DURATION_MS, FLAG_GRANT_TRUST_DISMISS_KEYGUARD); + int uid = userHandle.getIdentifier(); + if (ActivityManager.getCurrentUser() != uid) { + Log.d(TAG, "Switch to user: " + uid); + // Try to unlock when user switch completes + ActivityManager.getService().registerUserSwitchObserver( + getUserSwitchObserver(uid, token, handle), TAG); + ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); + am.switchUser(uid); + } else { + unlockUserInternally(uid, token, handle); } } }; + /** + * Delegates the escrow token API calls from {@link CarTrustAgentBleService} to + * {@link TrustAgentService}. Due to the asynchronous nature, the results will be posted to + * {@link CarTrustAgentBleService} by the following calls + * <ul> + * <li>{@link #onEscrowTokenAdded(byte[], long, UserHandle)}</li> + * <li>{@link #onEscrowTokenRemoved(long, boolean)}</li> + * <li>{@link #onEscrowTokenStateReceived(long, int)}</li> + * </ul> + */ private final ICarTrustAgentTokenRequestDelegate mTokenRequestDelegate = new ICarTrustAgentTokenRequestDelegate.Stub() { @Override @@ -105,6 +108,9 @@ public class CarBleTrustAgent extends TrustAgentService { } }; + /** + * Service connection to {@link CarTrustAgentBleService} + */ private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { @@ -114,7 +120,6 @@ public class CarBleTrustAgent extends TrustAgentService { try { mCarTrustAgentBleService.registerUnlockCallback(mUnlockCallback); mCarTrustAgentBleService.setTokenRequestDelegate(mTokenRequestDelegate); - maybeStartBleUnlockService(); } catch (RemoteException e) { Log.e(TAG, "Error registerUnlockCallback", e); } @@ -122,10 +127,12 @@ public class CarBleTrustAgent extends TrustAgentService { @Override public void onServiceDisconnected(ComponentName name) { + Log.d(TAG, "CarTrustAgentBleService disconnected"); if (mCarTrustAgentBleService != null) { try { mCarTrustAgentBleService.unregisterUnlockCallback(mUnlockCallback); mCarTrustAgentBleService.setTokenRequestDelegate(null); + mCarTrustAgentBleService.stopUnlockAdvertising(); } catch (RemoteException e) { Log.e(TAG, "Error unregisterUnlockCallback", e); } @@ -135,45 +142,57 @@ public class CarBleTrustAgent extends TrustAgentService { } }; + /** + * Receives the bluetooth state change broadcasts. Bluetooth is restarted when switching user, + * we need to ensure calling {@link ICarTrustAgentBleService#startUnlockAdvertising} after + * bluetooth is started. + */ + private final BroadcastReceiver mBluetoothBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case BluetoothAdapter.ACTION_STATE_CHANGED: + onBluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)); + break; + } + } + }; + + private ICarTrustAgentBleService mCarTrustAgentBleService; + private boolean mCarTrustAgentBleServiceBound; + + /** + * TODO: Currently it relies on {@link #onDeviceLocked()} and {@link #onDeviceUnlocked()} + * callback, and these callbacks won't happen if the user has unlocked once. + */ + private boolean mIsOnLockScreen; + @Override public void onCreate() { super.onCreate(); - - Log.d(TAG, "Bluetooth trust agent starting up"); - mHandler = new Handler(); - mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); - - // If the user is already unlocked, don't bother starting the BLE service. - UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - if (!um.isUserUnlocked(getForegroundUserHandle())) { - Log.d(TAG, "User locked, will now bind CarTrustAgentBleService"); - Intent intent = new Intent(this, CarTrustAgentBleService.class); - bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); - } else { - setManagingTrust(true); - } + setManagingTrust(true); + bindService(new Intent(this, CarTrustAgentBleService.class), + mServiceConnection, Context.BIND_AUTO_CREATE); + IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); + registerReceiver(mBluetoothBroadcastReceiver, intentFilter); } @Override public void onDestroy() { Log.d(TAG, "Car Trust agent shutting down"); - mHandler.removeCallbacks(null); - - // Unbind the service to avoid leaks from BLE stack. if (mCarTrustAgentBleServiceBound) { unbindService(mServiceConnection); } + unregisterReceiver(mBluetoothBroadcastReceiver); super.onDestroy(); } @Override public void onDeviceLocked() { super.onDeviceLocked(); + mIsOnLockScreen = true; if (mCarTrustAgentBleServiceBound) { try { - // Only one BLE advertising is allowed, ensure enrolment advertising is stopped - // before start unlock advertising. - mCarTrustAgentBleService.stopEnrolmentAdvertising(); mCarTrustAgentBleService.startUnlockAdvertising(); } catch (RemoteException e) { Log.e(TAG, "Error startUnlockAdvertising", e); @@ -184,36 +203,47 @@ public class CarBleTrustAgent extends TrustAgentService { @Override public void onDeviceUnlocked() { super.onDeviceUnlocked(); + mIsOnLockScreen = false; if (mCarTrustAgentBleServiceBound) { try { - // Only one BLE advertising is allowed, ensure unlock advertising is stopped. mCarTrustAgentBleService.stopUnlockAdvertising(); } catch (RemoteException e) { Log.e(TAG, "Error stopUnlockAdvertising", e); } } + // Revoke trust right after to enable keyguard when switching user + revokeTrust(); } - private void maybeStartBleUnlockService() { - Log.d(TAG, "Trying to open a Ble GATT server"); - BluetoothGattServer gattServer = mBluetoothManager.openGattServer( - this, new BluetoothGattServerCallback() { + private UserSwitchObserver getUserSwitchObserver(int uid, + byte[] token, long handle) { + return new UserSwitchObserver() { + @Override + public void onUserSwitchComplete(int newUserId) throws RemoteException { + if (uid != newUserId) return; + unlockUserInternally(uid, token, handle); + ActivityManager.getService().unregisterUserSwitchObserver(this); + } + @Override - public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { - super.onConnectionStateChange(device, status, newState); + public void onLockedBootComplete(int newUserId) { + // ignored. } - }); + }; + } + + private void unlockUserInternally(int uid, byte[] token, long handle) { + Log.d(TAG, "About to unlock user: " + uid); + unlockUserWithToken(handle, token, UserHandle.of(uid)); + grantTrust("Granting trust from escrow token", + 0, FLAG_GRANT_TRUST_DISMISS_KEYGUARD); + } - // The BLE stack is started up before the trust agent service, however Gatt capabilities - // might not be ready just yet. Keep trying until a GattServer can open up before proceeding - // to start the rest of the BLE services. - if (gattServer == null) { - Log.e(TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms"); - mHandler.postDelayed(this::maybeStartBleUnlockService, BLE_RETRY_MS); - } else { - mHandler.removeCallbacks(null); - gattServer.close(); - Log.d(TAG, "GATT available, starting up UnlockService"); + private void onBluetoothStateChanged(int state) { + Log.d(TAG, "onBluetoothStateChanged: " + state); + if ((state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_BLE_ON) + && mCarTrustAgentBleServiceBound + && mIsOnLockScreen) { try { mCarTrustAgentBleService.startUnlockAdvertising(); } catch (RemoteException e) { @@ -259,12 +289,15 @@ public class CarBleTrustAgent extends TrustAgentService { } } - /** - * TODO(b/77854782): return the {@link UserHandle} of foreground user. - * CarBleTrustAgent itself runs as user-0 - */ - private UserHandle getForegroundUserHandle() { - Log.d(TAG, "getForegroundUserHandle for " + UserHandle.myUserId()); - return UserHandle.of(UserHandle.myUserId()); + private @Nullable UserHandle getUserHandleByTokenHandle(long tokenHandle) { + if (mCarTrustAgentBleServiceBound) { + try { + int userId = mCarTrustAgentBleService.getUserIdByEscrowTokenHandle(tokenHandle); + return userId < 0 ? null : UserHandle.of(userId); + } catch (RemoteException e) { + Log.e(TAG, "Error getUserHandleByTokenHandle"); + } + } + return null; } } diff --git a/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java b/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java index d7160e3435..4b587d1419 100644 --- a/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java +++ b/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java @@ -46,13 +46,16 @@ public class CarEnrolmentActivity extends Activity { private static final String SP_HANDLE_KEY = "sp-test"; private static final int FINE_LOCATION_REQUEST_CODE = 42; + /** + * Receives escrow token callbacks, registered on {@link CarTrustAgentBleService} + */ private final ICarTrustAgentTokenResponseCallback mCarTrustAgentTokenResponseCallback = new ICarTrustAgentTokenResponseCallback.Stub() { @Override public void onEscrowTokenAdded(byte[] token, long handle, int uid) { runOnUiThread(() -> { mPrefs.edit().putLong(SP_HANDLE_KEY, handle).apply(); - Log.d(TAG, "stored new handle"); + Log.d(TAG, "stored new handle for user: " + uid); }); if (mBluetoothDevice == null) { @@ -81,6 +84,9 @@ public class CarEnrolmentActivity extends Activity { } }; + /** + * Receives BLE state change callbacks, registered on {@link CarTrustAgentBleService} + */ private final ICarTrustAgentBleCallback mBleConnectionCallback = new ICarTrustAgentBleCallback.Stub() { @Override @@ -108,6 +114,12 @@ public class CarEnrolmentActivity extends Activity { } }; + /** + * {@link CarTrustAgentBleService} will callback this when receives enrolment data. + * + * Here is the place we can prompt to the user on HU whether or not to add this + * {@link #mBluetoothDevice} as a trust device. + */ private final ICarTrustAgentEnrolmentCallback mEnrolmentCallback = new ICarTrustAgentEnrolmentCallback.Stub() { @Override @@ -121,6 +133,9 @@ public class CarEnrolmentActivity extends Activity { } }; + /** + * Service connection to {@link CarTrustAgentBleService} + */ private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { @@ -135,6 +150,7 @@ public class CarEnrolmentActivity extends Activity { } catch (RemoteException e) { Log.e(TAG, "Error startEnrolmentAdvertising", e); } + checkTokenHandle(); } @Override @@ -169,8 +185,10 @@ public class CarEnrolmentActivity extends Activity { mPrefs = PreferenceManager.getDefaultSharedPreferences(this /* context */); findViewById(R.id.start_button).setOnClickListener((view) -> { - Intent bindIntent = new Intent(this, CarTrustAgentBleService.class); - bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + if (!mCarTrustAgentBleServiceBound) { + Intent bindIntent = new Intent(this, CarTrustAgentBleService.class); + bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE); + } }); findViewById(R.id.revoke_trust_button).setOnClickListener((view) -> { @@ -193,33 +211,17 @@ public class CarEnrolmentActivity extends Activity { requestPermissions( new String[] { android.Manifest.permission.ACCESS_FINE_LOCATION }, FINE_LOCATION_REQUEST_CODE); - } else { - long tokenHandle = getTokenHandle(); - if (tokenHandle != -1) { - Log.d(TAG, "onResume, checking handle active: " + tokenHandle); - if (mCarTrustAgentBleServiceBound) { - try { - // Due to the asynchronous nature of isEscrowTokenActive in - // TrustAgentService, query result will be delivered via - // {@link #mCarTrustAgentTokenResponseCallback} - mCarTrustAgentBleService.isEscrowTokenActive(tokenHandle, - UserHandle.myUserId()); - } catch (RemoteException e) { - Log.e(TAG, "Error isEscrowTokenActive", e); - } - } - } else { - appendOutputText("No handles found"); - } } } @Override - protected void onDestroy() { + protected void onStop() { + super.onStop(); + if (mCarTrustAgentBleServiceBound) { unbindService(mServiceConnection); + mCarTrustAgentBleServiceBound = false; } - super.onDestroy(); } private void appendOutputText(final String text) { @@ -234,7 +236,23 @@ public class CarEnrolmentActivity extends Activity { mCarTrustAgentBleService.addEscrowToken(token, UserHandle.myUserId()); } - private long getTokenHandle() { - return mPrefs.getLong(SP_HANDLE_KEY, -1); + private void checkTokenHandle() { + long tokenHandle = mPrefs.getLong(SP_HANDLE_KEY, -1); + if (tokenHandle != -1) { + Log.d(TAG, "Checking handle active: " + tokenHandle); + if (mCarTrustAgentBleServiceBound) { + try { + // Due to the asynchronous nature of isEscrowTokenActive in + // TrustAgentService, query result will be delivered via + // {@link #mCarTrustAgentTokenResponseCallback} + mCarTrustAgentBleService.isEscrowTokenActive(tokenHandle, + UserHandle.myUserId()); + } catch (RemoteException e) { + Log.e(TAG, "Error isEscrowTokenActive", e); + } + } + } else { + appendOutputText("No handles found"); + } } } diff --git a/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java b/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java index f8ba90f36a..2cc2080089 100644 --- a/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java +++ b/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java @@ -25,6 +25,7 @@ import android.car.trust.ICarTrustAgentTokenRequestDelegate; import android.car.trust.ICarTrustAgentTokenResponseCallback; import android.car.trust.ICarTrustAgentUnlockCallback; import android.content.Intent; +import android.content.SharedPreferences; import android.os.IBinder; import android.os.ParcelUuid; import android.os.RemoteCallbackList; @@ -62,6 +63,8 @@ public class CarTrustAgentBleService extends SimpleBleServer { private byte[] mCurrentUnlockToken; private Long mCurrentUnlockHandle; + private SharedPreferences mTokenHandleSharedPreferences; + @Override public void onCreate() { super.onCreate(); @@ -72,6 +75,10 @@ public class CarTrustAgentBleService extends SimpleBleServer { setupEnrolmentBleServer(); setupUnlockBleServer(); + + mTokenHandleSharedPreferences = getSharedPreferences( + getString(R.string.token_handle_shared_preferences), + MODE_PRIVATE); } @Override @@ -260,8 +267,8 @@ public class CarTrustAgentBleService extends SimpleBleServer { @Override public void startEnrolmentAdvertising() { - Log.d(TAG, "startEnrolmentAdvertising"); stopUnlockAdvertising(); + Log.d(TAG, "startEnrolmentAdvertising"); startAdvertising(mEnrolmentUuid, mEnrolmentGattServer); } @@ -290,8 +297,8 @@ public class CarTrustAgentBleService extends SimpleBleServer { @Override public void startUnlockAdvertising() { - Log.d(TAG, "startUnlockAdvertising"); stopEnrolmentAdvertising(); + Log.d(TAG, "startUnlockAdvertising"); startAdvertising(mUnlockUuid, mUnlockGattServer); } @@ -353,6 +360,9 @@ public class CarTrustAgentBleService extends SimpleBleServer { public void onEscrowTokenAdded(byte[] token, long handle, int uid) throws RemoteException { Log.d(TAG, "onEscrowTokenAdded handle:" + handle + " uid:" + uid); + mTokenHandleSharedPreferences.edit() + .putInt(String.valueOf(handle), uid) + .apply(); if (mTokenResponseCallback != null) { mTokenResponseCallback.onEscrowTokenAdded(token, handle, uid); } @@ -361,6 +371,9 @@ public class CarTrustAgentBleService extends SimpleBleServer { @Override public void onEscrowTokenRemoved(long handle, boolean successful) throws RemoteException { Log.d(TAG, "onEscrowTokenRemoved handle:" + handle); + mTokenHandleSharedPreferences.edit() + .remove(String.valueOf(handle)) + .apply(); if (mTokenResponseCallback != null) { mTokenResponseCallback.onEscrowTokenRemoved(handle, successful); } @@ -373,5 +386,10 @@ public class CarTrustAgentBleService extends SimpleBleServer { mTokenResponseCallback.onEscrowTokenActiveStateChanged(handle, active); } } + + @Override + public int getUserIdByEscrowTokenHandle(long tokenHandle) { + return mTokenHandleSharedPreferences.getInt(String.valueOf(tokenHandle), -1); + } } } diff --git a/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl b/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl index 8b50fd379a..4dec6a0a43 100644 --- a/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl +++ b/car-lib/src/android/car/trust/ICarTrustAgentBleService.aidl @@ -58,4 +58,7 @@ interface ICarTrustAgentBleService { void onEscrowTokenAdded(in byte[] token, long handle, int uid); void onEscrowTokenRemoved(long handle, boolean successful); void onEscrowTokenActiveStateChanged(long handle, boolean active); + + /** Management */ + int getUserIdByEscrowTokenHandle(long tokenHandle); } |