New Manager for Trusted device enrollment.
Adding a new Car Manager with APIs to facilitate trusted device
enrollment. The service implementation doesn't do anything yet, will
add that in a follow up change.
Bug: 119826696
Test: Build and flash.
Change-Id: I4b7454c0c58ce76daadc1efebac4058ef7573b81
diff --git a/car-lib/api/system-current.txt b/car-lib/api/system-current.txt
index a0046eb..d7305a8 100644
--- a/car-lib/api/system-current.txt
+++ b/car-lib/api/system-current.txt
@@ -10,6 +10,7 @@
field public static final java.lang.String PERMISSION_CAR_DRIVING_STATE = "android.car.permission.CAR_DRIVING_STATE";
field public static final java.lang.String PERMISSION_CAR_DYNAMICS_STATE = "android.car.permission.CAR_DYNAMICS_STATE";
field public static final java.lang.String PERMISSION_CAR_ENGINE_DETAILED = "android.car.permission.CAR_ENGINE_DETAILED";
+ field public static final java.lang.String PERMISSION_CAR_ENROLL_TRUST = "android.car.permission.CAR_ENROLL_TRUST";
field public static final java.lang.String PERMISSION_CAR_INSTRUMENT_CLUSTER_CONTROL = "android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL";
field public static final java.lang.String PERMISSION_CAR_POWER = "android.car.permission.CAR_POWER";
field public static final java.lang.String PERMISSION_CAR_PROJECTION = "android.car.permission.CAR_PROJECTION";
@@ -891,6 +892,38 @@
}
+package android.car.trust {
+
+ public final class CarTrustAgentEnrollmentManager {
+ method public void activateToken(long) throws android.car.CarNotConnectedException;
+ method public void enrollmentHandshakeAccepted() throws android.car.CarNotConnectedException;
+ method public java.util.List<java.lang.Integer> getEnrollmentHandlesForUser(int) throws android.car.CarNotConnectedException;
+ method public void initiateEnrollmentHandshake(android.bluetooth.BluetoothDevice) throws android.car.CarNotConnectedException;
+ method public void revokeTrust(long) throws android.car.CarNotConnectedException;
+ method public void setBleCallback(android.car.trust.CarTrustAgentEnrollmentManager.CarTrustAgentBleCallback) throws android.car.CarNotConnectedException;
+ method public void setEnrollmentCallback(android.car.trust.CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback) throws android.car.CarNotConnectedException;
+ method public void startEnrollmentAdvertising() throws android.car.CarNotConnectedException;
+ method public void stopEnrollmentAdvertising() throws android.car.CarNotConnectedException;
+ method public void terminateEnrollmentHandshake() throws android.car.CarNotConnectedException;
+ }
+
+ public static abstract interface CarTrustAgentEnrollmentManager.CarTrustAgentBleCallback {
+ method public abstract void onBleEnrollmentDeviceConnected(android.bluetooth.BluetoothDevice);
+ method public abstract void onBleEnrollmentDeviceDisconnected(android.bluetooth.BluetoothDevice);
+ method public abstract void onEnrollmentAdvertisingFailed(int);
+ method public abstract void onEnrollmentAdvertisingStarted();
+ }
+
+ public static abstract interface CarTrustAgentEnrollmentManager.CarTrustAgentEnrollmentCallback {
+ method public abstract void onAuthStringAvailable(android.bluetooth.BluetoothDevice, java.lang.String);
+ method public abstract void onEnrollmentHandshakeFailure(android.bluetooth.BluetoothDevice, int);
+ method public abstract void onEscrowTokenActiveStateChanged(long, boolean);
+ method public abstract void onEscrowTokenAdded(long);
+ method public abstract void onTrustRevoked(long, boolean);
+ }
+
+}
+
package android.car.vms {
public final class VmsAvailableLayers implements android.os.Parcelable {
diff --git a/car-lib/src/android/car/Car.java b/car-lib/src/android/car/Car.java
index b4c4509..940c518 100644
--- a/car-lib/src/android/car/Car.java
+++ b/car-lib/src/android/car/Car.java
@@ -426,6 +426,15 @@
public static final String PERMISSION_STORAGE_MONITORING =
"android.car.permission.STORAGE_MONITORING";
+ /**
+ * Permission necessary to enroll a device as a trusted authenticator device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String PERMISSION_CAR_ENROLL_TRUST =
+ "android.car.permission.CAR_ENROLL_TRUST";
+
/** Type of car connection: platform runs directly in car. */
public static final int CONNECTION_TYPE_EMBEDDED = 5;
diff --git a/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java b/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
new file mode 100644
index 0000000..48ee384
--- /dev/null
+++ b/car-lib/src/android/car/trust/CarTrustAgentEnrollmentManager.java
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.trust;
+
+import static android.car.Car.PERMISSION_CAR_ENROLL_TRUST;
+
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.bluetooth.BluetoothDevice;
+import android.car.CarManagerBase;
+import android.car.CarNotConnectedException;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+
+/**
+ * APIs to help enroll a remote device as a trusted device that can be used to authenticate a user
+ * in the head unit.
+ * <p>
+ * The call sequence to add a new trusted device from the client should be as follows:
+ * <ol>
+ * <li> setEnrollmentCallback()
+ * <li> setBleCallback(bleCallback)
+ * <li> startEnrollmentAdvertising()
+ * <li> wait for onEnrollmentAdvertisingStarted() or
+ * <li> wait for onBleEnrollmentDeviceConnected() and check if the device connected is the right
+ * one.
+ * <li> initiateEnrollmentHandhake()
+ * <li> wait for onAuthStringAvailable() to get the pairing code to display to the user
+ * <li> enrollmentHandshakeAccepted() after user confirms the pairing code
+ * <li> wait for onEscrowTokenAdded()
+ * <li> Authenticate user's credentials by showing the lock screen
+ * <li> activateToken()
+ * <li> wait for onEscrowTokenActiveStateChanged() to add the device as a trusted device and show
+ * in the list
+ * </ol>
+ *
+ * @hide
+ */
+@SystemApi
+public final class CarTrustAgentEnrollmentManager implements CarManagerBase {
+ private static final String TAG = "CarTrustEnrollMgr";
+ private final Context mContext;
+ private final ICarTrustAgentEnrollment mEnrollmentService;
+ private Object mListenerLock = new Object();
+ @GuardedBy("mListenerLock")
+ private CarTrustAgentEnrollmentCallback mEnrollmentCallback;
+ @GuardedBy("mListenerLock")
+ private CarTrustAgentBleCallback mBleCallback;
+ @GuardedBy("mListenerLock")
+ private final ListenerToEnrollmentService mListenerToEnrollmentService =
+ new ListenerToEnrollmentService(this);
+ private final ListenerToBleService mListenerToBleService = new ListenerToBleService(this);
+
+
+ /** @hide */
+ public CarTrustAgentEnrollmentManager(IBinder service, Context context, Handler handler) {
+ mContext = context;
+ mEnrollmentService = ICarTrustAgentEnrollment.Stub.asInterface(service);
+ }
+
+ /** @hide */
+ @Override
+ public synchronized void onCarDisconnected() {
+ }
+
+ /**
+ * Starts broadcasting enrollment UUID on BLE.
+ * Phones can scan and connect for the enrollment process to begin.
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void startEnrollmentAdvertising() throws CarNotConnectedException {
+ try {
+ mEnrollmentService.startEnrollmentAdvertising();
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Stops Enrollment advertising.
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void stopEnrollmentAdvertising() throws CarNotConnectedException {
+ try {
+ mEnrollmentService.stopEnrollmentAdvertising();
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Initiates the handshake with the phone for enrollment. This should be called after the
+ * user has confirmed the phone that is requesting enrollment.
+ *
+ * @param device the remote Bluetooth device that is trying to enroll.
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void initiateEnrollmentHandshake(BluetoothDevice device)
+ throws CarNotConnectedException {
+ try {
+ mEnrollmentService.initiateEnrollmentHandshake(device);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Confirms that the enrollment handshake has been accepted by the user. This should be called
+ * after the user has confirmed the verification code displayed on the UI.
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void enrollmentHandshakeAccepted() throws CarNotConnectedException {
+ try {
+ mEnrollmentService.enrollmentHandshakeAccepted();
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Provides an option to quit enrollment if the pairing code doesn't match for example.
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void terminateEnrollmentHandshake() throws CarNotConnectedException {
+ try {
+ mEnrollmentService.terminateEnrollmentHandshake();
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Activate the newly added escrow token.
+ *
+ * @param handle the handle corresponding to the escrow token
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void activateToken(long handle) throws CarNotConnectedException {
+ try {
+ mEnrollmentService.activateToken(handle);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Revoke trust for the remote device denoted by the handle.
+ *
+ * @param handle the handle associated with the escrow token
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void revokeTrust(long handle) throws CarNotConnectedException {
+ try {
+ mEnrollmentService.revokeTrust(handle);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Register for enrollment event callbacks.
+ *
+ * @param callback The callback methods to call, null to unregister
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void setEnrollmentCallback(@Nullable CarTrustAgentEnrollmentCallback callback)
+ throws CarNotConnectedException {
+ if (callback == null) {
+ unregisterEnrollmentCallback();
+ } else {
+ registerEnrollmentCallback(callback);
+ }
+ }
+
+ private void registerEnrollmentCallback(CarTrustAgentEnrollmentCallback callback)
+ throws CarNotConnectedException {
+ synchronized (mListenerLock) {
+ if (callback != null && mEnrollmentCallback == null) {
+ try {
+ mEnrollmentService.registerEnrollmentCallback(mListenerToEnrollmentService);
+ mEnrollmentCallback = callback;
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+ }
+ }
+
+ private void unregisterEnrollmentCallback() throws CarNotConnectedException {
+ synchronized (mListenerLock) {
+ if (mEnrollmentCallback != null) {
+ try {
+ mEnrollmentService.unregisterEnrollmentCallback(mListenerToEnrollmentService);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ mEnrollmentCallback = null;
+ }
+ }
+ }
+
+ /**
+ * Register for general BLE callbacks
+ *
+ * @param callback The callback methods to call, null to unregister
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public void setBleCallback(@Nullable CarTrustAgentBleCallback callback)
+ throws CarNotConnectedException {
+ if (callback == null) {
+ unregisterBleCallback();
+ } else {
+ registerBleCallback(callback);
+ }
+ }
+
+ private void registerBleCallback(CarTrustAgentBleCallback callback)
+ throws CarNotConnectedException {
+ synchronized (mListenerLock) {
+ if (callback != null && mBleCallback == null) {
+ try {
+ mEnrollmentService.registerBleCallback(mListenerToBleService);
+ mBleCallback = callback;
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+ }
+ }
+
+ private void unregisterBleCallback() throws CarNotConnectedException {
+ synchronized (mListenerLock) {
+ if (mBleCallback != null) {
+ try {
+ mEnrollmentService.unregisterBleCallback(mListenerToBleService);
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ mBleCallback = null;
+ }
+ }
+ }
+
+ /**
+ * Provides a list of enrollment handles for the given user id.
+ * Each enrollment handle corresponds to a trusted device for the given user.
+ *
+ * @param uid user id.
+ * @return list of the Enrollment handles for the user id.
+ */
+ @RequiresPermission(PERMISSION_CAR_ENROLL_TRUST)
+ public List<Integer> getEnrollmentHandlesForUser(int uid) throws CarNotConnectedException {
+ try {
+ return Arrays.stream(
+ mEnrollmentService.getEnrollmentHandlesForUser(uid)).boxed().collect(
+ Collectors.toList());
+ } catch (RemoteException e) {
+ throw new CarNotConnectedException(e);
+ }
+ }
+
+ /**
+ * Callback interface for Trusted device enrollment applications to implement. The applications
+ * get notified on various enrollment state change events.
+ */
+ public interface CarTrustAgentEnrollmentCallback {
+ /**
+ * Communicate about failure/timeouts in the handshake process.
+ *
+ * @param device the remote device trying to enroll
+ * @param errorCode information on what failed.
+ */
+ void onEnrollmentHandshakeFailure(BluetoothDevice device, int errorCode);
+
+ /**
+ * Present the pairing/authentication string to the user.
+ *
+ * @param device the remote device trying to enroll
+ * @param authString the authentication string to show to the user to confirm across
+ * both devices
+ */
+ void onAuthStringAvailable(BluetoothDevice device, String authString);
+
+ /**
+ * Escrow token was received and the Trust Agent framework has generated a corresponding
+ * handle.
+ *
+ * @param handle the handle associated with the escrow token.
+ */
+ void onEscrowTokenAdded(long handle);
+
+ /**
+ * Escrow token corresponding to the given handle has been removed.
+ *
+ * @param handle the handle associated with the escrow token.
+ * @param success status of the revoke operation.
+ */
+ void onTrustRevoked(long handle, boolean success);
+
+ /**
+ * Escrow token's active state changed.
+ *
+ * @param handle the handle associated with the escrow token
+ * @param active True if token has been activated, false if not.
+ */
+ void onEscrowTokenActiveStateChanged(long handle, boolean active);
+
+ }
+
+ /**
+ * Callback interface for Trusted device enrollment applications to implement. The applications
+ * get notified on various BLE state change events that happen during trusted device enrollment.
+ */
+ public interface CarTrustAgentBleCallback {
+ /**
+ * Indicates a remote device connected on BLE.
+ */
+ void onBleEnrollmentDeviceConnected(BluetoothDevice device);
+
+ /**
+ * Indicates a remote device disconnected on BLE.
+ */
+ void onBleEnrollmentDeviceDisconnected(BluetoothDevice device);
+
+ /**
+ * Indicates that the device is broadcasting for trusted device enrollment on BLE.
+ */
+ void onEnrollmentAdvertisingStarted();
+
+ /**
+ * Indicates a failure in BLE broadcasting for enrollment.
+ */
+ void onEnrollmentAdvertisingFailed(int errorCode);
+ }
+
+ // TODO(ramperry) - call the client callbacks from the methods of this listener to the service
+ // callbacks.
+ private class ListenerToEnrollmentService extends ICarTrustAgentEnrollmentCallback.Stub {
+ private final WeakReference<CarTrustAgentEnrollmentManager> mMgr;
+
+ ListenerToEnrollmentService(CarTrustAgentEnrollmentManager mgr) {
+ mMgr = new WeakReference<>(mgr);
+ }
+
+ /**
+ * Communicate about failure/timeouts in the handshake process.
+ */
+ public void onEnrollmentHandshakeFailure(BluetoothDevice device, int errorCode) {
+ }
+
+ /**
+ * Present the pairing/authentication string to the user.
+ */
+ public void onAuthStringAvailable(BluetoothDevice device, byte[] authString) {
+ }
+
+ /**
+ * Escrow token was received and the Trust Agent framework has generated a corresponding
+ * handle.
+ */
+ public void onEscrowTokenAdded(long handle) {
+ }
+
+ /**
+ * Escrow token corresponding to the given handle has been removed.
+ */
+ public void onTrustRevoked(long handle, boolean success) {
+ }
+
+ /**
+ * Escrow token's active state changed.
+ */
+ public void onEscrowTokenActiveStateChanged(long handle, boolean active) {
+ }
+ }
+
+ // TODO(ramperry) - call the client callbacks from the methods of this listener to the service
+ // callbacks.
+ private class ListenerToBleService extends ICarTrustAgentBleCallback.Stub {
+ private final WeakReference<CarTrustAgentEnrollmentManager> mMgr;
+
+ ListenerToBleService(CarTrustAgentEnrollmentManager mgr) {
+ mMgr = new WeakReference<>(mgr);
+ }
+
+ /**
+ * Called when the GATT server is started and BLE is successfully advertising for
+ * enrollment.
+ */
+ public void onEnrollmentAdvertisingStarted() {
+ }
+
+ /**
+ * Called when the BLE enrollment advertisement fails to start.
+ * see AdvertiseCallback#ADVERTISE_FAILED_* for possible error codes.
+ */
+ public void onEnrollmentAdvertisingFailed(int errorCode) {
+ }
+
+ /**
+ * Called when a remote device is connected on BLE.
+ */
+ public void onBleEnrollmentDeviceConnected(BluetoothDevice device) {
+ }
+
+ /**
+ * Called when a remote device is disconnected on BLE.
+ */
+ public void onBleEnrollmentDeviceDisconnected(BluetoothDevice device) {
+ }
+ }
+}
diff --git a/car-lib/src/android/car/trust/ICarTrustAgentBleCallback.aidl b/car-lib/src/android/car/trust/ICarTrustAgentBleCallback.aidl
new file mode 100644
index 0000000..e2a972d
--- /dev/null
+++ b/car-lib/src/android/car/trust/ICarTrustAgentBleCallback.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.trust;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Callback interface for BLE connection state changes during trusted device enrollment.
+ *
+ * @hide
+ */
+oneway interface ICarTrustAgentBleCallback {
+ /**
+ * Called when the GATT server is started and BLE is successfully advertising for enrollment.
+ */
+ void onEnrollmentAdvertisingStarted();
+
+ /**
+ * Called when the BLE enrollment advertisement fails to start.
+ * see AdvertiseCallback#ADVERTISE_FAILED_* for possible error codes.
+ */
+ void onEnrollmentAdvertisingFailed(int errorCode);
+
+ /**
+ * Called when a remote device is connected on BLE.
+ */
+ void onBleEnrollmentDeviceConnected(in BluetoothDevice device);
+
+ /**
+ * Called when a remote device is disconnected on BLE.
+ */
+ void onBleEnrollmentDeviceDisconnected(in BluetoothDevice device);
+}
diff --git a/car-lib/src/android/car/trust/ICarTrustAgentEnrollment.aidl b/car-lib/src/android/car/trust/ICarTrustAgentEnrollment.aidl
new file mode 100644
index 0000000..0476f70
--- /dev/null
+++ b/car-lib/src/android/car/trust/ICarTrustAgentEnrollment.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.trust;
+
+import android.bluetooth.BluetoothDevice;
+import android.car.trust.ICarTrustAgentBleCallback;
+import android.car.trust.ICarTrustAgentEnrollmentCallback;
+
+/**
+ * Binder interface for CarTrustAgentEnrollmentService. The service implements the functionality
+ * to communicate with the remote device securely to enroll the remote device as a trusted device.
+ *
+ * @hide
+ */
+interface ICarTrustAgentEnrollment {
+ void startEnrollmentAdvertising();
+ void stopEnrollmentAdvertising();
+ void initiateEnrollmentHandshake(in BluetoothDevice device);
+ void enrollmentHandshakeAccepted();
+ void terminateEnrollmentHandshake();
+ void activateToken(in long handle);
+ void revokeTrust(in long handle);
+ int[] getEnrollmentHandlesForUser(in int uid);
+ void registerEnrollmentCallback(in ICarTrustAgentEnrollmentCallback callback);
+ void unregisterEnrollmentCallback(in ICarTrustAgentEnrollmentCallback callback);
+ void registerBleCallback(in ICarTrustAgentBleCallback callback);
+ void unregisterBleCallback(in ICarTrustAgentBleCallback callback);
+}
diff --git a/car-lib/src/android/car/trust/ICarTrustAgentEnrollmentCallback.aidl b/car-lib/src/android/car/trust/ICarTrustAgentEnrollmentCallback.aidl
new file mode 100644
index 0000000..9457fec
--- /dev/null
+++ b/car-lib/src/android/car/trust/ICarTrustAgentEnrollmentCallback.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.car.trust;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Callback interface for state changes during Trusted device enrollment.
+ *
+ * @hide
+ */
+oneway interface ICarTrustAgentEnrollmentCallback {
+ /**
+ * Communicate about failure/timeouts in the handshake process.
+ */
+ void onEnrollmentHandshakeFailure(in BluetoothDevice device, in int errorCode);
+
+ /**
+ * Present the pairing/authentication string to the user.
+ */
+ void onAuthStringAvailable(in BluetoothDevice device, in byte[] authString);
+
+ /**
+ * Escrow token was received and the Trust Agent framework has generated a corresponding handle.
+ */
+ void onEscrowTokenAdded(in long handle);
+
+ /*
+ * Escrow token corresponding to the given handle has been removed.
+ */
+ void onTrustRevoked(in long handle, in boolean success);
+
+ /**
+ * Escrow token's active state changed.
+ */
+ void onEscrowTokenActiveStateChanged(in long handle, in boolean active);
+
+}
diff --git a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
new file mode 100644
index 0000000..e6aeeac
--- /dev/null
+++ b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.car.trust;
+
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
+import android.car.trust.ICarTrustAgentBleCallback;
+import android.car.trust.ICarTrustAgentEnrollment;
+import android.car.trust.ICarTrustAgentEnrollmentCallback;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.car.CarServiceBase;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A service that enables enrolling a phone as a trusted device for authenticating a user on the
+ * IHU. This implements the APIs that an enrollment app can call to conduct an enrollment.
+ */
+public class CarTrustAgentEnrollmentService extends ICarTrustAgentEnrollment.Stub implements
+ CarServiceBase {
+ private static final String TAG = "CarTrustAgentEnroll";
+ private final Context mContext;
+ // List of clients listening to Enrollment state change events.
+ private final List<EnrollmentStateClient> mEnrollmentStateClients = new ArrayList<>();
+ // List of clients listening to BLE state change events.
+ private final List<BleStateChangeClient> mBleStateChangeClients = new ArrayList<>();
+
+ public CarTrustAgentEnrollmentService(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public synchronized void init() {
+ }
+
+ @Override
+ public synchronized void release() {
+ for (EnrollmentStateClient client : mEnrollmentStateClients) {
+ client.mListenerBinder.unlinkToDeath(client, 0);
+ }
+ mEnrollmentStateClients.clear();
+ }
+
+
+ // Binder methods
+ // TODO(b/120911995) The methods don't do anything yet. The implementation will be checked in
+ // a follow up CL.
+ @Override
+ public void startEnrollmentAdvertising() {
+ }
+
+ @Override
+ public void stopEnrollmentAdvertising() {
+ }
+
+ @Override
+ public void initiateEnrollmentHandshake(BluetoothDevice device) {
+ }
+
+ @Override
+ public void enrollmentHandshakeAccepted() {
+ }
+
+ @Override
+ public void terminateEnrollmentHandshake() {
+ }
+
+ @Override
+ public void activateToken(long handle) {
+ }
+
+ @Override
+ public void revokeTrust(long handle) {
+ }
+
+ @Override
+ public int[] getEnrollmentHandlesForUser(int uid) {
+ int[] handles = {};
+ return handles;
+ }
+
+ /**
+ * Registers a {@link ICarTrustAgentEnrollmentCallback} to be notified for changes to the
+ * enrollment state.
+ *
+ * @param listener {@link ICarTrustAgentEnrollmentCallback}
+ */
+ @Override
+ public synchronized void registerEnrollmentCallback(ICarTrustAgentEnrollmentCallback listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener is null");
+ }
+ // If a new client is registering, create a new EnrollmentStateClient and add it to the list
+ // of listening clients.
+ EnrollmentStateClient client = findEnrollmentStateClientLocked(listener);
+ if (client == null) {
+ client = new EnrollmentStateClient(listener);
+ try {
+ listener.asBinder().linkToDeath(client, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot link death recipient to binder ", e);
+ return;
+ }
+ mEnrollmentStateClients.add(client);
+ }
+ }
+
+ /**
+ * Iterates through the list of registered Enrollment State Change clients -
+ * {@link EnrollmentStateClient} and finds if the given client is already registered.
+ *
+ * @param listener Listener to look for.
+ * @return the {@link EnrollmentStateClient} if found, null if not
+ */
+ @Nullable
+ private EnrollmentStateClient findEnrollmentStateClientLocked(
+ ICarTrustAgentEnrollmentCallback listener) {
+ IBinder binder = listener.asBinder();
+ // Find the listener by comparing the binder object they host.
+ for (EnrollmentStateClient client : mEnrollmentStateClients) {
+ if (client.isHoldingBinder(binder)) {
+ return client;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Unregister the given Enrollment State Change listener
+ *
+ * @param listener client to unregister
+ */
+ @Override
+ public synchronized void unregisterEnrollmentCallback(
+ ICarTrustAgentEnrollmentCallback listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener is null");
+ }
+
+ EnrollmentStateClient client = findEnrollmentStateClientLocked(listener);
+ if (client == null) {
+ Log.e(TAG, "unregisterEnrollmentCallback(): listener was not previously "
+ + "registered");
+ return;
+ }
+ listener.asBinder().unlinkToDeath(client, 0);
+ mEnrollmentStateClients.remove(client);
+ }
+
+ /**
+ * Registers a {@link ICarTrustAgentBleCallback} to be notified for changes to the BLE state
+ * changes.
+ *
+ * @param listener {@link ICarTrustAgentBleCallback}
+ */
+ @Override
+ public synchronized void registerBleCallback(ICarTrustAgentBleCallback listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener is null");
+ }
+ // If a new client is registering, create a new EnrollmentStateClient and add it to the list
+ // of listening clients.
+ BleStateChangeClient client = findBleStateClientLocked(listener);
+ if (client == null) {
+ client = new BleStateChangeClient(listener);
+ try {
+ listener.asBinder().linkToDeath(client, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Cannot link death recipient to binder " + e);
+ return;
+ }
+ mBleStateChangeClients.add(client);
+ }
+ }
+
+ /**
+ * Iterates through the list of registered BLE State Change clients -
+ * {@link BleStateChangeClient} and finds if the given client is already registered.
+ *
+ * @param listener Listener to look for.
+ * @return the {@link BleStateChangeClient} if found, null if not
+ */
+ @Nullable
+ private BleStateChangeClient findBleStateClientLocked(
+ ICarTrustAgentBleCallback listener) {
+ IBinder binder = listener.asBinder();
+ // Find the listener by comparing the binder object they host.
+ for (BleStateChangeClient client : mBleStateChangeClients) {
+ if (client.isHoldingBinder(binder)) {
+ return client;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Unregister the given BLE State Change listener
+ *
+ * @param listener client to unregister
+ */
+ @Override
+ public synchronized void unregisterBleCallback(ICarTrustAgentBleCallback listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("Listener is null");
+ }
+
+ BleStateChangeClient client = findBleStateClientLocked(listener);
+ if (client == null) {
+ Log.e(TAG, "unregisterBleCallback(): listener was not previously "
+ + "registered");
+ return;
+ }
+ listener.asBinder().unlinkToDeath(client, 0);
+ mBleStateChangeClients.remove(client);
+ }
+
+ /**
+ * Class that holds onto client related information - listener interface, process that hosts the
+ * binder object etc.
+ * <p>
+ * It also registers for death notifications of the host.
+ */
+ private class EnrollmentStateClient implements IBinder.DeathRecipient {
+ private final IBinder mListenerBinder;
+ private final ICarTrustAgentEnrollmentCallback mListener;
+
+ EnrollmentStateClient(ICarTrustAgentEnrollmentCallback listener) {
+ mListener = listener;
+ mListenerBinder = listener.asBinder();
+ }
+
+ @Override
+ public void binderDied() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Binder died " + mListenerBinder);
+ }
+ mListenerBinder.unlinkToDeath(this, 0);
+ synchronized (CarTrustAgentEnrollmentService.this) {
+ mEnrollmentStateClients.remove(this);
+ }
+ }
+
+ /**
+ * Returns if the given binder object matches to what this client info holds.
+ * Used to check if the listener asking to be registered is already registered.
+ *
+ * @return true if matches, false if not
+ */
+ public boolean isHoldingBinder(IBinder binder) {
+ return mListenerBinder == binder;
+ }
+ }
+
+ private class BleStateChangeClient implements IBinder.DeathRecipient {
+ private final IBinder mListenerBinder;
+ private final ICarTrustAgentBleCallback mListener;
+
+ BleStateChangeClient(ICarTrustAgentBleCallback listener) {
+ mListener = listener;
+ mListenerBinder = listener.asBinder();
+ }
+
+ @Override
+ public void binderDied() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Binder died " + mListenerBinder);
+ }
+ mListenerBinder.unlinkToDeath(this, 0);
+ synchronized (CarTrustAgentEnrollmentService.this) {
+ mBleStateChangeClients.remove(this);
+ }
+ }
+
+ /**
+ * Returns if the given binder object matches to what this client info holds.
+ * Used to check if the listener asking to be registered is already registered.
+ *
+ * @return true if matches, false if not
+ */
+ public boolean isHoldingBinder(IBinder binder) {
+ return mListenerBinder == binder;
+ }
+
+ }
+
+ @Override
+ public void dump(PrintWriter writer) {
+ }
+}