CarBleTrustAgent.java revision 77e5e49cf9dcceb69b07510c380ae2a9285ebfee
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.content.BroadcastReceiver;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.ServiceConnection;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.UserHandle;
32import android.os.UserManager;
33import android.service.trust.TrustAgentService;
34import android.support.v4.content.LocalBroadcastManager;
35import android.util.Log;
36import com.android.car.trust.comms.SimpleBleServer;
37
38import java.util.concurrent.TimeUnit;
39
40/**
41 * A sample trust agent that demonstrates how to use 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.
45 */
46public class CarBleTrustAgent extends TrustAgentService {
47    public static final String ACTION_REVOKE_TRUST = "revoke-trust-action";
48    public static final String ACTION_ADD_TOKEN = "add-token-action";
49    public static final String ACTION_IS_TOKEN_ACTIVE = "is-token-active-action";
50    public static final String ACTION_REMOVE_TOKEN = "remove-token-action";
51    public static final String ACTION_UNLOCK_DEVICE = "unlock-device-action";
52
53    public static final String ACTION_TOKEN_STATUS_RESULT = "token-status-result-action";
54    public static final String ACTION_ADD_TOKEN_RESULT = "add-token-result-action";
55
56    public static final String INTENT_EXTRA_ESCROW_TOKEN = "extra-escrow-token";
57    public static final String INTENT_EXTRA_TOKEN_HANDLE = "extra-token-handle";
58    public static final String INTENT_EXTRA_TOKEN_STATUS = "extra-token-status";
59
60
61    private static final String TAG = "CarBleTrustAgent";
62
63    private static final long TRUST_DURATION_MS = TimeUnit.MINUTES.toMicros(5);
64    private static final long BLE_RETRY_MS = TimeUnit.SECONDS.toMillis(1);
65
66    private CarUnlockService mCarUnlockService;
67    private LocalBroadcastManager mLocalBroadcastManager;
68
69    private boolean mBleServiceBound;
70
71    // We cannot directly bind to TrustAgentService since the onBind method is final.
72    // As a result, we communicate with the various UI components using a LocalBroadcastManager.
73    private final BroadcastReceiver mTrustEventReceiver = new BroadcastReceiver() {
74        @Override
75        public void onReceive(Context context, Intent intent) {
76            String action = intent.getAction();
77            Log.d(TAG, "Received broadcast: " + action);
78            if (ACTION_REVOKE_TRUST.equals(action)) {
79                revokeTrust();
80            } else if (ACTION_ADD_TOKEN.equals(action)) {
81                byte[] token = intent.getByteArrayExtra(INTENT_EXTRA_ESCROW_TOKEN);
82                addEscrowToken(token, getCurrentUserHandle());
83            } else if (ACTION_IS_TOKEN_ACTIVE.equals(action)) {
84                long handle = intent.getLongExtra(INTENT_EXTRA_TOKEN_HANDLE, -1);
85                isEscrowTokenActive(handle, getCurrentUserHandle());
86            } else if (ACTION_REMOVE_TOKEN.equals(action)) {
87                long handle = intent.getLongExtra(INTENT_EXTRA_TOKEN_HANDLE, -1);
88                removeEscrowToken(handle, getCurrentUserHandle());
89            }
90        }
91    };
92
93    @Override
94    public void onTrustTimeout() {
95        super.onTrustTimeout();
96        Log.d(TAG, "onTrustTimeout(): timeout expired");
97    }
98
99    @Override
100    public void onCreate() {
101        super.onCreate();
102
103        Log.d(TAG, "Bluetooth trust agent starting up");
104        IntentFilter filter = new IntentFilter();
105        filter.addAction(ACTION_REVOKE_TRUST);
106        filter.addAction(ACTION_ADD_TOKEN);
107        filter.addAction(ACTION_IS_TOKEN_ACTIVE);
108        filter.addAction(ACTION_REMOVE_TOKEN);
109
110        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this /* context */);
111        mLocalBroadcastManager.registerReceiver(mTrustEventReceiver, filter);
112
113        // If the user is already unlocked, don't bother starting the BLE service.
114        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
115        if (!um.isUserUnlocked()) {
116            Log.d(TAG, "User locked, will now bind CarUnlockService");
117            Intent intent = new Intent(this, CarUnlockService.class);
118
119            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
120        } else {
121            setManagingTrust(true);
122        }
123    }
124
125    @Override
126    public void onDestroy() {
127        Log.d(TAG, "Car Trust agent shutting down");
128        mLocalBroadcastManager.unregisterReceiver(mTrustEventReceiver);
129
130        // Unbind the service to avoid leaks from BLE stack.
131        if (mBleServiceBound) {
132            unbindService(mServiceConnection);
133        }
134        super.onDestroy();
135    }
136
137    private SimpleBleServer.ConnectionListener mConnectionListener
138            = new SimpleBleServer.ConnectionListener() {
139        @Override
140        public void onServerStarted() {
141            Log.d(TAG, "BLE server started");
142        }
143
144        @Override
145        public void onServerStartFailed(int errorCode) {
146            Log.d(TAG, "BLE server failed to start. Error Code: " + errorCode);
147        }
148
149        @Override
150        public void onDeviceConnected(BluetoothDevice device) {
151            Log.d(TAG, "BLE device connected. Name: " + device.getName()
152                    + " Address: " + device.getAddress());
153        }
154    };
155
156    private CarUnlockService.UnlockServiceCallback mUnlockCallback
157            = new CarUnlockService.UnlockServiceCallback() {
158        @Override
159        public void unlockDevice(byte[] token, long handle) {
160            unlock(token, handle);
161        }
162    };
163
164    private ServiceConnection mServiceConnection = new ServiceConnection() {
165
166        public void onServiceConnected(ComponentName className, IBinder service) {
167            Log.d(TAG, "CarUnlockService connected");
168
169            mBleServiceBound = true;
170            CarUnlockService.UnlockServiceBinder binder
171                    = (CarUnlockService.UnlockServiceBinder) service;
172            mCarUnlockService = binder.getService();
173            mCarUnlockService.addUnlockServiceCallback(mUnlockCallback);
174            mCarUnlockService.addConnectionListener(mConnectionListener);
175            maybeStartBleUnlockService();
176        }
177
178        public void onServiceDisconnected(ComponentName arg0) {
179            mCarUnlockService = null;
180            mBleServiceBound = false;
181        }
182
183    };
184
185    private void maybeStartBleUnlockService() {
186        Log.d(TAG, "Trying to open a Ble GATT server");
187
188        BluetoothManager btManager =
189                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
190        BluetoothGattServer mGattServer
191                = btManager.openGattServer(this, new BluetoothGattServerCallback() {
192            @Override
193            public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
194                super.onConnectionStateChange(device, status, newState);
195            }
196        });
197
198        // The BLE stack is started up before the trust agent service, however Gatt capabilities
199        // might not be ready just yet. Keep trying until a GattServer can open up before proceeding
200        // to start the rest of the BLE services.
201        if (mGattServer == null) {
202            Log.e(TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms");
203
204            Handler handler = new Handler();
205            handler.postDelayed(new Runnable() {
206                @Override
207                public void run() {
208                    maybeStartBleUnlockService();
209                }
210            }, BLE_RETRY_MS);
211        } else {
212            mGattServer.close();
213            Log.d(TAG, "GATT available, starting up UnlockService");
214            mCarUnlockService.start();
215        }
216    }
217
218    private void unlock(byte[] token, long handle) {
219        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
220
221        Log.d(TAG, "About to unlock user. Current handle: " + handle
222                + " Time: " + System.currentTimeMillis());
223        unlockUserWithToken(handle, token, getCurrentUserHandle());
224        Log.d(TAG, "Attempted to unlock user, is user unlocked? " + um.isUserUnlocked()
225                + " Time: " + System.currentTimeMillis());
226        setManagingTrust(true);
227
228        if (um.isUserUnlocked()) {
229            Log.d(TAG, getString(R.string.trust_granted_explanation));
230            grantTrust("Granting trust from escrow token",
231                    TRUST_DURATION_MS, FLAG_GRANT_TRUST_DISMISS_KEYGUARD);
232            // Trust has been granted, disable the BLE server. This trust agent service does
233            // not need to receive additional BLE data.
234            unbindService(mServiceConnection);
235        }
236    }
237
238    @Override
239    public void onEscrowTokenRemoved(long handle, boolean successful) {
240        Log.d(TAG, "onEscrowTokenRemoved. Handle: " + handle + " successful? " + successful);
241    }
242
243    @Override
244    public void onEscrowTokenStateReceived(long handle, int tokenState) {
245        boolean isActive = tokenState == TOKEN_STATE_ACTIVE;
246        Log.d(TAG, "Token handle: " + handle + " isActive: " + isActive);
247
248        Intent intent = new Intent();
249        intent.setAction(ACTION_TOKEN_STATUS_RESULT);
250        intent.putExtra(INTENT_EXTRA_TOKEN_STATUS, isActive);
251
252        mLocalBroadcastManager.sendBroadcast(intent);
253    }
254
255    @Override
256    public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
257        Log.d(TAG, "onEscrowTokenAdded, handle: " + handle);
258
259        Intent intent = new Intent();
260        intent.setAction(ACTION_ADD_TOKEN_RESULT);
261        intent.putExtra(INTENT_EXTRA_TOKEN_HANDLE, handle);
262
263        mLocalBroadcastManager.sendBroadcast(intent);
264    }
265
266    private UserHandle getCurrentUserHandle() {
267        return UserHandle.of(UserHandle.myUserId());
268    }
269}
270