1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.car.trust;
18
19import android.bluetooth.BluetoothDevice;
20import android.bluetooth.BluetoothGattServer;
21import android.bluetooth.BluetoothGattServerCallback;
22import android.bluetooth.BluetoothManager;
23import android.car.trust.ICarTrustAgentBleService;
24import android.car.trust.ICarTrustAgentTokenRequestDelegate;
25import android.car.trust.ICarTrustAgentUnlockCallback;
26import android.content.ComponentName;
27import android.content.Context;
28import android.content.Intent;
29import android.content.ServiceConnection;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.RemoteException;
33import android.os.UserHandle;
34import android.os.UserManager;
35import android.service.trust.TrustAgentService;
36import android.util.Log;
37
38import java.util.concurrent.TimeUnit;
39
40/**
41 * A BluetoothLE (BLE) based {@link TrustAgentService} that uses the escrow token unlock APIs. </p>
42 *
43 * This trust agent runs during direct boot and binds to a BLE service that listens for remote
44 * devices to trigger an unlock. <p/>
45 *
46 * The permissions for this agent must be enabled as priv-app permissions for it to start.
47 */
48public class CarBleTrustAgent extends TrustAgentService {
49
50    private static final String TAG = CarBleTrustAgent.class.getSimpleName();
51
52    private static final long TRUST_DURATION_MS = TimeUnit.MINUTES.toMicros(5);
53    private static final long BLE_RETRY_MS = TimeUnit.SECONDS.toMillis(1);
54
55    private Handler mHandler;
56    private BluetoothManager mBluetoothManager;
57    private ICarTrustAgentBleService mCarTrustAgentBleService;
58    private boolean mCarTrustAgentBleServiceBound;
59
60    private final ICarTrustAgentUnlockCallback mUnlockCallback =
61            new ICarTrustAgentUnlockCallback.Stub() {
62        @Override
63        public void onUnlockDataReceived(byte[] token, long handle) {
64            UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
65            // TODO(b/77854782): get the actual user to unlock by token
66            UserHandle userHandle = getForegroundUserHandle();
67
68            Log.d(TAG, "About to unlock user. Handle: " + handle
69                    + " Time: " + System.currentTimeMillis());
70            unlockUserWithToken(handle, token, userHandle);
71
72            Log.d(TAG, "Attempted to unlock user, is user unlocked: "
73                    + um.isUserUnlocked(userHandle)
74                    + " Time: " + System.currentTimeMillis());
75            setManagingTrust(true);
76
77            if (um.isUserUnlocked(userHandle)) {
78                Log.d(TAG, getString(R.string.trust_granted_explanation));
79                grantTrust("Granting trust from escrow token",
80                        TRUST_DURATION_MS, FLAG_GRANT_TRUST_DISMISS_KEYGUARD);
81            }
82        }
83    };
84
85    private final ICarTrustAgentTokenRequestDelegate mTokenRequestDelegate =
86            new ICarTrustAgentTokenRequestDelegate.Stub() {
87        @Override
88        public void revokeTrust() {
89            CarBleTrustAgent.this.revokeTrust();
90        }
91
92        @Override
93        public void addEscrowToken(byte[] token, int uid) {
94            CarBleTrustAgent.this.addEscrowToken(token, UserHandle.of(uid));
95        }
96
97        @Override
98        public void removeEscrowToken(long handle, int uid) {
99            CarBleTrustAgent.this.removeEscrowToken(handle, UserHandle.of(uid));
100        }
101
102        @Override
103        public void isEscrowTokenActive(long handle, int uid) {
104            CarBleTrustAgent.this.isEscrowTokenActive(handle, UserHandle.of(uid));
105        }
106    };
107
108    private final ServiceConnection mServiceConnection = new ServiceConnection() {
109        @Override
110        public void onServiceConnected(ComponentName name, IBinder service) {
111            Log.d(TAG, "CarTrustAgentBleService connected");
112            mCarTrustAgentBleServiceBound = true;
113            mCarTrustAgentBleService = ICarTrustAgentBleService.Stub.asInterface(service);
114            try {
115                mCarTrustAgentBleService.registerUnlockCallback(mUnlockCallback);
116                mCarTrustAgentBleService.setTokenRequestDelegate(mTokenRequestDelegate);
117                maybeStartBleUnlockService();
118            } catch (RemoteException e) {
119                Log.e(TAG, "Error registerUnlockCallback", e);
120            }
121        }
122
123        @Override
124        public void onServiceDisconnected(ComponentName name) {
125            if (mCarTrustAgentBleService != null) {
126                try {
127                    mCarTrustAgentBleService.unregisterUnlockCallback(mUnlockCallback);
128                    mCarTrustAgentBleService.setTokenRequestDelegate(null);
129                } catch (RemoteException e) {
130                    Log.e(TAG, "Error unregisterUnlockCallback", e);
131                }
132                mCarTrustAgentBleService = null;
133                mCarTrustAgentBleServiceBound = false;
134            }
135        }
136    };
137
138    @Override
139    public void onCreate() {
140        super.onCreate();
141
142        Log.d(TAG, "Bluetooth trust agent starting up");
143        mHandler = new Handler();
144        mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
145
146        // If the user is already unlocked, don't bother starting the BLE service.
147        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
148        if (!um.isUserUnlocked(getForegroundUserHandle())) {
149            Log.d(TAG, "User locked, will now bind CarTrustAgentBleService");
150            Intent intent = new Intent(this, CarTrustAgentBleService.class);
151            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
152        } else {
153            setManagingTrust(true);
154        }
155    }
156
157    @Override
158    public void onDestroy() {
159        Log.d(TAG, "Car Trust agent shutting down");
160        mHandler.removeCallbacks(null);
161
162        // Unbind the service to avoid leaks from BLE stack.
163        if (mCarTrustAgentBleServiceBound) {
164            unbindService(mServiceConnection);
165        }
166        super.onDestroy();
167    }
168
169    @Override
170    public void onDeviceLocked() {
171        super.onDeviceLocked();
172        if (mCarTrustAgentBleServiceBound) {
173            try {
174                // Only one BLE advertising is allowed, ensure enrolment advertising is stopped
175                // before start unlock advertising.
176                mCarTrustAgentBleService.stopEnrolmentAdvertising();
177                mCarTrustAgentBleService.startUnlockAdvertising();
178            } catch (RemoteException e) {
179                Log.e(TAG, "Error startUnlockAdvertising", e);
180            }
181        }
182    }
183
184    @Override
185    public void onDeviceUnlocked() {
186        super.onDeviceUnlocked();
187        if (mCarTrustAgentBleServiceBound) {
188            try {
189                // Only one BLE advertising is allowed, ensure unlock advertising is stopped.
190                mCarTrustAgentBleService.stopUnlockAdvertising();
191            } catch (RemoteException e) {
192                Log.e(TAG, "Error stopUnlockAdvertising", e);
193            }
194        }
195    }
196
197    private void maybeStartBleUnlockService() {
198        Log.d(TAG, "Trying to open a Ble GATT server");
199        BluetoothGattServer gattServer = mBluetoothManager.openGattServer(
200                this, new BluetoothGattServerCallback() {
201            @Override
202            public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
203                super.onConnectionStateChange(device, status, newState);
204            }
205        });
206
207        // The BLE stack is started up before the trust agent service, however Gatt capabilities
208        // might not be ready just yet. Keep trying until a GattServer can open up before proceeding
209        // to start the rest of the BLE services.
210        if (gattServer == null) {
211            Log.e(TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms");
212            mHandler.postDelayed(this::maybeStartBleUnlockService, BLE_RETRY_MS);
213        } else {
214            mHandler.removeCallbacks(null);
215            gattServer.close();
216            Log.d(TAG, "GATT available, starting up UnlockService");
217            try {
218                mCarTrustAgentBleService.startUnlockAdvertising();
219            } catch (RemoteException e) {
220                Log.e(TAG, "Error startUnlockAdvertising", e);
221            }
222        }
223    }
224
225    @Override
226    public void onEscrowTokenRemoved(long handle, boolean successful) {
227        if (mCarTrustAgentBleServiceBound) {
228            try {
229                mCarTrustAgentBleService.onEscrowTokenRemoved(handle, successful);
230                Log.v(TAG, "Callback onEscrowTokenRemoved");
231            } catch (RemoteException e) {
232                Log.e(TAG, "Error callback onEscrowTokenRemoved", e);
233            }
234        }
235    }
236
237    @Override
238    public void onEscrowTokenStateReceived(long handle, int tokenState) {
239        boolean isActive = tokenState == TOKEN_STATE_ACTIVE;
240        if (mCarTrustAgentBleServiceBound) {
241            try {
242                mCarTrustAgentBleService.onEscrowTokenActiveStateChanged(handle, isActive);
243                Log.v(TAG, "Callback onEscrowTokenActiveStateChanged");
244            } catch (RemoteException e) {
245                Log.e(TAG, "Error callback onEscrowTokenActiveStateChanged", e);
246            }
247        }
248    }
249
250    @Override
251    public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
252        if (mCarTrustAgentBleServiceBound) {
253            try {
254                mCarTrustAgentBleService.onEscrowTokenAdded(token, handle, user.getIdentifier());
255                Log.v(TAG, "Callback onEscrowTokenAdded");
256            } catch (RemoteException e) {
257                Log.e(TAG, "Error callback onEscrowTokenAdded", e);
258            }
259        }
260    }
261
262    /**
263     * TODO(b/77854782): return the {@link UserHandle} of foreground user.
264     * CarBleTrustAgent itself runs as user-0
265     */
266    private UserHandle getForegroundUserHandle() {
267        Log.d(TAG, "getForegroundUserHandle for " + UserHandle.myUserId());
268        return UserHandle.of(UserHandle.myUserId());
269    }
270}
271