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