Merge "Adding KS example for observing changes in content"
diff --git a/car-usb-handler/AndroidManifest.xml b/car-usb-handler/AndroidManifest.xml
index 7803318..91730a6 100644
--- a/car-usb-handler/AndroidManifest.xml
+++ b/car-usb-handler/AndroidManifest.xml
@@ -15,12 +15,12 @@
 -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-        package="android.car.usb.handler" >
+        package="android.car.usb.handler">
     <uses-sdk android:minSdkVersion="25" />
-    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <uses-permission android:name="android.permission.MANAGE_USB" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
     <application android:label="@string/app_name" android:icon="@drawable/ic_launcher"
-                 android:directBootAware="true" >
+                 android:directBootAware="true">
         <activity android:name=".UsbHostManagementActivity"
                   android:theme="@android:style/Theme.Material.Light.Dialog"
                   android:launchMode="singleTop">
@@ -28,9 +28,10 @@
                 android:name="distractionOptimized"
                 android:value="true" />
         </activity>
-        <receiver android:name=".BootUsbScanner" >
+        <receiver android:name=".BootUsbScanner"
+                 android:directBootAware="true">
             <intent-filter>
-                <action android:name="android.intent.action.BOOT_COMPLETED" />
+                <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
             </intent-filter>
         </receiver>
     </application>
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
index ec9a89a..2762e6f 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbHostController.java
@@ -56,6 +56,8 @@
     private static final boolean LOCAL_LOGD = true;
     private static final boolean LOCAL_LOGV = true;
 
+    private static final int DISPATCH_RETRY_DELAY_MS = 1000;
+    private static final int DISPATCH_RETRY_ATTEMPTS = 5;
 
     private final List<UsbDeviceSettings> mEmptyList = new ArrayList<>();
     private final Context mContext;
@@ -158,16 +160,16 @@
         mCallback.processingStateChanged(true);
 
         UsbDeviceSettings settings = mUsbSettingsStorage.getSettings(device);
-        if (settings != null && mUsbResolver.dispatch(
-                    device, settings.getHandler(), settings.getAoap())) {
-            if (LOCAL_LOGV) {
-                Log.v(TAG, "Usb Device: " + device + " was sent to component: "
-                        + settings.getHandler());
-            }
-            return;
+
+        if (settings == null) {
+            resolveDevice(device);
+        } else {
+            Object obj =
+                    new UsbHostControllerHandlerDispatchData(
+                            device, settings, DISPATCH_RETRY_ATTEMPTS, true);
+            Message.obtain(mHandler, UsbHostControllerHandler.MSG_DEVICE_DISPATCH, obj)
+                    .sendToTarget();
         }
-        mCallback.titleChanged(generateTitle(mContext, device));
-        mUsbResolver.resolve(device);
     }
 
     /**
@@ -175,7 +177,17 @@
      */
     public void applyDeviceSettings(UsbDeviceSettings settings) {
         mUsbSettingsStorage.saveSettings(settings);
-        mUsbResolver.dispatch(getActiveDevice(), settings.getHandler(), settings.getAoap());
+        Message msg = mHandler.obtainMessage();
+        msg.obj =
+                new UsbHostControllerHandlerDispatchData(
+                        getActiveDevice(), settings, DISPATCH_RETRY_ATTEMPTS, false);
+        msg.what = UsbHostControllerHandler.MSG_DEVICE_DISPATCH;
+        msg.sendToTarget();
+    }
+
+    private void resolveDevice(UsbDevice device) {
+        mCallback.titleChanged(generateTitle(mContext, device));
+        mUsbResolver.resolve(device);
     }
 
     /**
@@ -244,8 +256,34 @@
         }
     }
 
+    private class UsbHostControllerHandlerDispatchData {
+        private final UsbDevice mUsbDevice;
+        private final UsbDeviceSettings mUsbDeviceSettings;
+
+        public int mRetries = 0;
+        public boolean mCanResolve = true;
+
+        public UsbHostControllerHandlerDispatchData(
+                UsbDevice usbDevice, UsbDeviceSettings usbDeviceSettings,
+                int retries, boolean canResolve) {
+            mUsbDevice = usbDevice;
+            mUsbDeviceSettings = usbDeviceSettings;
+            mRetries = retries;
+            mCanResolve = canResolve;
+        }
+
+        public UsbDevice getUsbDevice() {
+            return mUsbDevice;
+        }
+
+        public UsbDeviceSettings getUsbDeviceSettings() {
+            return mUsbDeviceSettings;
+        }
+    }
+
     private class UsbHostControllerHandler extends Handler {
         private static final int MSG_DEVICE_REMOVED = 1;
+        private static final int MSG_DEVICE_DISPATCH = 2;
 
         private static final int DEVICE_REMOVE_TIMEOUT_MS = 500;
 
@@ -263,6 +301,24 @@
                 case MSG_DEVICE_REMOVED:
                     doHandleDeviceRemoved();
                     break;
+                case MSG_DEVICE_DISPATCH:
+                    UsbHostControllerHandlerDispatchData data =
+                            (UsbHostControllerHandlerDispatchData) msg.obj;
+                    UsbDevice device = data.getUsbDevice();
+                    UsbDeviceSettings settings = data.getUsbDeviceSettings();
+                    if (!mUsbResolver.dispatch(device, settings.getHandler(), settings.getAoap())) {
+                        if (data.mRetries > 0) {
+                            --data.mRetries;
+                            Message nextMessage = Message.obtain(msg);
+                            mHandler.sendMessageDelayed(nextMessage, DISPATCH_RETRY_DELAY_MS);
+                        } else if (data.mCanResolve) {
+                            resolveDevice(device);
+                        }
+                    } else if (LOCAL_LOGV) {
+                        Log.v(TAG, "Usb Device: " + data.getUsbDevice() + " was sent to component: "
+                                + settings.getHandler());
+                    }
+                    break;
                 default:
                     Log.w(TAG, "Unhandled message: " + msg);
                     super.handleMessage(msg);
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java b/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java
index 387ae62..d571d5d 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbHostManagementActivity.java
@@ -15,17 +15,23 @@
  */
 package android.car.usb.handler;
 
+import static android.content.Intent.ACTION_USER_UNLOCKED;
+
 import android.annotation.Nullable;
 import android.app.Activity;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.hardware.usb.UsbDevice;
 import android.hardware.usb.UsbManager;
 import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
@@ -55,6 +61,39 @@
     private UsbHostController mController;
     private PackageManager mPackageManager;
 
+    private final ResolveBroadcastReceiver mResolveBroadcastReceiver
+            = new ResolveBroadcastReceiver();
+    private boolean mReceiverRegistered = false;
+
+    private void unregisterResolveBroadcastReceiver() {
+        if (mReceiverRegistered) {
+            unregisterReceiver(mResolveBroadcastReceiver);
+            mReceiverRegistered = false;
+        }
+    }
+
+    private void processDevice() {
+        UsbDevice connectedDevice = getDevice();
+
+        if (connectedDevice != null) {
+            mController.processDevice(connectedDevice);
+        } else {
+            unregisterResolveBroadcastReceiver();
+            finish();
+        }
+    }
+
+    private class ResolveBroadcastReceiver extends BroadcastReceiver {
+        public void onReceive(Context context, Intent intent) {
+            // We could have been unregistered after receiving the intent but before processing it,
+            // so make sure we are still registered.
+            if (mReceiverRegistered) {
+                processDevice();
+                unregisterResolveBroadcastReceiver();
+            }
+        }
+    }
+
     private final AdapterView.OnItemClickListener mHandlerClickListener =
             new AdapterView.OnItemClickListener() {
         @Override
@@ -85,13 +124,25 @@
     }
 
     @Override
+    public void onPause() {
+        super.onPause();
+        unregisterResolveBroadcastReceiver();
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
-        UsbDevice connectedDevice = getDevice();
-        if (connectedDevice != null) {
-            mController.processDevice(connectedDevice);
+
+        UserManager userManager = getSystemService(UserManager.class);
+        if (userManager.isUserUnlocked() || getUserId() == UserHandle.USER_SYSTEM) {
+            processDevice();
         } else {
-            finish();
+            mReceiverRegistered = true;
+            registerReceiver(mResolveBroadcastReceiver, new IntentFilter(ACTION_USER_UNLOCKED));
+            // in case the car was unlocked while the receiver was being registered
+            if (userManager.isUserUnlocked()) {
+                mResolveBroadcastReceiver.onReceive(this, new Intent(ACTION_USER_UNLOCKED));
+            }
         }
     }
 
diff --git a/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java b/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
index 7e8704e..b578161 100644
--- a/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
+++ b/car-usb-handler/src/android/car/usb/handler/UsbSettingsStorage.java
@@ -183,13 +183,18 @@
         return contentValues;
     }
 
-
     private static class UsbSettingsDbHelper extends SQLiteOpenHelper {
         private static final int DATABASE_VERSION = 2;
         private static final String DATABASE_NAME = "usb_devices.db";
 
+        // we are using device protected storage because we may need to access the db before the
+        // user has authenticated
         UsbSettingsDbHelper(Context context) {
-            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+            super(
+                    context.createDeviceProtectedStorageContext(),
+                    DATABASE_NAME,
+                    null,
+                    DATABASE_VERSION);
         }
 
         @Override
diff --git a/service/AndroidManifest.xml b/service/AndroidManifest.xml
index 2ce3d45..0775d82 100644
--- a/service/AndroidManifest.xml
+++ b/service/AndroidManifest.xml
@@ -289,6 +289,7 @@
     <uses-permission android:name="android.permission.MANAGE_USERS" />
     <uses-permission android:name="android.permission.LOCATION_HARDWARE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
 
     <application android:label="@string/app_title"
                  android:directBootAware="true"
@@ -303,6 +304,21 @@
             </intent-filter>
         </service>
         <service android:name=".PerUserCarService" android:exported="false" />
+
+        <service
+            android:name="com.android.car.trust.CarBleTrustAgent"
+            android:permission="android.permission.BIND_TRUST_AGENT"
+            android:singleUser="true">
+            <intent-filter>
+                <action android:name="android.service.trust.TrustAgentService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <!-- Warning: the meta data must be included if the service is direct boot aware.
+                If not included, the device will crash before boot completes. Rendering the
+                device unusable. -->
+            <meta-data android:name="android.service.trust.trustagent"
+                       android:resource="@xml/car_trust_agent"/>
+        </service>
         <activity android:name="com.android.car.pm.ActivityBlockingActivity"
                   android:excludeFromRecents="true"
                   android:theme="@android:style/Theme.Translucent.NoTitleBar"
diff --git a/service/res/values/config.xml b/service/res/values/config.xml
index 48f346e..200c4c3 100644
--- a/service/res/values/config.xml
+++ b/service/res/values/config.xml
@@ -158,4 +158,16 @@
     <!-- UI mode for projection activity. See ProjectionOptions class for possible values. -->
     <integer name="config_projectionUiMode" translatable="false">0</integer>
 
+    <!-- service/characteristics uuid for adding new escrow token -->
+    <string name="enrollment_service_uuid" translatable="false">5e2a68a4-27be-43f9-8d1e-4546976fabd7</string>
+    <string name="enrollment_handle_uuid" translatable="false">5e2a68a5-27be-43f9-8d1e-4546976fabd7</string>
+    <string name="enrollment_token_uuid" translatable="false">5e2a68a6-27be-43f9-8d1e-4546976fabd7</string>
+
+    <!-- service/characteristics uuid for unlocking a device -->
+    <string name="unlock_service_uuid" translatable="false">5e2a68a1-27be-43f9-8d1e-4546976fabd7</string>
+    <string name="unlock_escrow_token_uuid" translatable="false">5e2a68a2-27be-43f9-8d1e-4546976fabd7</string>
+    <string name="unlock_handle_uuid" translatable="false">5e2a68a3-27be-43f9-8d1e-4546976fabd7</string>
+
+    <string name="token_handle_shared_preferences" translatable="false">com.android.car.trust.TOKEN_HANDLE</string>
+
 </resources>
diff --git a/service/res/xml/car_trust_agent.xml b/service/res/xml/car_trust_agent.xml
new file mode 100644
index 0000000..e53932d
--- /dev/null
+++ b/service/res/xml/car_trust_agent.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+<trust-agent xmlns:android="http://schemas.android.com/apk/res/android"
+             xmlns:priv-android="http://schemas.android.com/apk/prv/res/android"
+             android:settingsActivity=".MainActivity"
+             priv-android:unlockProfile="true" />
diff --git a/service/src/com/android/car/CarLocalServices.java b/service/src/com/android/car/CarLocalServices.java
index 2c619af..fe3dc07 100644
--- a/service/src/com/android/car/CarLocalServices.java
+++ b/service/src/com/android/car/CarLocalServices.java
@@ -17,6 +17,7 @@
 package com.android.car;
 
 import android.util.ArrayMap;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -38,6 +39,7 @@
      */
     @SuppressWarnings("unchecked")
     public static <T> T getService(Class<T> type) {
+        Log.d("CarLocalServices", " getService " + type.getSimpleName());
         synchronized (sLocalServiceObjects) {
             return (T) sLocalServiceObjects.get(type);
         }
@@ -51,6 +53,7 @@
             if (sLocalServiceObjects.containsKey(type)) {
                 throw new IllegalStateException("Overriding service registration");
             }
+            Log.d("CarLocalServices", " Adding " + type.getSimpleName());
             sLocalServiceObjects.put(type, service);
         }
     }
@@ -60,6 +63,7 @@
      */
     @VisibleForTesting
     public static <T> void removeServiceForTest(Class<T> type) {
+        Log.d("CarLocalServices", " Removing " + type.getSimpleName());
         synchronized (sLocalServiceObjects) {
             sLocalServiceObjects.remove(type);
         }
@@ -69,6 +73,7 @@
      * Remove all registered services. Should be called when car service restarts.
      */
     public static void removeAllServices() {
+        Log.d("CarLocalServices", " removeAllServices");
         synchronized (sLocalServiceObjects) {
             sLocalServiceObjects.clear();
         }
diff --git a/service/src/com/android/car/ICarImpl.java b/service/src/com/android/car/ICarImpl.java
index c43547b..35255ee 100644
--- a/service/src/com/android/car/ICarImpl.java
+++ b/service/src/com/android/car/ICarImpl.java
@@ -42,7 +42,7 @@
 import com.android.car.internal.FeatureConfiguration;
 import com.android.car.pm.CarPackageManagerService;
 import com.android.car.systeminterface.SystemInterface;
-import com.android.car.trust.CarTrustAgentEnrollmentService;
+import com.android.car.trust.CarTrustedDeviceService;
 import com.android.car.user.CarUserService;
 import com.android.car.vms.VmsBrokerService;
 import com.android.car.vms.VmsClientManager;
@@ -86,7 +86,7 @@
     private final CarDiagnosticService mCarDiagnosticService;
     private final CarStorageMonitoringService mCarStorageMonitoringService;
     private final CarConfigurationService mCarConfigurationService;
-    private final CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
+    private final CarTrustedDeviceService mCarTrustedDeviceService;
     private final CarMediaService mCarMediaService;
     private final CarUserManagerHelper mUserManagerHelper;
     private final CarUserService mCarUserService;
@@ -156,10 +156,13 @@
                 new CarConfigurationService(serviceContext, new JsonReaderImpl());
         mCarLocationService = new CarLocationService(mContext, mCarPropertyService,
                 mUserManagerHelper);
-        mCarTrustAgentEnrollmentService = new CarTrustAgentEnrollmentService(serviceContext);
+        mCarTrustedDeviceService = new CarTrustedDeviceService(serviceContext);
         mCarMediaService = new CarMediaService(serviceContext);
 
         CarLocalServices.addService(CarUserService.class, mCarUserService);
+        Log.d(TAG, "Adding CarTrustedDeviceService");
+        CarLocalServices.addService(CarTrustedDeviceService.class,
+                mCarTrustedDeviceService);
         CarLocalServices.addService(SystemInterface.class, mSystemInterface);
 
         // Be careful with order. Service depending on other service should be inited later.
@@ -187,7 +190,7 @@
         allServices.add(mVmsClientManager);
         allServices.add(mVmsSubscriberService);
         allServices.add(mVmsPublisherService);
-        allServices.add(mCarTrustAgentEnrollmentService);
+        allServices.add(mCarTrustedDeviceService);
         allServices.add(mCarMediaService);
         allServices.add(mCarLocationService);
         mAllServices = allServices.toArray(new CarServiceBase[allServices.size()]);
@@ -301,7 +304,7 @@
                 return mCarConfigurationService;
             case Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE:
                 assertTrustAgentEnrollmentPermission(mContext);
-                return mCarTrustAgentEnrollmentService;
+                return mCarTrustedDeviceService.getCarTrustAgentEnrollmentService();
             case Car.CAR_MEDIA_SERVICE:
                 return mCarMediaService;
             default:
diff --git a/service/src/com/android/car/Utils.java b/service/src/com/android/car/Utils.java
index 284e610..70e1d7c 100644
--- a/service/src/com/android/car/Utils.java
+++ b/service/src/com/android/car/Utils.java
@@ -1,8 +1,25 @@
+/*
+ * 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;
 
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 
+import java.nio.ByteBuffer;
+
 /**
  * Some potentially useful static methods.
  */
@@ -17,7 +34,7 @@
     }
 
     static String getProfileName(int profile) {
-        switch(profile) {
+        switch (profile) {
             case BluetoothProfile.A2DP_SINK:
                 return "A2DP_SINK";
             case BluetoothProfile.HEADSET_CLIENT:
@@ -46,7 +63,6 @@
      * <p>
      * A specific service in CarService can choose to use a circular buffer of N records to keep
      * track of the last N transitions.
-     *
      */
     public static class TransitionLog {
         private String mServiceName; // name of the service or tag
@@ -78,4 +94,43 @@
                     : "") + " changed from " + mFromState + " to " + mToState;
         }
     }
+
+    /**
+     * Returns a byte buffer corresponding to the passed long argument.
+     *
+     * @param primitive data to convert format.
+     */
+    public static byte[] longToBytes(long primitive) {
+        ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
+        buffer.putLong(primitive);
+        return buffer.array();
+    }
+
+    /**
+     * Returns a byte buffer corresponding to the passed long argument.
+     *
+     * @param array data to convert format.
+     */
+    public static long bytesToLong(byte[] array) {
+        ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE);
+        buffer.put(array);
+        buffer.flip();
+        long value = buffer.getLong();
+        return value;
+    }
+
+    /**
+     * Returns a String in Hex format that is formed from the bytes in the byte array
+     * Useful for debugging
+     *
+     * @param array the byte array
+     * @return the Hex string version of the input byte array
+     */
+    public static String byteArrayToHexString(byte[] array) {
+        StringBuilder sb = new StringBuilder(array.length * 2);
+        for (byte b : array) {
+            sb.append(String.format("%02x", b));
+        }
+        return sb.toString();
+    }
 }
diff --git a/service/src/com/android/car/trust/BleManager.java b/service/src/com/android/car/trust/BleManager.java
new file mode 100644
index 0000000..26a9946
--- /dev/null
+++ b/service/src/com/android/car/trust/BleManager.java
@@ -0,0 +1,287 @@
+/*
+ * 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 static android.bluetooth.BluetoothProfile.GATT_SERVER;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattServer;
+import android.bluetooth.BluetoothGattServerCallback;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.car.Utils;
+
+/**
+ * A generic class that manages BLE operations like start/stop advertising, notifying connects/
+ * disconnects and reading/writing values to GATT characteristics.
+ *
+ * TODO(b/123248433) This could move to a separate comms library.
+ */
+public abstract class BleManager {
+    private static final String TAG = BleManager.class.getSimpleName();
+
+    private static final int BLE_RETRY_LIMIT = 5;
+    private static final int BLE_RETRY_INTERVAL_MS = 1000;
+
+    private final Handler mHandler = new Handler();
+
+    private final Context mContext;
+    private BluetoothManager mBluetoothManager;
+    private BluetoothLeAdvertiser mAdvertiser;
+    private BluetoothGattServer mGattServer;
+    private int mAdvertiserStartCount;
+
+    BleManager(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Starts the GATT server with the given {@link BluetoothGattService} and begins
+     * advertising.
+     *
+     * <p>It is possible that BLE service is still in TURNING_ON state when this method is invoked.
+     * Therefore, several retries will be made to ensure advertising is started.
+     *
+     * @param service {@link BluetoothGattService} that will be discovered by clients
+     */
+    protected void startAdvertising(BluetoothGattService service,
+            AdvertiseCallback advertiseCallback) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "startAdvertising: " + service.getUuid().toString());
+        }
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
+            Log.e(TAG, "System does not support BLE");
+            return;
+        }
+
+        // Only open one Gatt server.
+        if (mGattServer == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Opening a new GATT Server");
+            }
+            mBluetoothManager = (BluetoothManager) mContext.getSystemService(
+                    Context.BLUETOOTH_SERVICE);
+            mGattServer = mBluetoothManager.openGattServer(mContext, mGattServerCallback);
+
+            if (mGattServer == null) {
+                Log.e(TAG, "Gatt Server not created");
+                return;
+            }
+        }
+
+        mGattServer.clearServices();
+        mGattServer.addService(service);
+
+        AdvertiseSettings settings = new AdvertiseSettings.Builder()
+                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
+                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
+                .setConnectable(true)
+                .build();
+
+        AdvertiseData data = new AdvertiseData.Builder()
+                .setIncludeDeviceName(true)
+                .addServiceUuid(new ParcelUuid(service.getUuid()))
+                .build();
+
+        mAdvertiserStartCount = 0;
+        startAdvertisingInternally(settings, data, advertiseCallback);
+    }
+
+    private void startAdvertisingInternally(AdvertiseSettings settings, AdvertiseData data,
+            AdvertiseCallback advertiseCallback) {
+        mAdvertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
+        if (mAdvertiser == null && mAdvertiserStartCount < BLE_RETRY_LIMIT) {
+            mHandler.postDelayed(
+                    () -> startAdvertisingInternally(settings, data, advertiseCallback),
+                    BLE_RETRY_INTERVAL_MS);
+            mAdvertiserStartCount += 1;
+        } else {
+            mHandler.removeCallbacks(null);
+            mAdvertiser.startAdvertising(settings, data, advertiseCallback);
+            mAdvertiserStartCount = 0;
+        }
+    }
+
+    protected void stopAdvertising(AdvertiseCallback advertiseCallback) {
+        if (mAdvertiser != null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "stopAdvertising: ");
+            }
+            mAdvertiser.stopAdvertising(advertiseCallback);
+        }
+    }
+
+    /**
+     * Notifies the characteristic change via {@link BluetoothGattServer}
+     */
+    protected void notifyCharacteristicChanged(BluetoothDevice device,
+            BluetoothGattCharacteristic characteristic, boolean confirm) {
+        if (mGattServer != null) {
+            mGattServer.notifyCharacteristicChanged(device, characteristic, confirm);
+        }
+    }
+
+    protected Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Cleans up the BLE GATT server state.
+     */
+    void cleanup() {
+        // Stops the advertiser and GATT server. This needs to be done to avoid leaks
+        if (mAdvertiser != null) {
+            mAdvertiser.cleanup();
+        }
+
+        if (mGattServer != null) {
+            mGattServer.clearServices();
+            try {
+                for (BluetoothDevice d : mBluetoothManager.getConnectedDevices(GATT_SERVER)) {
+                    mGattServer.cancelConnection(d);
+                }
+            } catch (UnsupportedOperationException e) {
+                Log.e(TAG, "Error getting connected devices", e);
+            } finally {
+                stopGattServer();
+            }
+        }
+    }
+
+    /**
+     * Close the GATT Server
+     */
+    void stopGattServer() {
+        if (mGattServer == null) {
+            return;
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "stopGattServer");
+        }
+        mGattServer.close();
+        mGattServer = null;
+    }
+
+    /**
+     * Triggered when a device (GATT client) connected.
+     *
+     * @param device Remote device that connected on BLE.
+     */
+    protected void onRemoteDeviceConnected(BluetoothDevice device) {
+    }
+
+    /**
+     * Triggered when a device (GATT client) disconnected.
+     *
+     * @param device Remote device that disconnected on BLE.
+     */
+    protected void onRemoteDeviceDisconnected(BluetoothDevice device) {
+    }
+
+    /**
+     * Triggered when this BleManager receives a write request from a remote
+     * device. Sub-classes should implement how to handle requests.
+     * <p>
+     *
+     * @see BluetoothGattServerCallback#onCharacteristicWriteRequest(BluetoothDevice, int,
+     * BluetoothGattCharacteristic, boolean, boolean, int, byte[])
+     */
+    protected abstract void onCharacteristicWrite(BluetoothDevice device, int requestId,
+            BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
+            responseNeeded, int offset, byte[] value);
+
+    /**
+     * Triggered when this BleManager receives a read request from a remote device.
+     * <p>
+     *
+     * @see BluetoothGattServerCallback#onCharacteristicReadRequest(BluetoothDevice, int, int,
+     * BluetoothGattCharacteristic)
+     */
+    protected abstract void onCharacteristicRead(BluetoothDevice device,
+            int requestId, int offset, BluetoothGattCharacteristic characteristic);
+
+    private final BluetoothGattServerCallback mGattServerCallback =
+            new BluetoothGattServerCallback() {
+                @Override
+                public void onConnectionStateChange(BluetoothDevice device, int status,
+                        int newState) {
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG, "BLE Connection State Change: " + newState);
+                    }
+                    switch (newState) {
+                        case BluetoothProfile.STATE_CONNECTED:
+                            onRemoteDeviceConnected(device);
+                            break;
+                        case BluetoothProfile.STATE_DISCONNECTED:
+                            onRemoteDeviceDisconnected(device);
+                            break;
+                        default:
+                            Log.w(TAG,
+                                    "Connection state not connecting or disconnecting; ignoring: "
+                                            + newState);
+                    }
+                }
+
+                @Override
+                public void onServiceAdded(int status, BluetoothGattService service) {
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG,
+                                "Service added status: " + status + " uuid: " + service.getUuid());
+                    }
+                }
+
+                @Override
+                public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
+                        int offset, BluetoothGattCharacteristic characteristic) {
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG, "Read request for characteristic: " + characteristic.getUuid());
+                    }
+
+                    mGattServer.sendResponse(device, requestId,
+                            BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
+                    onCharacteristicRead(device, requestId, offset, characteristic);
+                }
+
+                @Override
+                public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
+                        BluetoothGattCharacteristic characteristic, boolean preparedWrite,
+                        boolean responseNeeded, int offset, byte[] value) {
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG, "Write request for characteristic: " + characteristic.getUuid()
+                                + "value: " + Utils.byteArrayToHexString(value));
+                    }
+
+                    mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,
+                            offset, value);
+                    onCharacteristicWrite(device, requestId, characteristic,
+                            preparedWrite, responseNeeded, offset, value);
+                }
+            };
+}
diff --git a/service/src/com/android/car/trust/BleService.java b/service/src/com/android/car/trust/BleService.java
deleted file mode 100644
index 3ec1dca..0000000
--- a/service/src/com/android/car/trust/BleService.java
+++ /dev/null
@@ -1,226 +0,0 @@
-/*
- * 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 static android.bluetooth.BluetoothProfile.GATT_SERVER;
-
-import android.app.Service;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattCharacteristic;
-import android.bluetooth.BluetoothGattServer;
-import android.bluetooth.BluetoothGattServerCallback;
-import android.bluetooth.BluetoothGattService;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.le.AdvertiseCallback;
-import android.bluetooth.le.AdvertiseData;
-import android.bluetooth.le.AdvertiseSettings;
-import android.bluetooth.le.BluetoothLeAdvertiser;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Handler;
-import android.os.ParcelUuid;
-import android.util.Log;
-
-/**
- * A generic service to start a BLE
- * TODO(b/123248433) This could move to a separate comms library.
- */
-public abstract class BleService extends Service {
-    private static final String TAG = BleService.class.getSimpleName();
-
-    private static final int BLE_RETRY_LIMIT = 5;
-    private static final int BLE_RETRY_INTERVAL_MS = 1000;
-
-    private final Handler mHandler = new Handler();
-
-    private BluetoothManager mBluetoothManager;
-    private BluetoothLeAdvertiser mAdvertiser;
-    private BluetoothGattServer mGattServer;
-    private int mAdvertiserStartCount;
-
-    /**
-     * Starts the GATT server with the given {@link BluetoothGattService} and begins
-     * advertising.
-     *
-     * <p>It is possible that BLE service is still in TURNING_ON state when this method is invoked.
-     * Therefore, several retries will be made to ensure advertising is started.
-     *
-     * @param service {@link BluetoothGattService} that will be discovered by clients
-     */
-    protected void startAdvertising(BluetoothGattService service,
-            AdvertiseCallback advertiseCallback) {
-        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
-            Log.e(TAG, "System does not support BLE");
-            return;
-        }
-
-        // Only open one Gatt server.
-        if (mGattServer == null) {
-            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
-            mGattServer = mBluetoothManager.openGattServer(this, mGattServerCallback);
-
-            if (mGattServer == null) {
-                Log.e(TAG, "Gatt Server not created");
-                return;
-            }
-        }
-
-        mGattServer.clearServices();
-        mGattServer.addService(service);
-
-        AdvertiseSettings settings = new AdvertiseSettings.Builder()
-                .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
-                .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
-                .setConnectable(true)
-                .build();
-
-        AdvertiseData data = new AdvertiseData.Builder()
-                .setIncludeDeviceName(true)
-                .addServiceUuid(new ParcelUuid(service.getUuid()))
-                .build();
-
-        mAdvertiserStartCount = 0;
-        startAdvertisingInternally(settings, data, advertiseCallback);
-    }
-
-    private void startAdvertisingInternally(AdvertiseSettings settings, AdvertiseData data,
-            AdvertiseCallback advertiseCallback) {
-        mAdvertiserStartCount += 1;
-        mAdvertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
-        if (mAdvertiser == null && mAdvertiserStartCount < BLE_RETRY_LIMIT) {
-            mHandler.postDelayed(
-                    () -> startAdvertisingInternally(settings, data, advertiseCallback),
-                            BLE_RETRY_INTERVAL_MS);
-        } else {
-            mHandler.removeCallbacks(null);
-            mAdvertiser.startAdvertising(settings, data, advertiseCallback);
-            mAdvertiserStartCount = 0;
-        }
-    }
-
-    protected void stopAdvertising(AdvertiseCallback advertiseCallback) {
-        if (mAdvertiser != null) {
-            mAdvertiser.stopAdvertising(advertiseCallback);
-        }
-    }
-
-    /**
-     * Notifies the characteristic change via {@link BluetoothGattServer}
-     */
-    protected void notifyCharacteristicChanged(BluetoothDevice device,
-            BluetoothGattCharacteristic characteristic, boolean confirm) {
-        if (mGattServer != null) {
-            mGattServer.notifyCharacteristicChanged(device, characteristic, confirm);
-        }
-    }
-
-    @Override
-    public void onDestroy() {
-        // Stops the advertiser and GATT server. This needs to be done to avoid leaks
-        if (mAdvertiser != null) {
-            mAdvertiser.cleanup();
-        }
-
-        if (mGattServer != null) {
-            mGattServer.clearServices();
-            try {
-                for (BluetoothDevice d : mBluetoothManager.getConnectedDevices(GATT_SERVER)) {
-                    mGattServer.cancelConnection(d);
-                }
-            } catch (UnsupportedOperationException e) {
-                Log.e(TAG, "Error getting connected devices", e);
-            } finally {
-                mGattServer.close();
-            }
-        }
-        super.onDestroy();
-    }
-
-    // Delegate to subclass
-    protected void onAdvertiseStartSuccess() { }
-    protected void onAdvertiseStartFailure(int errorCode) { }
-    protected void onAdvertiseDeviceConnected(BluetoothDevice device) { }
-    protected void onAdvertiseDeviceDisconnected(BluetoothDevice device) { }
-
-    /**
-     * Triggered when this BleService receives a write request from a remote
-     * device. Sub-classes should implement how to handle requests.
-     */
-    protected abstract void onCharacteristicWrite(BluetoothDevice device, int requestId,
-            BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
-            responseNeeded, int offset, byte[] value);
-
-    /**
-     * Triggered when this BleService receives a read request from a remote device.
-     */
-    protected abstract void onCharacteristicRead(BluetoothDevice device,
-            int requestId, int offset, BluetoothGattCharacteristic characteristic);
-
-    private final BluetoothGattServerCallback mGattServerCallback =
-            new BluetoothGattServerCallback() {
-        @Override
-        public void onConnectionStateChange(BluetoothDevice device,
-                final int status, final int newState) {
-            switch (newState) {
-                case BluetoothProfile.STATE_CONNECTED:
-                    onAdvertiseDeviceConnected(device);
-                    break;
-                case BluetoothProfile.STATE_DISCONNECTED:
-                    onAdvertiseDeviceDisconnected(device);
-                    break;
-                default:
-                    Log.w(TAG, "Connection state not connecting or disconnecting; ignoring: "
-                            + newState);
-            }
-        }
-
-        @Override
-        public void onServiceAdded(final int status, BluetoothGattService service) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Service added status: " + status + " uuid: " + service.getUuid());
-            }
-        }
-
-        @Override
-        public void onCharacteristicReadRequest(BluetoothDevice device,
-                int requestId, int offset, final BluetoothGattCharacteristic characteristic) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Read request for characteristic: " + characteristic.getUuid());
-            }
-
-            mGattServer.sendResponse(device, requestId,
-                    BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
-            onCharacteristicRead(device, requestId, offset, characteristic);
-        }
-
-        @Override
-        public void onCharacteristicWriteRequest(final BluetoothDevice device, int requestId,
-                BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
-                responseNeeded, int offset, byte[] value) {
-            if (Log.isLoggable(TAG, Log.DEBUG)) {
-                Log.d(TAG, "Write request for characteristic: " + characteristic.getUuid());
-            }
-
-            mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS,
-                    offset, value);
-            onCharacteristicWrite(device, requestId, characteristic,
-                    preparedWrite, responseNeeded, offset, value);
-        }
-    };
-}
diff --git a/service/src/com/android/car/trust/CarBleTrustAgent.java b/service/src/com/android/car/trust/CarBleTrustAgent.java
new file mode 100644
index 0000000..ff360a3
--- /dev/null
+++ b/service/src/com/android/car/trust/CarBleTrustAgent.java
@@ -0,0 +1,289 @@
+/*
+ * 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.app.ActivityManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.service.trust.TrustAgentService;
+import android.util.Log;
+
+import com.android.car.CarLocalServices;
+import com.android.car.Utils;
+import com.android.car.trust.CarTrustAgentEnrollmentService.CarTrustAgentEnrollmentRequestDelegate;
+import com.android.car.trust.CarTrustAgentUnlockService.CarTrustAgentUnlockDelegate;
+
+/**
+ * A BluetoothLE (BLE) based {@link TrustAgentService} that uses the escrow token unlock APIs.
+ * <p>
+ * This trust agent runs during direct boot and interacts with {@link CarTrustedDeviceService}
+ * to listen for remote devices to trigger an unlock.
+ * <p>
+ * The system {@link com.android.server.trust.TrustManagerService} binds to this agent and uses
+ * the data it receives from this agent to authorize a user in lieu of the PIN/Pattern/Password
+ * credentials.
+ */
+public class CarBleTrustAgent extends TrustAgentService {
+    private static final String TAG = CarBleTrustAgent.class.getSimpleName();
+    private boolean mIsDeviceLocked;
+    private CarTrustedDeviceService mCarTrustedDeviceService;
+    private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
+    private CarTrustAgentUnlockService mCarTrustAgentUnlockService;
+
+    @Override
+    public void onCreate() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onCreate()");
+        }
+        super.onCreate();
+        // Registering for more granular BLE specific state changes as against Bluetooth state
+        // changes, helps with reducing latency in getting notified.
+        IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+        registerReceiver(mBluetoothBroadcastReceiver, intentFilter);
+
+        // TODO(b/129144535) handle scenarios where CarService crashed.  Maybe retrieve this
+        //  every time we need instead of caching.
+        mCarTrustedDeviceService = CarLocalServices.getService(CarTrustedDeviceService.class);
+        if (mCarTrustedDeviceService == null) {
+            Log.e(TAG, "Cannot retrieve the Trusted device Service");
+            return;
+        }
+        mCarTrustAgentEnrollmentService =
+                mCarTrustedDeviceService.getCarTrustAgentEnrollmentService();
+        setEnrollmentRequestDelegate();
+        mCarTrustAgentUnlockService = mCarTrustedDeviceService.getCarTrustAgentUnlockService();
+        setUnlockRequestDelegate();
+        setManagingTrust(true);
+    }
+
+    @Override
+    public void onDestroy() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Car Trust agent shutting down");
+        }
+        super.onDestroy();
+        mCarTrustAgentEnrollmentService = null;
+        if (mBluetoothBroadcastReceiver != null) {
+            unregisterReceiver(mBluetoothBroadcastReceiver);
+        }
+    }
+
+    // Overriding TrustAgentService methods
+    @Override
+    public void onDeviceLocked() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onDeviceLocked Current user: " + ActivityManager.getCurrentUser());
+        }
+        super.onDeviceLocked();
+        mIsDeviceLocked = true;
+        if (BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_OFF) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Not starting Unlock Advertising yet, since Bluetooth Adapter is off");
+            }
+            return;
+        }
+        if (mCarTrustAgentUnlockService != null) {
+            mCarTrustAgentUnlockService.startUnlockAdvertising();
+        }
+    }
+
+    @Override
+    public void onDeviceUnlocked() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onDeviceUnlocked Current user: " + ActivityManager.getCurrentUser());
+        }
+        super.onDeviceUnlocked();
+        mIsDeviceLocked = false;
+        if (BluetoothAdapter.getDefaultAdapter().getState() == BluetoothAdapter.STATE_OFF) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Not stopping Unlock Advertising, since Bluetooth Adapter is off");
+            }
+            return;
+        }
+        if (mCarTrustAgentUnlockService != null) {
+            mCarTrustAgentUnlockService.stopUnlockAdvertising();
+
+        }
+        // TODO(b/128857992) - should we revoke trust right after to enable keyguard when
+        //  switching user
+    }
+
+    @Override
+    public void onEscrowTokenRemoved(long handle, boolean successful) {
+        // TODO(b/128857992) To be implemented
+    }
+
+    @Override
+    public void onEscrowTokenStateReceived(long handle, int tokenState) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onEscrowTokenStateReceived: " + Long.toHexString(handle) + " state: "
+                    + tokenState);
+        }
+        if (mCarTrustAgentEnrollmentService == null) {
+            return;
+        }
+        mCarTrustAgentEnrollmentService.onEscrowTokenActiveStateChanged(handle,
+                tokenState == TOKEN_STATE_ACTIVE);
+    }
+
+    @Override
+    public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onEscrowTokenAdded handle: " + Long.toHexString(handle) + "token: "
+                    + Utils.byteArrayToHexString(token));
+        }
+        if (mCarTrustAgentEnrollmentService == null) {
+            return;
+        }
+        mCarTrustAgentEnrollmentService.onEscrowTokenAdded(token, handle, user.getIdentifier());
+    }
+
+    private void setEnrollmentRequestDelegate() {
+        if (mCarTrustAgentEnrollmentService == null) {
+            return;
+        }
+        mCarTrustAgentEnrollmentService.setEnrollmentRequestDelegate(mEnrollDelegate);
+    }
+
+    private void setUnlockRequestDelegate() {
+        if (mCarTrustAgentUnlockService == null) {
+            return;
+        }
+        mCarTrustAgentUnlockService.setUnlockRequestDelegate(mUnlockDelegate);
+    }
+
+    private void unlockUserInternally(int uid, byte[] token, long handle) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "About to unlock user: " + uid);
+            UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+            if (um.isUserUnlocked(UserHandle.of(uid))) {
+                Log.d(TAG, "User currently unlocked");
+            } else {
+                Log.d(TAG, "User currently locked");
+            }
+        }
+        unlockUserWithToken(handle, token, UserHandle.of(uid));
+        grantTrust("Granting trust from escrow token",
+                0, FLAG_GRANT_TRUST_DISMISS_KEYGUARD);
+    }
+
+    private final BroadcastReceiver mBluetoothBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction() != null && BluetoothAdapter.ACTION_BLE_STATE_CHANGED.equals(
+                    intent.getAction())) {
+                onBluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1));
+            }
+        }
+    };
+
+    private void onBluetoothStateChanged(int state) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onBluetoothStateChanged: " + state);
+        }
+        if (!mIsDeviceLocked) {
+            return;
+        }
+        switch (state) {
+            case BluetoothAdapter.STATE_BLE_ON:
+                if (mCarTrustAgentUnlockService != null) {
+                    mCarTrustAgentUnlockService.startUnlockAdvertising();
+                }
+                break;
+            case BluetoothAdapter.STATE_OFF:
+                Log.e(TAG, "Bluetooth Adapter Off in lock screen");
+                if (mCarTrustedDeviceService != null) {
+                    mCarTrustedDeviceService.cleanupBleService();
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    // Implementing Delegates for Enrollment and Unlock.  The CarBleTrustAgent acts as the interface
+    // between the Trust Agent framework and the Car Service.  The Car service handles communicating
+    // with the peer device part and the framework handles the actual authentication.  The
+    // CarBleTrustAgent abstracts these 2 pieces from each other.
+    /**
+     * Implementation of the {@link CarTrustAgentEnrollmentRequestDelegate}
+     */
+    private final CarTrustAgentEnrollmentRequestDelegate mEnrollDelegate =
+            new CarTrustAgentEnrollmentRequestDelegate() {
+                @Override
+                public void revokeTrust() {
+                    // TODO(b/128857992) to be implemented
+                }
+
+                @Override
+                public void addEscrowToken(byte[] token, int uid) {
+                    if (Log.isLoggable(TAG, Log.DEBUG)) {
+                        Log.d(TAG,
+                                "addEscrowToken. uid: " + uid + " token: "
+                                        + Utils.byteArrayToHexString(
+                                        token));
+                    }
+                    CarBleTrustAgent.this.addEscrowToken(token, UserHandle.of(uid));
+                }
+
+                @Override
+                public void removeEscrowToken(long handle, int uid) {
+                    // TODO(b/128857992) to be implemented
+                }
+
+                @Override
+                public void isEscrowTokenActive(long handle, int uid) {
+                    CarBleTrustAgent.this.isEscrowTokenActive(handle, UserHandle.of(uid));
+                }
+            };
+
+    /**
+     * Implementation of the {@link CarTrustAgentUnlockDelegate}
+     */
+    private final CarTrustAgentUnlockDelegate mUnlockDelegate = new CarTrustAgentUnlockDelegate() {
+        /**
+         * Pass the user and token credentials to authenticate with the LockSettingsService.
+         *
+         * @param user   user being authorized
+         * @param token  escrow token for the user
+         * @param handle the handle corresponding to the escrow token
+         */
+        @Override
+        public void onUnlockDataReceived(int user, byte[] token, long handle) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "onUnlockDataReceived:" + user + " token: " + Long.toHexString(
+                        Utils.bytesToLong(token)) + " handle: " + Long.toHexString(handle));
+            }
+            if (ActivityManager.getCurrentUser() != user) {
+                // Current behavior is to only authenticate the user we have booted into.
+                // TODO(b/129029418) Make identification & Auth vs Auth-only a
+                // configurable option
+                Log.e(TAG, "Expected User: " + ActivityManager.getCurrentUser()
+                        + " Presented User: " + user);
+                return;
+            } else {
+                unlockUserInternally(user, token, handle);
+            }
+
+        }
+    };
+}
diff --git a/service/src/com/android/car/trust/CarTrustAgentBleManager.java b/service/src/com/android/car/trust/CarTrustAgentBleManager.java
new file mode 100644
index 0000000..0b6cae1
--- /dev/null
+++ b/service/src/com/android/car/trust/CarTrustAgentBleManager.java
@@ -0,0 +1,281 @@
+/*
+ * 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.bluetooth.BluetoothGattCharacteristic;
+import android.bluetooth.BluetoothGattService;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseSettings;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.car.CarLocalServices;
+import com.android.car.R;
+import com.android.car.Utils;
+
+import java.util.UUID;
+
+/**
+ * A BLE Service that is used for communicating with the trusted peer device.  This extends from a
+ * more generic {@link BLEManager} and has more context on the BLE requirements for the Trusted
+ * device feature. It has knowledge on the GATT services and characteristics that are specific to
+ * the Trusted Device feature.
+ */
+class CarTrustAgentBleManager extends BleManager {
+    private static final String TAG = "CarTrustBLEManager";
+    private CarTrustedDeviceService mCarTrustedDeviceService;
+    private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
+    private CarTrustAgentUnlockService mCarTrustAgentUnlockService;
+
+    // Enrollment Service and Characteristic UUIDs
+    private UUID mEnrollmentServiceUuid;
+    private UUID mEnrollmentEscrowTokenUuid;
+    private UUID mEnrollmentTokenHandleUuid;
+    private BluetoothGattService mEnrollmentGattService;
+
+    // Unlock Service and Characteristic UUIDs
+    private UUID mUnlockServiceUuid;
+    private UUID mUnlockEscrowTokenUuid;
+    private UUID mUnlockTokenHandleUuid;
+    private BluetoothGattService mUnlockGattService;
+
+    CarTrustAgentBleManager(Context context) {
+        super(context);
+    }
+
+    // Overriding some of the {@link BLEManager} methods to be specific for Trusted Device feature.
+    @Override
+    public void onRemoteDeviceConnected(BluetoothDevice device) {
+        if (getTrustedDeviceService() != null) {
+            getTrustedDeviceService().onRemoteDeviceConnected(device);
+        }
+    }
+
+    @Override
+    public void onRemoteDeviceDisconnected(BluetoothDevice device) {
+        if (getTrustedDeviceService() != null) {
+            getTrustedDeviceService().onRemoteDeviceDisconnected(device);
+        }
+    }
+
+    @Override
+    public void onCharacteristicWrite(BluetoothDevice device, int requestId,
+            BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean
+            responseNeeded, int offset, byte[] value) {
+        UUID uuid = characteristic.getUuid();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onCharacteristicWrite received uuid: " + uuid);
+        }
+        if (uuid.equals(mEnrollmentEscrowTokenUuid)) {
+            if (getEnrollmentService() != null) {
+                getEnrollmentService().onEnrollmentDataReceived(value);
+            }
+        } else if (uuid.equals(mUnlockEscrowTokenUuid)) {
+            if (getUnlockService() != null) {
+                getUnlockService().onUnlockTokenReceived(value);
+            }
+        } else if (uuid.equals(mUnlockTokenHandleUuid)) {
+            if (getUnlockService() != null) {
+                getUnlockService().onUnlockHandleReceived(value);
+            }
+        }
+    }
+
+    @Override
+    public void onCharacteristicRead(BluetoothDevice device,
+            int requestId, int offset, final BluetoothGattCharacteristic characteristic) {
+        // Ignored read requests.
+    }
+
+    @Nullable
+    private CarTrustedDeviceService getTrustedDeviceService() {
+        if (mCarTrustedDeviceService == null) {
+            mCarTrustedDeviceService = CarLocalServices.getService(
+                    CarTrustedDeviceService.class);
+        }
+        return mCarTrustedDeviceService;
+    }
+
+    @Nullable
+    private CarTrustAgentEnrollmentService getEnrollmentService() {
+        if (mCarTrustAgentEnrollmentService != null) {
+            return mCarTrustAgentEnrollmentService;
+        }
+
+        if (getTrustedDeviceService() != null) {
+            mCarTrustAgentEnrollmentService =
+                    getTrustedDeviceService().getCarTrustAgentEnrollmentService();
+        }
+        return mCarTrustAgentEnrollmentService;
+    }
+
+    @Nullable
+    private CarTrustAgentUnlockService getUnlockService() {
+        if (mCarTrustAgentUnlockService != null) {
+            return mCarTrustAgentUnlockService;
+        }
+
+        if (getTrustedDeviceService() != null) {
+            mCarTrustAgentUnlockService =
+                    getTrustedDeviceService().getCarTrustAgentUnlockService();
+        }
+        return mCarTrustAgentUnlockService;
+    }
+
+    /**
+     * Setup the BLE GATT server for Enrollment. The GATT server for Enrollment comprises of
+     * one GATT Service and 2 characteristics - one for the escrow token to be generated and sent
+     * from the phone and the other for the handle generated and sent by the Head unit.
+     */
+    void setupEnrollmentBleServer() {
+        mEnrollmentServiceUuid = UUID.fromString(
+                getContext().getString(R.string.enrollment_service_uuid));
+        mEnrollmentEscrowTokenUuid = UUID.fromString(
+                getContext().getString(R.string.enrollment_token_uuid));
+        mEnrollmentTokenHandleUuid = UUID.fromString(
+                getContext().getString(R.string.enrollment_handle_uuid));
+
+        mEnrollmentGattService = new BluetoothGattService(mEnrollmentServiceUuid,
+                BluetoothGattService.SERVICE_TYPE_PRIMARY);
+
+        // Characteristic to describe the escrow token being used for unlock
+        BluetoothGattCharacteristic tokenCharacteristic = new BluetoothGattCharacteristic(
+                mEnrollmentEscrowTokenUuid,
+                BluetoothGattCharacteristic.PROPERTY_WRITE,
+                BluetoothGattCharacteristic.PERMISSION_WRITE);
+
+        // Characteristic to describe the handle being used for this escrow token
+        BluetoothGattCharacteristic handleCharacteristic = new BluetoothGattCharacteristic(
+                mEnrollmentTokenHandleUuid,
+                BluetoothGattCharacteristic.PROPERTY_NOTIFY,
+                BluetoothGattCharacteristic.PERMISSION_READ);
+
+        mEnrollmentGattService.addCharacteristic(tokenCharacteristic);
+        mEnrollmentGattService.addCharacteristic(handleCharacteristic);
+    }
+
+    /**
+     * Setup the BLE GATT server for Unlocking the Head unit.  The GATT server for this phase also
+     * comprises of 1 Service and 2 characteristics.  However both the token and the handle are
+     * sent ftrom the phone to the head unit.
+     */
+    void setupUnlockBleServer() {
+        mUnlockServiceUuid = UUID.fromString(
+                getContext().getString(R.string.unlock_service_uuid));
+        mUnlockEscrowTokenUuid = UUID.fromString(
+                getContext().getString(R.string.unlock_escrow_token_uuid));
+        mUnlockTokenHandleUuid = UUID.fromString(
+                getContext().getString(R.string.unlock_handle_uuid));
+
+        mUnlockGattService = new BluetoothGattService(mUnlockServiceUuid,
+                BluetoothGattService.SERVICE_TYPE_PRIMARY);
+
+        // Characteristic to describe the escrow token being used for unlock
+        BluetoothGattCharacteristic tokenCharacteristic = new BluetoothGattCharacteristic(
+                mUnlockEscrowTokenUuid,
+                BluetoothGattCharacteristic.PROPERTY_WRITE,
+                BluetoothGattCharacteristic.PERMISSION_WRITE);
+
+        // Characteristic to describe the handle being used for this escrow token
+        BluetoothGattCharacteristic handleCharacteristic = new BluetoothGattCharacteristic(
+                mUnlockTokenHandleUuid,
+                BluetoothGattCharacteristic.PROPERTY_WRITE,
+                BluetoothGattCharacteristic.PERMISSION_WRITE);
+
+        mUnlockGattService.addCharacteristic(tokenCharacteristic);
+        mUnlockGattService.addCharacteristic(handleCharacteristic);
+    }
+
+    void startEnrollmentAdvertising() {
+        startAdvertising(mEnrollmentGattService, mEnrollmentAdvertisingCallback);
+    }
+
+    void stopEnrollmentAdvertising() {
+        stopAdvertising(mEnrollmentAdvertisingCallback);
+    }
+
+    void startUnlockAdvertising() {
+        startAdvertising(mUnlockGattService, mUnlockAdvertisingCallback);
+    }
+
+    void stopUnlockAdvertising() {
+        stopAdvertising(mUnlockAdvertisingCallback);
+    }
+
+    void disconnectRemoteDevice(BluetoothDevice device) {
+        // TODO(b/129029421) - is closing the GATT Server the right thing to do here?
+    }
+
+    /**
+     * Sends the handle corresponding to the escrow token that was generated by the phone to the
+     * phone.
+     *
+     * @param device the BLE peer device to send the handle to.
+     * @param handle the handle corresponding to the escrow token
+     */
+    void sendEnrollmentHandle(BluetoothDevice device, long handle) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "sendEnrollmentHandle: " + Long.toHexString(handle) + " "
+                    + device.getAddress());
+        }
+        BluetoothGattCharacteristic enrollmentHandle = mEnrollmentGattService.getCharacteristic(
+                mEnrollmentTokenHandleUuid);
+        enrollmentHandle.setValue(Utils.longToBytes(handle));
+        notifyCharacteristicChanged(device, enrollmentHandle, false);
+    }
+
+    private final AdvertiseCallback mEnrollmentAdvertisingCallback = new AdvertiseCallback() {
+        @Override
+        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
+            super.onStartSuccess(settingsInEffect);
+            if (getEnrollmentService() != null) {
+                getEnrollmentService().onEnrollmentAdvertiseStartSuccess();
+            }
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Successfully started advertising service");
+            }
+        }
+
+        @Override
+        public void onStartFailure(int errorCode) {
+            Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
+
+            super.onStartFailure(errorCode);
+            if (getEnrollmentService() != null) {
+                getEnrollmentService().onEnrollmentAdvertiseStartFailure(errorCode);
+            }
+        }
+    };
+
+    private final AdvertiseCallback mUnlockAdvertisingCallback = new AdvertiseCallback() {
+        @Override
+        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
+            super.onStartSuccess(settingsInEffect);
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Unlock Advertising onStartSuccess");
+            }
+        }
+
+        @Override
+        public void onStartFailure(int errorCode) {
+            Log.e(TAG, "Failed to advertise, errorCode: " + errorCode);
+            super.onStartFailure(errorCode);
+        }
+    };
+}
diff --git a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
index cd421e7..0054957 100644
--- a/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
+++ b/service/src/com/android/car/trust/CarTrustAgentEnrollmentService.java
@@ -17,86 +17,161 @@
 package com.android.car.trust;
 
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 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 com.android.car.Utils;
+import com.android.internal.annotations.GuardedBy;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
- * 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.
+ * A service that is part of the CarTrustedDeviceService that is responsible for allowing a
+ * phone to enroll as a trusted device.  The enrolled phone can then be used for authenticating a
+ * user on the HU.  This implements the {@link android.car.trust.CarTrustAgentEnrollmentManager}
+ * APIs that an app like Car Settings can call to conduct an enrollment.
  */
-public class CarTrustAgentEnrollmentService extends ICarTrustAgentEnrollment.Stub implements
-        CarServiceBase {
+public class CarTrustAgentEnrollmentService extends ICarTrustAgentEnrollment.Stub {
     private static final String TAG = "CarTrustAgentEnroll";
-    private final Context mContext;
+    private static final String FAKE_AUTH_STRING = "000000";
+    private final CarTrustedDeviceService mTrustedDeviceService;
     // 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.
+    // List of clients listening to BLE state changes events during enrollment.
     private final List<BleStateChangeClient> mBleStateChangeClients = new ArrayList<>();
+    private final CarTrustAgentBleManager mCarTrustAgentBleManager;
+    private CarTrustAgentEnrollmentRequestDelegate mEnrollmentDelegate;
+    private Object mRemoteDeviceLock = new Object();
+    @GuardedBy("mRemoteDeviceLock")
+    private BluetoothDevice mRemoteEnrollmentDevice;
+    @GuardedBy("this")
+    private boolean mEnrollmentHandshakeAccepted;
+    private final Map<Long, Boolean> mTokenActiveState = new HashMap<>();
 
-    public CarTrustAgentEnrollmentService(Context context) {
-        mContext = context;
+    public CarTrustAgentEnrollmentService(CarTrustedDeviceService service,
+            CarTrustAgentBleManager bleService) {
+        mTrustedDeviceService = service;
+        mCarTrustAgentBleManager = bleService;
     }
 
-    @Override
     public synchronized void init() {
+        mCarTrustAgentBleManager.setupEnrollmentBleServer();
     }
 
-    @Override
     public synchronized void release() {
         for (EnrollmentStateClient client : mEnrollmentStateClients) {
             client.mListenerBinder.unlinkToDeath(client, 0);
         }
+        for (BleStateChangeClient client : mBleStateChangeClients) {
+            client.mListenerBinder.unlinkToDeath(client, 0);
+        }
         mEnrollmentStateClients.clear();
+        setEnrollmentHandshakeAccepted(false);
     }
 
+    // Implementing the ICarTrustAgentEnrollment interface
 
-    // Binder methods
-    // TODO(b/120911995) The methods don't do anything yet.  The implementation will be checked in
-    // a follow up CL.
+    /**
+     * Begin BLE advertisement for Enrollment. This should be called from an app that conducts
+     * the enrollment of the trusted device.
+     */
     @Override
     public void startEnrollmentAdvertising() {
+        // Stop any current broadcasts
+        mTrustedDeviceService.getCarTrustAgentUnlockService().stopUnlockAdvertising();
+        stopEnrollmentAdvertising();
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "startEnrollmentAdvertising");
+        }
+        mCarTrustAgentBleManager.startEnrollmentAdvertising();
     }
 
+    /**
+     * Stop BLE advertisement for Enrollment
+     */
     @Override
     public void stopEnrollmentAdvertising() {
+        mCarTrustAgentBleManager.stopEnrollmentAdvertising();
     }
 
     @Override
     public void initiateEnrollmentHandshake(BluetoothDevice device) {
+        // TODO(b/129029320) - this is not needed since the IHU plays the server
+        // role and the secure handshake is initiated by the client.
     }
 
+    /**
+     * Called by the client to notify that the user has accepted a pairing code or any out-of-band
+     * confirmation.
+     */
     @Override
     public void enrollmentHandshakeAccepted() {
+        setEnrollmentHandshakeAccepted(true);
     }
 
+    /**
+     * Terminate the Enrollment process.  To be called when an error is encountered during
+     * enrollment.  For example - user pressed cancel on pairing code confirmation or user
+     * navigated away from the app before completing enrollment.
+     */
     @Override
     public void terminateEnrollmentHandshake() {
+        setEnrollmentHandshakeAccepted(false);
+        // Disconnect from BLE
+        mCarTrustAgentBleManager.disconnectRemoteDevice(mRemoteEnrollmentDevice);
     }
 
+    /**
+     * Returns if there is an active token for the given user and handle.
+     *
+     * @param handle handle corresponding to the escrow token
+     * @param uid    user id
+     * @return True if the escrow token is active, false if not
+     */
     @Override
     public boolean isEscrowTokenActive(long handle, int uid) {
+        if (mTokenActiveState.get(handle) != null) {
+            return mTokenActiveState.get(handle);
+        }
         return false;
     }
 
+    // TODO(b/128857992)- Implement this
     @Override
     public void revokeTrust(long handle) {
     }
 
+    /**
+     * Get the Handles corresponding to the token for the current user.  The client can use this
+     * to list the trusted devices for the user.  This means that the client should maintain a map
+     * of the handles:Bluetooth device names.
+     *
+     * @param uid user id
+     * @return array of handles for the user.
+     */
     @Override
     public long[] getEnrollmentHandlesForUser(int uid) {
-        return new long[0];
+        Set<String> handlesSet = mTrustedDeviceService.getSharedPrefs().getStringSet(
+                String.valueOf(uid),
+                new HashSet<>());
+        long[] handles = new long[handlesSet.size()];
+        int i = 0;
+        for (String handle : handlesSet) {
+            handles[i++] = Long.valueOf(handle);
+        }
+        return handles;
     }
 
     /**
@@ -125,6 +200,127 @@
         }
     }
 
+    void onEscrowTokenAdded(byte[] token, long handle, int uid) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onEscrowTokenAdded handle:" + handle + " uid:" + uid);
+        }
+        mTrustedDeviceService.getSharedPrefs().edit()
+                .putInt(String.valueOf(handle), uid)
+                .apply();
+        Set<String> handles = mTrustedDeviceService.getSharedPrefs().getStringSet(
+                String.valueOf(uid),
+                new HashSet<>());
+        handles.add(String.valueOf(handle));
+        mTrustedDeviceService.getSharedPrefs().edit().putStringSet(String.valueOf(uid),
+                handles).apply();
+
+        if (mRemoteEnrollmentDevice == null) {
+            Log.e(TAG, "onEscrowTokenAdded() but no remote device connected!");
+            //TODO(b/128857992) remove Escrow token now?
+            return;
+        }
+        mCarTrustAgentBleManager.sendEnrollmentHandle(mRemoteEnrollmentDevice, handle);
+        for (EnrollmentStateClient client : mEnrollmentStateClients) {
+            try {
+                client.mListener.onEscrowTokenAdded(handle);
+            } catch (RemoteException e) {
+                Log.e(TAG, "onEscrowTokenAdded dispatch failed", e);
+            }
+        }
+    }
+
+    void onEscrowTokenActiveStateChanged(long handle, boolean tokenState) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onEscrowTokenActiveStateChanged: " + Long.toHexString(handle));
+        }
+        mTokenActiveState.put(handle, tokenState);
+        dispatchEscrowTokenActiveStateChanged(handle, tokenState);
+    }
+
+    void onEnrollmentAdvertiseStartSuccess() {
+        for (BleStateChangeClient client : mBleStateChangeClients) {
+            try {
+                client.mListener.onEnrollmentAdvertisingStarted();
+            } catch (RemoteException e) {
+                Log.e(TAG, "onAdvertiseSuccess dispatch failed", e);
+            }
+        }
+    }
+
+    void onEnrollmentAdvertiseStartFailure(int errorcode) {
+        for (BleStateChangeClient client : mBleStateChangeClients) {
+            try {
+                client.mListener.onEnrollmentAdvertisingFailed(errorcode);
+            } catch (RemoteException e) {
+                Log.e(TAG, "onAdvertiseSuccess dispatch failed", e);
+            }
+        }
+    }
+
+    void onRemoteDeviceConnected(BluetoothDevice device) {
+        synchronized (mRemoteDeviceLock) {
+            mRemoteEnrollmentDevice = device;
+        }
+        for (BleStateChangeClient client : mBleStateChangeClients) {
+            try {
+                client.mListener.onBleEnrollmentDeviceConnected(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "onAdvertiseSuccess dispatch failed", e);
+            }
+        }
+        //TODO(b/11788064) Fake Authentication to enable clients to go through the enrollment flow.
+        fakeAuthentication();
+    }
+
+    void onRemoteDeviceDisconnected(BluetoothDevice device) {
+        synchronized (mRemoteDeviceLock) {
+            mRemoteEnrollmentDevice = null;
+        }
+        for (BleStateChangeClient client : mBleStateChangeClients) {
+            try {
+                client.mListener.onBleEnrollmentDeviceDisconnected(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, "onAdvertiseSuccess dispatch failed", e);
+            }
+        }
+    }
+
+    void onEnrollmentDataReceived(byte[] value) {
+        if (mEnrollmentDelegate == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Enrollment Delegate not set");
+            }
+            return;
+        }
+        // The phone is not expected to send any data until the user has accepted the
+        // pairing.
+        if (!mEnrollmentHandshakeAccepted) {
+            Log.e(TAG, "User has not accepted the pairing code yet."
+                    + Utils.byteArrayToHexString(value));
+            return;
+        }
+        mEnrollmentDelegate.addEscrowToken(value, ActivityManager.getCurrentUser());
+    }
+
+    // TODO(b/11788064) Fake Authentication until we hook up the crypto lib
+    private void fakeAuthentication() {
+        if (mRemoteEnrollmentDevice == null) {
+            Log.e(TAG, "Remote Device disconnected before Enrollment completed");
+            return;
+        }
+        for (EnrollmentStateClient client : mEnrollmentStateClients) {
+            try {
+                client.mListener.onAuthStringAvailable(mRemoteEnrollmentDevice, FAKE_AUTH_STRING);
+            } catch (RemoteException e) {
+                Log.e(TAG, "onAdvertiseSuccess dispatch failed", e);
+            }
+        }
+    }
+
+    private synchronized void setEnrollmentHandshakeAccepted(boolean accepted) {
+        mEnrollmentHandshakeAccepted = accepted;
+    }
+
     /**
      * Iterates through the list of registered Enrollment State Change clients -
      * {@link EnrollmentStateClient} and finds if the given client is already registered.
@@ -235,6 +431,59 @@
     }
 
     /**
+     * The interface that an enrollment delegate has to implement to add/remove escrow tokens.
+     */
+    interface CarTrustAgentEnrollmentRequestDelegate {
+        /**
+         * Add the given escrow token that was generated by the peer device that is being enrolled.
+         *
+         * @param token the 64 bit token
+         * @param uid   user id
+         */
+        void addEscrowToken(byte[] token, int uid);
+
+        /**
+         * Remove the given escrow token.  This should be called when removing a trusted device.
+         *
+         * @param handle the 64 bit token
+         * @param uid    user id
+         */
+        void removeEscrowToken(long handle, int uid);
+
+        /**
+         * Query if the token is active.  The result is asynchronously delivered through a callback
+         * {@link CarTrustAgentEnrollmentService#onEscrowTokenActiveStateChanged(long, boolean)}
+         *
+         * @param handle the 64 bit token
+         * @param uid    user id
+         */
+        void isEscrowTokenActive(long handle, int uid);
+
+        /**
+         * Calls the Trust framwework's revoke trust to revoke the trust that was granted for the
+         * current user.
+         */
+        void revokeTrust();
+    }
+
+    void setEnrollmentRequestDelegate(CarTrustAgentEnrollmentRequestDelegate delegate) {
+        mEnrollmentDelegate = delegate;
+    }
+
+    void dump(PrintWriter writer) {
+    }
+
+    private void dispatchEscrowTokenActiveStateChanged(long handle, boolean active) {
+        for (EnrollmentStateClient client : mEnrollmentStateClients) {
+            try {
+                client.mListener.onEscrowTokenActiveStateChanged(handle, active);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Cannot notify client of a Token Activation change: " + active);
+            }
+        }
+    }
+
+    /**
      * Class that holds onto client related information - listener interface, process that hosts the
      * binder object etc.
      * <p>
@@ -301,9 +550,12 @@
             return mListenerBinder == binder;
         }
 
-    }
-
-    @Override
-    public void dump(PrintWriter writer) {
+        public void onEnrollmentAdvertisementStarted() {
+            try {
+                mListener.onEnrollmentAdvertisingStarted();
+            } catch (RemoteException e) {
+                Log.e(TAG, "onEnrollmentAdvertisementStarted() failed", e);
+            }
+        }
     }
 }
diff --git a/service/src/com/android/car/trust/CarTrustAgentUnlockService.java b/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
new file mode 100644
index 0000000..0f5e031
--- /dev/null
+++ b/service/src/com/android/car/trust/CarTrustAgentUnlockService.java
@@ -0,0 +1,196 @@
+/*
+ * 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.bluetooth.BluetoothDevice;
+import android.util.Log;
+
+import com.android.car.Utils;
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.PrintWriter;
+
+/**
+ * A service that interacts with the Trust Agent {@link CarBleTrustAgent} and a comms (BLE) service
+ * {@link CarTrustAgentBleManager} to receive the necessary credentials to authenticate
+ * an Android user.
+ */
+public class CarTrustAgentUnlockService {
+    private static final String TAG = "CarTrustAgentUnlock";
+    private final CarTrustedDeviceService mTrustedDeviceService;
+    private final CarTrustAgentBleManager mCarTrustAgentBleManager;
+    private CarTrustAgentUnlockDelegate mUnlockDelegate;
+    // Locks
+    private final Object mTokenLock = new Object();
+    private final Object mHandleLock = new Object();
+    private final Object mDeviceLock = new Object();
+
+    @GuardedBy("mTokenLock")
+    private byte[] mUnlockToken;
+    @GuardedBy("mHandleLock")
+    private byte[] mUnlockHandle;
+    @GuardedBy("mDeviceLock")
+    private BluetoothDevice mRemoteUnlockDevice;
+
+    CarTrustAgentUnlockService(CarTrustedDeviceService service,
+            CarTrustAgentBleManager bleService) {
+        mTrustedDeviceService = service;
+        mCarTrustAgentBleManager = bleService;
+    }
+
+    /**
+     * The interface that an unlock delegate has to implement to get the auth credentials from
+     * the unlock service.
+     */
+    interface CarTrustAgentUnlockDelegate {
+        /**
+         * Called when the Unlock service has the auth credentials to pass.
+         *
+         * @param user   user being authorized
+         * @param token  escrow token for the user
+         * @param handle the handle corresponding to the escrow token
+         */
+        void onUnlockDataReceived(int user, byte[] token, long handle);
+    }
+
+    /**
+     * Set a delegate that implements {@link CarTrustAgentUnlockDelegate}. The delegate will be
+     * handed the auth related data (token and handle) when it is received from the remote
+     * trusted device. The delegate is expected to use that to authorize the user.
+     */
+    void setUnlockRequestDelegate(CarTrustAgentUnlockDelegate delegate) {
+        mUnlockDelegate = delegate;
+    }
+
+    /**
+     * Start Unlock Advertising
+     */
+    void startUnlockAdvertising() {
+        mTrustedDeviceService.getCarTrustAgentEnrollmentService().stopEnrollmentAdvertising();
+        stopUnlockAdvertising();
+        mCarTrustAgentBleManager.startUnlockAdvertising();
+    }
+
+    /**
+     * Stop unlock advertising
+     */
+    void stopUnlockAdvertising() {
+        mCarTrustAgentBleManager.stopUnlockAdvertising();
+        // Also disconnect from the peer.
+        if (mRemoteUnlockDevice != null) {
+            mCarTrustAgentBleManager.disconnectRemoteDevice(mRemoteUnlockDevice);
+        }
+    }
+
+    void init() {
+        mCarTrustAgentBleManager.setupUnlockBleServer();
+    }
+
+    void release() {
+        synchronized (mDeviceLock) {
+            mRemoteUnlockDevice = null;
+        }
+    }
+
+    void onRemoteDeviceConnected(BluetoothDevice device) {
+        synchronized (mDeviceLock) {
+            if (mRemoteUnlockDevice != null) {
+                // TBD, return when this is encountered?
+                Log.e(TAG, "Unexpected: Cannot connect to another device when already connected");
+            }
+            mRemoteUnlockDevice = device;
+        }
+    }
+
+    void onRemoteDeviceDisconnected(BluetoothDevice device) {
+        // sanity checking
+        if (!device.equals(mRemoteUnlockDevice) && device.getAddress() != null) {
+            Log.e(TAG, "Disconnected from an unknown device:" + device.getAddress());
+        }
+        synchronized (mDeviceLock) {
+            mRemoteUnlockDevice = null;
+        }
+    }
+
+    void onUnlockTokenReceived(byte[] value) {
+        synchronized (mTokenLock) {
+            mUnlockToken = value;
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Unlock Token: " + mUnlockToken);
+        }
+        if (mUnlockToken == null || mUnlockHandle == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Unlock Handle not available yet");
+            }
+            return;
+        }
+        if (mUnlockDelegate == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "No Unlock delegate");
+            }
+            return;
+        }
+        mUnlockDelegate.onUnlockDataReceived(
+                mTrustedDeviceService.getUserHandleByTokenHandle(Utils.bytesToLong(mUnlockHandle)),
+                mUnlockToken,
+                Utils.bytesToLong(mUnlockHandle));
+
+        synchronized (mTokenLock) {
+            mUnlockToken = null;
+        }
+        synchronized (mHandleLock) {
+            mUnlockHandle = null;
+        }
+    }
+
+    void onUnlockHandleReceived(byte[] value) {
+        synchronized (mHandleLock) {
+            mUnlockHandle = value;
+        }
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Unlock Handle: " + mUnlockHandle);
+        }
+        if (mUnlockToken == null || mUnlockHandle == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Unlock Token not available yet");
+            }
+            return;
+        }
+
+        if (mUnlockDelegate == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "No Unlock delegate");
+            }
+            return;
+        }
+        mUnlockDelegate.onUnlockDataReceived(
+                mTrustedDeviceService.getUserHandleByTokenHandle(Utils.bytesToLong(mUnlockHandle)),
+                mUnlockToken,
+                Utils.bytesToLong(mUnlockHandle));
+
+        synchronized (mUnlockToken) {
+            mUnlockToken = null;
+        }
+        synchronized (mHandleLock) {
+            mUnlockHandle = null;
+        }
+    }
+
+    void dump(PrintWriter writer) {
+    }
+}
diff --git a/service/src/com/android/car/trust/CarTrustedDeviceService.java b/service/src/com/android/car/trust/CarTrustedDeviceService.java
new file mode 100644
index 0000000..538f31f
--- /dev/null
+++ b/service/src/com/android/car/trust/CarTrustedDeviceService.java
@@ -0,0 +1,124 @@
+/*
+ * 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.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import com.android.car.CarServiceBase;
+import com.android.car.R;
+
+import java.io.PrintWriter;
+
+/**
+ * The part of the Car service that enables the Trusted device feature.  Trusted Device is a feature
+ * where a remote device is enrolled as a trusted device that can authorize an Android user in lieu
+ * of the user entering a password or PIN.
+ * <p>
+ * It is comprised of the {@link CarTrustAgentEnrollmentService} for handling enrollment and
+ * {@link CarTrustAgentUnlockService} for handling unlock/auth.
+ *
+ */
+public class CarTrustedDeviceService implements CarServiceBase {
+    private static final String TAG = CarTrustedDeviceService.class.getSimpleName();
+    private final Context mContext;
+    private CarTrustAgentEnrollmentService mCarTrustAgentEnrollmentService;
+    private CarTrustAgentUnlockService mCarTrustAgentUnlockService;
+    private CarTrustAgentBleManager mCarTrustAgentBleManager;
+    private SharedPreferences mTrustAgentTokenPreferences;
+
+
+    public CarTrustedDeviceService(Context context) {
+        mContext = context;
+        mCarTrustAgentBleManager = new CarTrustAgentBleManager(context);
+        mCarTrustAgentEnrollmentService = new CarTrustAgentEnrollmentService(this,
+                mCarTrustAgentBleManager);
+        mCarTrustAgentUnlockService = new CarTrustAgentUnlockService(this,
+                mCarTrustAgentBleManager);
+    }
+
+    @Override
+    public synchronized void init() {
+        mCarTrustAgentEnrollmentService.init();
+        mCarTrustAgentUnlockService.init();
+    }
+
+    @Override
+    public synchronized void release() {
+        mCarTrustAgentBleManager.cleanup();
+        mCarTrustAgentEnrollmentService.release();
+        mCarTrustAgentUnlockService.release();
+    }
+
+    /**
+     * Returns the internal {@link CarTrustAgentEnrollmentService} instance.
+     */
+    public CarTrustAgentEnrollmentService getCarTrustAgentEnrollmentService() {
+        return mCarTrustAgentEnrollmentService;
+    }
+
+    /**
+     * Returns the internal {@link CarTrustAgentUnlockService} instance.
+     */
+    public CarTrustAgentUnlockService getCarTrustAgentUnlockService() {
+        return mCarTrustAgentUnlockService;
+    }
+
+    /**
+     * Returns User Id for the given token handle
+     *
+     * @param handle The handle corresponding to the escrow token
+     * @return User id corresponding to the handle
+     */
+    int getUserHandleByTokenHandle(long handle) {
+        return getSharedPrefs().getInt(String.valueOf(handle), -1);
+    }
+
+    void onRemoteDeviceConnected(BluetoothDevice device) {
+        mCarTrustAgentEnrollmentService.onRemoteDeviceConnected(device);
+        mCarTrustAgentUnlockService.onRemoteDeviceConnected(device);
+    }
+
+    void onRemoteDeviceDisconnected(BluetoothDevice device) {
+        mCarTrustAgentEnrollmentService.onRemoteDeviceDisconnected(device);
+        mCarTrustAgentUnlockService.onRemoteDeviceDisconnected(device);
+    }
+
+    void cleanupBleService() {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "cleanupBleService");
+        }
+        mCarTrustAgentBleManager.stopGattServer();
+        mCarTrustAgentBleManager.stopEnrollmentAdvertising();
+        mCarTrustAgentBleManager.stopUnlockAdvertising();
+    }
+
+    SharedPreferences getSharedPrefs() {
+        if (mTrustAgentTokenPreferences != null) {
+            return mTrustAgentTokenPreferences;
+        }
+        mTrustAgentTokenPreferences = mContext.getSharedPreferences(
+                mContext.getString(R.string.token_handle_shared_preferences), Context.MODE_PRIVATE);
+        return mTrustAgentTokenPreferences;
+    }
+
+    @Override
+    public void dump(PrintWriter writer) {
+    }
+}