com.android.car.trust.CarBleTrustAgent.java Source code

Java tutorial

Introduction

Here is the source code for com.android.car.trust.CarBleTrustAgent.java

Source

/*
 * Copyright (C) 2016 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.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.trust.TrustAgentService;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import com.android.car.trust.comms.SimpleBleServer;

import java.util.concurrent.TimeUnit;

/**
 * A sample trust agent that demonstrates how to use the escrow token unlock APIs. </p>
 *
 * This trust agent runs during direct boot and binds to a BLE service that listens for remote
 * devices to trigger an unlock. <p/>
 *
 * The permissions for this agent must be enabled as priv-app permissions for it to start.
 */
public class CarBleTrustAgent extends TrustAgentService {
    public static final String ACTION_REVOKE_TRUST = "revoke-trust-action";
    public static final String ACTION_ADD_TOKEN = "add-token-action";
    public static final String ACTION_IS_TOKEN_ACTIVE = "is-token-active-action";
    public static final String ACTION_REMOVE_TOKEN = "remove-token-action";
    public static final String ACTION_UNLOCK_DEVICE = "unlock-device-action";

    public static final String ACTION_TOKEN_STATUS_RESULT = "token-status-result-action";
    public static final String ACTION_ADD_TOKEN_RESULT = "add-token-result-action";

    public static final String INTENT_EXTRA_ESCROW_TOKEN = "extra-escrow-token";
    public static final String INTENT_EXTRA_TOKEN_HANDLE = "extra-token-handle";
    public static final String INTENT_EXTRA_TOKEN_STATUS = "extra-token-status";

    private static final String TAG = "CarBleTrustAgent";

    private static final long TRUST_DURATION_MS = TimeUnit.MINUTES.toMicros(5);
    private static final long BLE_RETRY_MS = TimeUnit.SECONDS.toMillis(1);

    private CarUnlockService mCarUnlockService;
    private LocalBroadcastManager mLocalBroadcastManager;

    private boolean mBleServiceBound;

    // We cannot directly bind to TrustAgentService since the onBind method is final.
    // As a result, we communicate with the various UI components using a LocalBroadcastManager.
    private final BroadcastReceiver mTrustEventReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "Received broadcast: " + action);
            }
            if (ACTION_REVOKE_TRUST.equals(action)) {
                revokeTrust();
            } else if (ACTION_ADD_TOKEN.equals(action)) {
                byte[] token = intent.getByteArrayExtra(INTENT_EXTRA_ESCROW_TOKEN);
                addEscrowToken(token, getCurrentUserHandle());
            } else if (ACTION_IS_TOKEN_ACTIVE.equals(action)) {
                long handle = intent.getLongExtra(INTENT_EXTRA_TOKEN_HANDLE, -1);
                isEscrowTokenActive(handle, getCurrentUserHandle());
            } else if (ACTION_REMOVE_TOKEN.equals(action)) {
                long handle = intent.getLongExtra(INTENT_EXTRA_TOKEN_HANDLE, -1);
                removeEscrowToken(handle, getCurrentUserHandle());
            }
        }
    };

    @Override
    public void onTrustTimeout() {
        super.onTrustTimeout();
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onTrustTimeout(): timeout expired");
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Bluetooth trust agent starting up");
        }
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_REVOKE_TRUST);
        filter.addAction(ACTION_ADD_TOKEN);
        filter.addAction(ACTION_IS_TOKEN_ACTIVE);
        filter.addAction(ACTION_REMOVE_TOKEN);

        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this /* context */);
        mLocalBroadcastManager.registerReceiver(mTrustEventReceiver, filter);

        // If the user is already unlocked, don't bother starting the BLE service.
        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
        if (!um.isUserUnlocked()) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "User locked, will now bind CarUnlockService");
            }
            Intent intent = new Intent(this, CarUnlockService.class);

            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        } else {
            setManagingTrust(true);
        }
    }

    @Override
    public void onDestroy() {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Car Trust agent shutting down");
        }
        mLocalBroadcastManager.unregisterReceiver(mTrustEventReceiver);

        // Unbind the service to avoid leaks from BLE stack.
        if (mBleServiceBound) {
            unbindService(mServiceConnection);
        }
        super.onDestroy();
    }

    private SimpleBleServer.ConnectionListener mConnectionListener = new SimpleBleServer.ConnectionListener() {
        @Override
        public void onServerStarted() {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "BLE server started");
            }
        }

        @Override
        public void onServerStartFailed(int errorCode) {
            Log.w(TAG, "BLE server failed to start. Error Code: " + errorCode);
        }

        @Override
        public void onDeviceConnected(BluetoothDevice device) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "BLE device connected. Name: " + device.getName() + " Address: " + device.getAddress());
            }
        }
    };

    private CarUnlockService.UnlockServiceCallback mUnlockCallback = new CarUnlockService.UnlockServiceCallback() {
        @Override
        public void unlockDevice(byte[] token, long handle) {
            unlock(token, handle);
        }
    };

    private ServiceConnection mServiceConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "CarUnlockService connected");
            }

            mBleServiceBound = true;
            CarUnlockService.UnlockServiceBinder binder = (CarUnlockService.UnlockServiceBinder) service;
            mCarUnlockService = binder.getService();
            mCarUnlockService.addUnlockServiceCallback(mUnlockCallback);
            mCarUnlockService.addConnectionListener(mConnectionListener);
            maybeStartBleUnlockService();
        }

        public void onServiceDisconnected(ComponentName arg0) {
            mCarUnlockService = null;
            mBleServiceBound = false;
        }

    };

    private void maybeStartBleUnlockService() {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Trying to open a Ble GATT server");
        }

        BluetoothManager btManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        BluetoothGattServer mGattServer = btManager.openGattServer(this, new BluetoothGattServerCallback() {
            @Override
            public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
                super.onConnectionStateChange(device, status, newState);
            }
        });

        // The BLE stack is started up before the trust agent service, however Gatt capabilities
        // might not be ready just yet. Keep trying until a GattServer can open up before proceeding
        // to start the rest of the BLE services.
        if (mGattServer == null) {
            Log.e(TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms");

            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    maybeStartBleUnlockService();
                }
            }, BLE_RETRY_MS);
        } else {
            mGattServer.close();
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "GATT available, starting up UnlockService");
            }
            mCarUnlockService.start();
        }
    }

    private void unlock(byte[] token, long handle) {
        UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "About to unlock user. Current handle: " + handle + " Time: " + System.currentTimeMillis());
        }
        unlockUserWithToken(handle, token, getCurrentUserHandle());

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Attempted to unlock user, is user unlocked? " + um.isUserUnlocked() + " Time: "
                    + System.currentTimeMillis());
        }
        setManagingTrust(true);

        if (um.isUserUnlocked()) {
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, getString(R.string.trust_granted_explanation));
            }
            grantTrust("Granting trust from escrow token", TRUST_DURATION_MS, FLAG_GRANT_TRUST_DISMISS_KEYGUARD);
            // Trust has been granted, disable the BLE server. This trust agent service does
            // not need to receive additional BLE data.
            unbindService(mServiceConnection);
        }
    }

    @Override
    public void onEscrowTokenRemoved(long handle, boolean successful) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onEscrowTokenRemoved. Handle: " + handle + " successful? " + successful);
        }
    }

    @Override
    public void onEscrowTokenStateReceived(long handle, int tokenState) {
        boolean isActive = tokenState == TOKEN_STATE_ACTIVE;
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "Token handle: " + handle + " isActive: " + isActive);
        }

        Intent intent = new Intent();
        intent.setAction(ACTION_TOKEN_STATUS_RESULT);
        intent.putExtra(INTENT_EXTRA_TOKEN_STATUS, isActive);

        mLocalBroadcastManager.sendBroadcast(intent);
    }

    @Override
    public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) {
        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "onEscrowTokenAdded, handle: " + handle);
        }

        Intent intent = new Intent();
        intent.setAction(ACTION_ADD_TOKEN_RESULT);
        intent.putExtra(INTENT_EXTRA_TOKEN_HANDLE, handle);

        mLocalBroadcastManager.sendBroadcast(intent);
    }

    private UserHandle getCurrentUserHandle() {
        return UserHandle.of(UserHandle.myUserId());
    }
}