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