diff options
author | Hongwei Wang <hwwang@google.com> | 2018-04-12 12:24:08 -0700 |
---|---|---|
committer | Hongwei Wang <hwwang@google.com> | 2018-04-17 13:29:03 -0700 |
commit | 779f513f6a40a9ae041fc64d5f55a98b1ffe558b (patch) | |
tree | c860f6ba1222ecb71dcf817d84ea8c5e89f203c3 /TrustAgent/src | |
parent | d380b9461a9bc1dac949ee0b52e02746c3b0e164 (diff) | |
download | Car-779f513f6a40a9ae041fc64d5f55a98b1ffe558b.tar.gz |
Merges CarEnrolmentService and CarUnlockService
This is a major overhaul of car TrustAgent component for I/O 2018 trust
device demo
Highlights:
- Reduces the number of services from 3 to 2. Would like to be one but
CarBleTrustAgent[TrustAgent] declares its onBind method as final
- CarEnrolmentActivity lives as a reference how to communicate with
CarTrustAgentBleService and maybe removed from the final release
- Both CarBleTrustAgent[TrustAgent] and CarEnrolmentActivity talk to the
CarTrustAgentBleService for token exchanging
What's next:
- Simplify the reference CarEnrolmentActivity, it currently holds
implementations should live in underlying service
Bug: 76008345
Test: manual
Change-Id: I54090db88c1f701b1e79f623ac0c415be55f59b9
Diffstat (limited to 'TrustAgent/src')
7 files changed, 531 insertions, 544 deletions
diff --git a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java index 352f978abb..2c46dea6a4 100644 --- a/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java +++ b/TrustAgent/src/com/android/car/trust/CarBleTrustAgent.java @@ -20,6 +20,8 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGattServer; import android.bluetooth.BluetoothGattServerCallback; import android.bluetooth.BluetoothManager; +import android.car.trust.ICarTrustAgentBleService; +import android.car.trust.ICarTrustAgentUnlockCallback; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -28,6 +30,7 @@ import android.content.IntentFilter; import android.content.ServiceConnection; import android.os.Handler; import android.os.IBinder; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.service.trust.TrustAgentService; @@ -38,7 +41,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager; import java.util.concurrent.TimeUnit; /** - * A sample trust agent that demonstrates how to use the escrow token unlock APIs. </p> + * A BluetoothLE (BLE) based {@link TrustAgentService} that uses 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/> @@ -46,11 +49,13 @@ import java.util.concurrent.TimeUnit; * The permissions for this agent must be enabled as priv-app permissions for it to start. */ public class CarBleTrustAgent extends TrustAgentService { + + private static final String TAG = CarBleTrustAgent.class.getSimpleName(); + 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"; @@ -64,18 +69,17 @@ public class CarBleTrustAgent extends TrustAgentService { private Handler mHandler; private BluetoothManager mBluetoothManager; - private CarUnlockService mCarUnlockService; + private ICarTrustAgentBleService mCarTrustAgentBleService; + private boolean mCarTrustAgentBleServiceBound; 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(); - Log.d(Utils.LOG_TAG, "Received broadcast: " + action); + Log.d(TAG, "Received broadcast: " + action); if (ACTION_REVOKE_TRUST.equals(action)) { revokeTrust(); } else if (ACTION_ADD_TOKEN.equals(action)) { @@ -91,60 +95,70 @@ public class CarBleTrustAgent extends TrustAgentService { } }; - private final SimpleBleServer.ConnectionCallback mConnectionCallback - = new SimpleBleServer.ConnectionCallback() { + private final ICarTrustAgentUnlockCallback mUnlockCallback = + new ICarTrustAgentUnlockCallback.Stub() { @Override - public void onServerStarted() { - Log.d(Utils.LOG_TAG, "BLE server started"); - } - - @Override - public void onServerStartFailed(int errorCode) { - Log.e(Utils.LOG_TAG, "BLE server failed to start. Error Code: " + errorCode); - } + public void onUnlockDataReceived(byte[] token, long handle) { + UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); + // TODO(b/77854782): get the actual user to unlock by token + UserHandle userHandle = getForegroundUserHandle(); + + Log.d(TAG, "About to unlock user. Handle: " + handle + + " Time: " + System.currentTimeMillis()); + unlockUserWithToken(handle, token, userHandle); + + Log.d(TAG, "Attempted to unlock user, is user unlocked: " + + um.isUserUnlocked(userHandle) + + " Time: " + System.currentTimeMillis()); + setManagingTrust(true); - @Override - public void onDeviceConnected(BluetoothDevice device) { - Log.d(Utils.LOG_TAG, "BLE device connected. Name: " + device.getName() - + " Address: " + device.getAddress()); + if (um.isUserUnlocked(userHandle)) { + 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); + } } }; private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(Utils.LOG_TAG, "CarUnlockService connected"); - mBleServiceBound = true; - CarUnlockService.UnlockServiceBinder binder - = (CarUnlockService.UnlockServiceBinder) service; - mCarUnlockService = binder.getService(); - mCarUnlockService.setOnUnlockDeviceListener(CarBleTrustAgent.this::unlock); - mCarUnlockService.registerConnectionCallback(mConnectionCallback); - maybeStartBleUnlockService(); + public void onServiceConnected(ComponentName name, IBinder service) { + Log.d(TAG, "CarTrustAgentBleService connected"); + mCarTrustAgentBleServiceBound = true; + mCarTrustAgentBleService = ICarTrustAgentBleService.Stub.asInterface(service); + try { + mCarTrustAgentBleService.registerUnlockCallback(mUnlockCallback); + maybeStartBleUnlockService(); + } catch (RemoteException e) { + Log.e(TAG, "Error registerUnlockCallback", e); + } } @Override - public void onServiceDisconnected(ComponentName arg0) { - mCarUnlockService = null; - mBleServiceBound = false; + public void onServiceDisconnected(ComponentName name) { + if (mCarTrustAgentBleService != null) { + try { + mCarTrustAgentBleService.unregisterUnlockCallback(mUnlockCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error unregisterUnlockCallback", e); + } + mCarTrustAgentBleService = null; + mCarTrustAgentBleServiceBound = false; + } } - }; @Override - public void onTrustTimeout() { - super.onTrustTimeout(); - Log.d(Utils.LOG_TAG, "onTrustTimeout(): timeout expired"); - } - - @Override public void onCreate() { super.onCreate(); mHandler = new Handler(); mBluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); - Log.d(Utils.LOG_TAG, "Bluetooth trust agent starting up"); + Log.d(TAG, "Bluetooth trust agent starting up"); IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_REVOKE_TRUST); filter.addAction(ACTION_ADD_TOKEN); @@ -157,9 +171,8 @@ public class CarBleTrustAgent extends TrustAgentService { // If the user is already unlocked, don't bother starting the BLE service. UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); if (!um.isUserUnlocked(getForegroundUserHandle())) { - Log.d(Utils.LOG_TAG, "User locked, will now bind CarUnlockService"); - Intent intent = new Intent(this, CarUnlockService.class); - + Log.d(TAG, "User locked, will now bind CarTrustAgentBleService"); + Intent intent = new Intent(this, CarTrustAgentBleService.class); bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); } else { setManagingTrust(true); @@ -168,19 +181,45 @@ public class CarBleTrustAgent extends TrustAgentService { @Override public void onDestroy() { - Log.d(Utils.LOG_TAG, "Car Trust agent shutting down"); + Log.d(TAG, "Car Trust agent shutting down"); mHandler.removeCallbacks(null); mLocalBroadcastManager.unregisterReceiver(mTrustEventReceiver); // Unbind the service to avoid leaks from BLE stack. - if (mBleServiceBound) { + if (mCarTrustAgentBleServiceBound) { unbindService(mServiceConnection); } super.onDestroy(); } + @Override + public void onDeviceLocked() { + super.onDeviceLocked(); + if (mCarTrustAgentBleServiceBound) { + try { + // Only one BLE advertising is allowed, ensure enrolment advertising is stopped. + mCarTrustAgentBleService.stopEnrolmentAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "Error stopEnrolmentAdvertising", e); + } + } + } + + @Override + public void onDeviceUnlocked() { + super.onDeviceUnlocked(); + if (mCarTrustAgentBleServiceBound) { + try { + // Only one BLE advertising is allowed, ensure unlock advertising is stopped. + mCarTrustAgentBleService.stopUnlockAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "Error stopUnlockAdvertising", e); + } + } + } + private void maybeStartBleUnlockService() { - Log.d(Utils.LOG_TAG, "Trying to open a Ble GATT server"); + Log.d(TAG, "Trying to open a Ble GATT server"); BluetoothGattServer gattServer = mBluetoothManager.openGattServer( this, new BluetoothGattServerCallback() { @Override @@ -193,49 +232,29 @@ public class CarBleTrustAgent extends TrustAgentService { // 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 (gattServer == null) { - Log.e(Utils.LOG_TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms"); - mHandler.postDelayed(() -> maybeStartBleUnlockService(), BLE_RETRY_MS); + Log.e(TAG, "Gatt not available, will try again...in " + BLE_RETRY_MS + "ms"); + mHandler.postDelayed(this::maybeStartBleUnlockService, BLE_RETRY_MS); } else { mHandler.removeCallbacks(null); gattServer.close(); - Log.d(Utils.LOG_TAG, "GATT available, starting up UnlockService"); - mCarUnlockService.start(); - } - } - - private void unlock(byte[] token, long handle) { - UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); - // STOPSHIP: get the actual user to unlock by token - UserHandle userHandle = getForegroundUserHandle(); - - Log.d(Utils.LOG_TAG, "About to unlock user. Handle: " + handle - + " Time: " + System.currentTimeMillis()); - unlockUserWithToken(handle, token, userHandle); - - Log.d(Utils.LOG_TAG, "Attempted to unlock user, is user unlocked: " - + um.isUserUnlocked(userHandle) - + " Time: " + System.currentTimeMillis()); - setManagingTrust(true); - - if (um.isUserUnlocked(userHandle)) { - Log.d(Utils.LOG_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); + Log.d(TAG, "GATT available, starting up UnlockService"); + try { + mCarTrustAgentBleService.startUnlockAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "Error startUnlockAdvertising", e); + } } } @Override public void onEscrowTokenRemoved(long handle, boolean successful) { - Log.d(Utils.LOG_TAG, "onEscrowTokenRemoved. Handle: " + handle + " successful? " + successful); + Log.d(TAG, "onEscrowTokenRemoved. Handle: " + handle + " successful? " + successful); } @Override public void onEscrowTokenStateReceived(long handle, int tokenState) { boolean isActive = tokenState == TOKEN_STATE_ACTIVE; - Log.d(Utils.LOG_TAG, "Token handle: " + handle + " isActive: " + isActive); + Log.d(TAG, "Token handle: " + handle + " isActive: " + isActive); Intent intent = new Intent(); intent.setAction(ACTION_TOKEN_STATUS_RESULT); @@ -246,7 +265,7 @@ public class CarBleTrustAgent extends TrustAgentService { @Override public void onEscrowTokenAdded(byte[] token, long handle, UserHandle user) { - Log.d(Utils.LOG_TAG, "onEscrowTokenAdded, handle: " + handle); + Log.d(TAG, "onEscrowTokenAdded, handle: " + handle); Intent intent = new Intent(); intent.setAction(ACTION_ADD_TOKEN_RESULT); intent.putExtra(INTENT_EXTRA_TOKEN_HANDLE, handle); @@ -255,11 +274,11 @@ public class CarBleTrustAgent extends TrustAgentService { } /** - * STOPSHIP: return the {@link UserHandle} of foreground user. - * CarTrustAgentService itself runs as user-0 + * TODO(b/77854782): return the {@link UserHandle} of foreground user. + * CarBleTrustAgent itself runs as user-0 */ private UserHandle getForegroundUserHandle() { - Log.d(Utils.LOG_TAG, "getForegroundUserHandle for " + UserHandle.myUserId()); + Log.d(TAG, "getForegroundUserHandle for " + UserHandle.myUserId()); return UserHandle.of(UserHandle.myUserId()); } } diff --git a/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java b/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java index 8047a4b447..2723a6a34a 100644 --- a/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java +++ b/TrustAgent/src/com/android/car/trust/CarEnrolmentActivity.java @@ -23,6 +23,9 @@ import static com.android.car.trust.CarBleTrustAgent.INTENT_EXTRA_TOKEN_STATUS; import android.Manifest; import android.app.Activity; import android.bluetooth.BluetoothDevice; +import android.car.trust.ICarTrustAgentBleCallback; +import android.car.trust.ICarTrustAgentBleService; +import android.car.trust.ICarTrustAgentEnrolmentCallback; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -40,13 +43,13 @@ import android.widget.TextView; import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import com.android.car.trust.CarEnrolmentService.OnEnrolmentDataReceivedListener; -import com.android.car.trust.SimpleBleServer.ConnectionCallback; - /** - * Setup activity that binds {@link CarEnrolmentService} and starts the enrolment process. + * Setup activity that binds {@link CarTrustAgentBleService} and starts the enrolment process. */ public class CarEnrolmentActivity extends Activity { + + private static final String TAG = CarEnrolmentActivity.class.getSimpleName(); + private static final String SP_HANDLE_KEY = "sp-test"; private static final int FINE_LOCATION_REQUEST_CODE = 42; @@ -54,77 +57,100 @@ public class CarEnrolmentActivity extends Activity { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - Log.d(Utils.LOG_TAG, "Received broadcast: " + action); + Log.d(TAG, "Received broadcast: " + action); if (ACTION_TOKEN_STATUS_RESULT.equals(action)) { boolean tokenActive = intent.getBooleanExtra(INTENT_EXTRA_TOKEN_STATUS, false); - appendOutputText("Is token active? " + tokenActive + " handle: " + mHandle); + appendOutputText("Is token active? " + tokenActive + " handle: " + mTokenHandle); } else if (ACTION_ADD_TOKEN_RESULT.equals(action)) { final long handle = intent.getLongExtra(INTENT_EXTRA_TOKEN_HANDLE, -1); runOnUiThread(() -> { mPrefs.edit().putLong(SP_HANDLE_KEY, handle).apply(); - Log.d(Utils.LOG_TAG, "stored new handle"); + Log.d(TAG, "stored new handle"); }); - mEnrolmentService.sendHandle(handle, mDevice); - appendOutputText("Escrow Token Added. Handle: " + handle - + "\nLock and unlock the device to activate token"); + try { + mCarTrustAgentBleService.sendEnrolmentHandle(mDevice, handle); + appendOutputText("Escrow Token Added. Handle: " + handle + + "\nLock and unlock the device to activate token"); + } catch (RemoteException e) { + Log.e(TAG, "Error sendEnrolmentHandle", e); + } } } }; - private final ConnectionCallback mConnectionCallback = new ConnectionCallback() { + private final ICarTrustAgentBleCallback mBleConnectionCallback = + new ICarTrustAgentBleCallback.Stub() { @Override - public void onServerStarted() { + public void onBleServerStartSuccess() { appendOutputText("Server started"); } @Override - public void onServerStartFailed(int errorCode) { + public void onBleServerStartFailure(int errorCode) { appendOutputText("Server failed to start, error code: " + errorCode); } @Override - public void onDeviceConnected(BluetoothDevice device) { + public void onBleDeviceConnected(BluetoothDevice device) { mDevice = device; appendOutputText("Device connected: " + device.getName() - + " addr: " + device.getAddress()); + + " address: " + device.getAddress()); + } + + @Override + public void onBleDeviceDisconnected(BluetoothDevice device) { + mDevice = null; + appendOutputText("Device disconnected: " + device.getName() + + " address: " + device.getAddress()); } }; - private final OnEnrolmentDataReceivedListener mEnrolmentListener = (byte[] token) -> { - appendOutputText("Enrolment data received "); - addEscrowToken(token); + private final ICarTrustAgentEnrolmentCallback mEnrolmentCallback = + new ICarTrustAgentEnrolmentCallback.Stub() { + @Override + public void onEnrolmentDataReceived(byte[] token) { + appendOutputText("Enrolment data received "); + addEscrowToken(token); + } }; private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override - public void onServiceConnected(ComponentName className, - IBinder service) { - mServiceBound = true; - CarEnrolmentService.EnrolmentServiceBinder binder - = (CarEnrolmentService.EnrolmentServiceBinder) service; - mEnrolmentService = binder.getService(); - mEnrolmentService.registerEnrolmentListener(mEnrolmentListener); - mEnrolmentService.registerConnectionCallback(mConnectionCallback); - mEnrolmentService.start(); + public void onServiceConnected(ComponentName name, IBinder service) { + mCarTrustAgentBleServiceBound = true; + mCarTrustAgentBleService = ICarTrustAgentBleService.Stub.asInterface(service); + try { + mCarTrustAgentBleService.registerBleCallback(mBleConnectionCallback); + mCarTrustAgentBleService.registerEnrolmentCallback(mEnrolmentCallback); + mCarTrustAgentBleService.startEnrolmentAdvertising(); + } catch (RemoteException e) { + Log.e(TAG, "Error startEnrolmentAdvertising", e); + } } @Override - public void onServiceDisconnected(ComponentName arg0) { - mEnrolmentService.unregisterEnrolmentListener(mEnrolmentListener); - mEnrolmentService.unregisterConnectionCallback(mConnectionCallback); - mEnrolmentService = null; - mServiceBound = false; + public void onServiceDisconnected(ComponentName name) { + if (mCarTrustAgentBleService != null) { + try { + mCarTrustAgentBleService.unregisterBleCallback(mBleConnectionCallback); + mCarTrustAgentBleService.unregisterEnrolmentCallback(mEnrolmentCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error unregister callbacks", e); + } + mCarTrustAgentBleService = null; + } + mCarTrustAgentBleServiceBound = false; } }; private TextView mOutputText; - private long mHandle; - private CarEnrolmentService mEnrolmentService; + private long mTokenHandle; + private ICarTrustAgentBleService mCarTrustAgentBleService; + private boolean mCarTrustAgentBleServiceBound; private BluetoothDevice mDevice; - private boolean mServiceBound; private LocalBroadcastManager mLocalBroadcastManager; private SharedPreferences mPrefs; @@ -144,7 +170,7 @@ public class CarEnrolmentActivity extends Activity { mLocalBroadcastManager.registerReceiver(mReceiver, filter); findViewById(R.id.start_button).setOnClickListener((view) -> { - Intent bindIntent = new Intent(this, CarEnrolmentService.class); + Intent bindIntent = new Intent(this, CarTrustAgentBleService.class); bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE); }); @@ -164,26 +190,20 @@ public class CarEnrolmentActivity extends Activity { requestPermissions( new String[] { android.Manifest.permission.ACCESS_FINE_LOCATION }, FINE_LOCATION_REQUEST_CODE); - } - - if (!mPrefs.contains(SP_HANDLE_KEY)) { - appendOutputText("No handles found."); - return; - } - - try { - mHandle = mPrefs.getLong(SP_HANDLE_KEY, -1); - Log.d(Utils.LOG_TAG, "onResume, checking handle active: " + mHandle); - isTokenActive(mHandle); - } catch (RemoteException e) { - Log.e(Utils.LOG_TAG, "Error checking if token is valid"); - appendOutputText("Error checking if token is valid"); + } else { + if (mPrefs.contains(SP_HANDLE_KEY)) { + mTokenHandle = mPrefs.getLong(SP_HANDLE_KEY, -1); + Log.d(TAG, "onResume, checking handle active: " + mTokenHandle); + isTokenActive(mTokenHandle); + } else { + appendOutputText("No handles found"); + } } } @Override protected void onDestroy() { - if (mServiceBound) { + if (mCarTrustAgentBleServiceBound) { unbindService(mServiceConnection); } super.onDestroy(); @@ -193,7 +213,7 @@ public class CarEnrolmentActivity extends Activity { runOnUiThread(() -> mOutputText.append("\n" + text)); } - private void isTokenActive(long handle) throws RemoteException { + private void isTokenActive(long handle) { Intent intent = new Intent(); intent.setAction(CarBleTrustAgent.ACTION_IS_TOKEN_ACTIVE); intent.putExtra(CarBleTrustAgent.INTENT_EXTRA_TOKEN_HANDLE, handle); diff --git a/TrustAgent/src/com/android/car/trust/CarEnrolmentService.java b/TrustAgent/src/com/android/car/trust/CarEnrolmentService.java deleted file mode 100644 index 16b6bdfbe7..0000000000 --- a/TrustAgent/src/com/android/car/trust/CarEnrolmentService.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright (C) 2017 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.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattService; -import android.content.Intent; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.ParcelUuid; -import android.util.Log; - -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; - -/** - * A service that receives escrow token enrollment requests from remote devices. - */ -public class CarEnrolmentService extends SimpleBleServer { - - private final Set<OnEnrolmentDataReceivedListener> mEnrolmentListeners = new HashSet<>(); - private final IBinder mBinder = new EnrolmentServiceBinder(); - - private BluetoothGattService mEnrolmentService; - private BluetoothGattCharacteristic mEnrolmentEscrowToken; - private BluetoothGattCharacteristic mEnrolmentTokenHandle; - - @Override - public void onCreate() { - super.onCreate(); - setupEnrolmentService(); - } - - public void start() { - ParcelUuid uuid = new ParcelUuid( - UUID.fromString(getString(R.string.enrollment_service_uuid))); - Log.d(Utils.LOG_TAG, "CarEnrolmentService start with uuid: " + uuid); - start(uuid, mEnrolmentService, new Handler()); - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public void onCharacteristicWrite(BluetoothDevice device, - int requestId, BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { - if (characteristic.getUuid().equals(mEnrolmentEscrowToken.getUuid())) { - Log.d(Utils.LOG_TAG, "Enrolment token received, value: " + Utils.getLong(value)); - for (OnEnrolmentDataReceivedListener callback : mEnrolmentListeners) { - callback.onEnrolmentDataReceived(value); - } - } - } - - @Override - public void onCharacteristicRead(BluetoothDevice device, - int requestId, int offset, BluetoothGattCharacteristic characteristic) { - //Enrolment service should not have any read requests. - } - - /** - * Register {@link OnEnrolmentDataReceivedListener} to receive the enrolment data - * @param listener - */ - public void registerEnrolmentListener(OnEnrolmentDataReceivedListener listener) { - mEnrolmentListeners.add(listener); - } - - /** - * Unregister {@link OnEnrolmentDataReceivedListener} from receiving enrolment data - * @param listener - */ - public void unregisterEnrolmentListener(OnEnrolmentDataReceivedListener listener) { - mEnrolmentListeners.remove(listener); - } - - public void sendHandle(long handle, BluetoothDevice device) { - mEnrolmentTokenHandle.setValue(Utils.getBytes(handle)); - - Log.d(Utils.LOG_TAG, "Sending notification for EscrowToken Handle"); - notifyCharacteristicChanged(device, mEnrolmentTokenHandle, false); - } - - public class EnrolmentServiceBinder extends Binder { - public CarEnrolmentService getService() { - return CarEnrolmentService.this; - } - } - - // Create services and characteristics for enrolling new unlocking escrow tokens - private void setupEnrolmentService() { - mEnrolmentService = new BluetoothGattService( - UUID.fromString(getString(R.string.enrollment_service_uuid)), - BluetoothGattService.SERVICE_TYPE_PRIMARY); - - // Characteristic to describe the escrow token being used for unlock - mEnrolmentEscrowToken = new BluetoothGattCharacteristic( - UUID.fromString(getString(R.string.enrollment_token_uuid)), - BluetoothGattCharacteristic.PROPERTY_WRITE, - BluetoothGattCharacteristic.PERMISSION_WRITE); - - // Characteristic to describe the handle being used for this escrow token - mEnrolmentTokenHandle = new BluetoothGattCharacteristic( - UUID.fromString(getString(R.string.enrollment_handle_uuid)), - BluetoothGattCharacteristic.PROPERTY_NOTIFY, - BluetoothGattCharacteristic.PERMISSION_READ); - - mEnrolmentService.addCharacteristic(mEnrolmentEscrowToken); - mEnrolmentService.addCharacteristic(mEnrolmentTokenHandle); - } - - /** - * Interface to receive enrolment data. - */ - public interface OnEnrolmentDataReceivedListener { - /** - * Called when the enrolment data is received - * @param token enrolment data - */ - void onEnrolmentDataReceived(byte[] token); - } -} diff --git a/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java b/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java new file mode 100644 index 0000000000..392bd7593b --- /dev/null +++ b/TrustAgent/src/com/android/car/trust/CarTrustAgentBleService.java @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2018 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.BluetoothGattCharacteristic; +import android.bluetooth.BluetoothGattService; +import android.car.trust.ICarTrustAgentBleCallback; +import android.car.trust.ICarTrustAgentBleService; +import android.car.trust.ICarTrustAgentEnrolmentCallback; +import android.car.trust.ICarTrustAgentUnlockCallback; +import android.content.Intent; +import android.os.IBinder; +import android.os.ParcelUuid; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Abstracts enrolment and unlock token exchange via BluetoothLE (BLE). + * {@link CarBleTrustAgent} and any enrolment client should bind to + * {@link ICarTrustAgentBleService}. + */ +public class CarTrustAgentBleService extends SimpleBleServer { + + private static final String TAG = CarTrustAgentBleService.class.getSimpleName(); + + private RemoteCallbackList<ICarTrustAgentBleCallback> mBleCallbacks; + private RemoteCallbackList<ICarTrustAgentEnrolmentCallback> mEnrolmentCallbacks; + private RemoteCallbackList<ICarTrustAgentUnlockCallback> mUnlockCallbacks; + private CarTrustAgentBleWrapper mCarTrustBleService; + + private ParcelUuid mEnrolmentUuid; + private BluetoothGattCharacteristic mEnrolmentEscrowToken; + private BluetoothGattCharacteristic mEnrolmentTokenHandle; + private BluetoothGattService mEnrolmentGattServer; + + private ParcelUuid mUnlockUuid; + private BluetoothGattCharacteristic mUnlockEscrowToken; + private BluetoothGattCharacteristic mUnlockTokenHandle; + private BluetoothGattService mUnlockGattServer; + private byte[] mCurrentUnlockToken; + private Long mCurrentUnlockHandle; + + @Override + public void onCreate() { + super.onCreate(); + mBleCallbacks = new RemoteCallbackList<>(); + mEnrolmentCallbacks = new RemoteCallbackList<>(); + mUnlockCallbacks = new RemoteCallbackList<>(); + mCarTrustBleService = new CarTrustAgentBleWrapper(); + + setupEnrolmentBleServer(); + setupUnlockBleServer(); + } + + @Override + public IBinder onBind(Intent intent) { + return mCarTrustBleService; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + // keep it alive. + return START_STICKY; + } + + @Override + public void onCharacteristicWrite(final BluetoothDevice device, int requestId, + BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean + responseNeeded, int offset, byte[] value) { + UUID uuid = characteristic.getUuid(); + Log.d(TAG, "onCharacteristicWrite received uuid: " + uuid); + if (uuid.equals(mEnrolmentEscrowToken.getUuid())) { + final int callbackCount = mEnrolmentCallbacks.beginBroadcast(); + for (int i = 0; i < callbackCount; i++) { + try { + mEnrolmentCallbacks.getBroadcastItem(i).onEnrolmentDataReceived(value); + } catch (RemoteException e) { + Log.e(TAG, "Error callback onEnrolmentDataReceived", e); + } + } + mEnrolmentCallbacks.finishBroadcast(); + } else if (uuid.equals(mUnlockEscrowToken.getUuid())) { + mCurrentUnlockToken = value; + maybeSendUnlockToken(); + } else if (uuid.equals(mUnlockTokenHandle.getUuid())) { + mCurrentUnlockHandle = getLong(value); + maybeSendUnlockToken(); + } + } + + @Override + public void onCharacteristicRead(BluetoothDevice device, + int requestId, int offset, final BluetoothGattCharacteristic characteristic) { + // Ignored read requests. + } + + @Override + protected void onAdvertiseStartSuccess() { + final int callbackCount = mBleCallbacks.beginBroadcast(); + for (int i = 0; i < callbackCount; i++) { + try { + mBleCallbacks.getBroadcastItem(i).onBleServerStartSuccess(); + } catch (RemoteException e) { + Log.e(TAG, "Error callback onBleServerStartSuccess", e); + } + } + mBleCallbacks.finishBroadcast(); + } + + @Override + protected void onAdvertiseStartFailure(int errorCode) { + final int callbackCount = mBleCallbacks.beginBroadcast(); + for (int i = 0; i < callbackCount; i++) { + try { + mBleCallbacks.getBroadcastItem(i).onBleServerStartFailure(errorCode); + } catch (RemoteException e) { + Log.e(TAG, "Error callback onBleServerStartFailure", e); + } + } + mBleCallbacks.finishBroadcast(); + } + + @Override + protected void onAdvertiseDeviceConnected(BluetoothDevice device) { + final int callbackCount = mBleCallbacks.beginBroadcast(); + for (int i = 0; i < callbackCount; i++) { + try { + mBleCallbacks.getBroadcastItem(i).onBleDeviceConnected(device); + } catch (RemoteException e) { + Log.e(TAG, "Error callback onBleDeviceConnected", e); + } + } + mBleCallbacks.finishBroadcast(); + } + + @Override + protected void onAdvertiseDeviceDisconnected(BluetoothDevice device) { + final int callbackCount = mBleCallbacks.beginBroadcast(); + for (int i = 0; i < callbackCount; i++) { + try { + mBleCallbacks.getBroadcastItem(i).onBleDeviceDisconnected(device); + } catch (RemoteException e) { + Log.e(TAG, "Error callback onBleDeviceDisconnected", e); + } + } + mBleCallbacks.finishBroadcast(); + } + + private void setupEnrolmentBleServer() { + mEnrolmentUuid = new ParcelUuid( + UUID.fromString(getString(R.string.enrollment_service_uuid))); + mEnrolmentGattServer = new BluetoothGattService( + UUID.fromString(getString(R.string.enrollment_service_uuid)), + BluetoothGattService.SERVICE_TYPE_PRIMARY); + + // Characteristic to describe the escrow token being used for unlock + mEnrolmentEscrowToken = new BluetoothGattCharacteristic( + UUID.fromString(getString(R.string.enrollment_token_uuid)), + BluetoothGattCharacteristic.PROPERTY_WRITE, + BluetoothGattCharacteristic.PERMISSION_WRITE); + + // Characteristic to describe the handle being used for this escrow token + mEnrolmentTokenHandle = new BluetoothGattCharacteristic( + UUID.fromString(getString(R.string.enrollment_handle_uuid)), + BluetoothGattCharacteristic.PROPERTY_NOTIFY, + BluetoothGattCharacteristic.PERMISSION_READ); + + mEnrolmentGattServer.addCharacteristic(mEnrolmentEscrowToken); + mEnrolmentGattServer.addCharacteristic(mEnrolmentTokenHandle); + } + + private void setupUnlockBleServer() { + mUnlockUuid = new ParcelUuid( + UUID.fromString(getString(R.string.unlock_service_uuid))); + mUnlockGattServer = new BluetoothGattService( + UUID.fromString(getString(R.string.unlock_service_uuid)), + BluetoothGattService.SERVICE_TYPE_PRIMARY); + + // Characteristic to describe the escrow token being used for unlock + mUnlockEscrowToken = new BluetoothGattCharacteristic( + UUID.fromString(getString(R.string.unlock_escrow_token_uiid)), + BluetoothGattCharacteristic.PROPERTY_WRITE, + BluetoothGattCharacteristic.PERMISSION_WRITE); + + // Characteristic to describe the handle being used for this escrow token + mUnlockTokenHandle = new BluetoothGattCharacteristic( + UUID.fromString(getString(R.string.unlock_handle_uiid)), + BluetoothGattCharacteristic.PROPERTY_WRITE, + BluetoothGattCharacteristic.PERMISSION_WRITE); + + mUnlockGattServer.addCharacteristic(mUnlockEscrowToken); + mUnlockGattServer.addCharacteristic(mUnlockTokenHandle); + } + + private synchronized void maybeSendUnlockToken() { + if (mCurrentUnlockToken == null || mCurrentUnlockHandle == null) { + return; + } + Log.d(TAG, "Handle and token both received, requesting unlock. Time: " + + System.currentTimeMillis()); + final int callbackCount = mUnlockCallbacks.beginBroadcast(); + for (int i = 0; i < callbackCount; i++) { + try { + mUnlockCallbacks.getBroadcastItem(i).onUnlockDataReceived( + mCurrentUnlockToken, mCurrentUnlockHandle); + } catch (RemoteException e) { + Log.e(TAG, "Error callback onUnlockDataReceived", e); + } + } + mUnlockCallbacks.finishBroadcast(); + mCurrentUnlockHandle = null; + mCurrentUnlockToken = null; + } + + private static byte[] getBytes(long primitive) { + ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); + buffer.putLong(0, primitive); + return buffer.array(); + } + + private static long getLong(byte[] bytes) { + ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); + buffer.put(bytes); + buffer.flip(); + return buffer.getLong(); + } + + private final class CarTrustAgentBleWrapper extends ICarTrustAgentBleService.Stub { + @Override + public void registerBleCallback(ICarTrustAgentBleCallback callback) { + mBleCallbacks.register(callback); + } + + @Override + public void unregisterBleCallback(ICarTrustAgentBleCallback callback) { + mBleCallbacks.unregister(callback); + } + + @Override + public void startEnrolmentAdvertising() { + Log.d(TAG, "startEnrolmentAdvertising"); + startAdvertising(mEnrolmentUuid, mEnrolmentGattServer); + } + + @Override + public void stopEnrolmentAdvertising() { + Log.d(TAG, "stopEnrolmentAdvertising"); + stopAdvertising(); + } + + @Override + public void sendEnrolmentHandle(BluetoothDevice device, long handle) { + Log.d(TAG, "sendEnrolmentHandle: " + handle); + mEnrolmentTokenHandle.setValue(getBytes(handle)); + notifyCharacteristicChanged(device, mEnrolmentTokenHandle, false); + } + + @Override + public void registerEnrolmentCallback(ICarTrustAgentEnrolmentCallback callback) { + mEnrolmentCallbacks.register(callback); + } + + @Override + public void unregisterEnrolmentCallback(ICarTrustAgentEnrolmentCallback callback) { + mEnrolmentCallbacks.unregister(callback); + } + + @Override + public void startUnlockAdvertising() { + Log.d(TAG, "startUnlockAdvertising"); + startAdvertising(mUnlockUuid, mUnlockGattServer); + } + + @Override + public void stopUnlockAdvertising() { + Log.d(TAG, "stopUnlockAdvertising"); + stopAdvertising(); + } + + @Override + public void registerUnlockCallback(ICarTrustAgentUnlockCallback callback) { + mUnlockCallbacks.register(callback); + } + + @Override + public void unregisterUnlockCallback(ICarTrustAgentUnlockCallback callback) { + mUnlockCallbacks.unregister(callback); + } + } +} diff --git a/TrustAgent/src/com/android/car/trust/CarUnlockService.java b/TrustAgent/src/com/android/car/trust/CarUnlockService.java deleted file mode 100644 index fecfe33c78..0000000000 --- a/TrustAgent/src/com/android/car/trust/CarUnlockService.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2017 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.BluetoothGattCharacteristic; -import android.bluetooth.BluetoothGattService; -import android.content.Intent; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.ParcelUuid; -import android.util.Log; - -import java.util.UUID; - -/** - * A service that receives unlock requests from remote devices. - */ -public class CarUnlockService extends SimpleBleServer { - - private final IBinder mBinder = new UnlockServiceBinder(); - - private BluetoothGattService mUnlockService; - private BluetoothGattCharacteristic mUnlockEscrowToken; - private BluetoothGattCharacteristic mUnlockTokenHandle; - - private OnUnlockDeviceListener mOnUnlockDeviceListener; - - private byte[] mCurrentToken; - private Long mCurrentHandle; - - public class UnlockServiceBinder extends Binder { - public CarUnlockService getService() { - return CarUnlockService.this; - } - } - - @Override - public void onCreate() { - super.onCreate(); - setupUnlockService(); - } - - /** - * Start advertising the BLE unlock service - */ - public void start() { - ParcelUuid uuid = new ParcelUuid(UUID.fromString(getString(R.string.unlock_service_uuid))); - Log.d(Utils.LOG_TAG, "CarUnlockService start with uuid: " + uuid); - start(uuid, mUnlockService, new Handler()); - } - - public void setOnUnlockDeviceListener(OnUnlockDeviceListener callback) { - mOnUnlockDeviceListener = callback; - } - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public void onCharacteristicWrite(BluetoothDevice device, - int requestId, BluetoothGattCharacteristic characteristic, - boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { - UUID uuid = characteristic.getUuid(); - - if (uuid.equals(mUnlockTokenHandle.getUuid())) { - Log.d(Utils.LOG_TAG, "Unlock handle received, value: " + Utils.getLong(value)); - mCurrentHandle = Utils.getLong(value); - unlockDataReceived(); - } else if (uuid.equals(mUnlockEscrowToken.getUuid())) { - Log.d(Utils.LOG_TAG, "Unlock escrow token received, value: " + Utils.getLong(value)); - mCurrentToken = value; - unlockDataReceived(); - } - } - - @Override - public void onCharacteristicRead(BluetoothDevice device, - int requestId, int offset, BluetoothGattCharacteristic characteristic) { - // The BLE unlock service should not receive any read requests. - } - - private synchronized void unlockDataReceived() { - // If any piece of the unlocking data is not received, then do not unlock. - if (mCurrentHandle == null || mCurrentToken == null) { - return; - } - - Log.d(Utils.LOG_TAG, "Handle and token both received, requesting unlock. Time: " - + System.currentTimeMillis()); - // Both the handle and token has been received, try to unlock the device. - mOnUnlockDeviceListener.onUnlockDevice(mCurrentToken, mCurrentHandle); - - // Once we've notified the client of the unlocking data, clear it out. - mCurrentToken = null; - mCurrentHandle = null; - } - - - // Create services and characteristics to receive tokens and handles for unlocking the device. - private void setupUnlockService() { - mUnlockService = new BluetoothGattService( - UUID.fromString(getString(R.string.unlock_service_uuid)), - BluetoothGattService.SERVICE_TYPE_PRIMARY); - - // Characteristic to describe the escrow token being used for unlock - mUnlockEscrowToken = new BluetoothGattCharacteristic( - UUID.fromString(getString(R.string.unlock_escrow_token_uiid)), - BluetoothGattCharacteristic.PROPERTY_WRITE, - BluetoothGattCharacteristic.PERMISSION_WRITE); - - // Characteristic to describe the handle being used for this escrow token - mUnlockTokenHandle = new BluetoothGattCharacteristic( - UUID.fromString(getString(R.string.unlock_handle_uiid)), - BluetoothGattCharacteristic.PROPERTY_WRITE, - BluetoothGattCharacteristic.PERMISSION_WRITE); - - mUnlockService.addCharacteristic(mUnlockEscrowToken); - mUnlockService.addCharacteristic(mUnlockTokenHandle); - } - - /** - * Interface to send the token to unlock a device - */ - public interface OnUnlockDeviceListener { - /** - * Sends token to unlock the trust device - * @param token - * @param handle - */ - void onUnlockDevice(byte[] token, long handle); - } -} diff --git a/TrustAgent/src/com/android/car/trust/SimpleBleServer.java b/TrustAgent/src/com/android/car/trust/SimpleBleServer.java index 8c46e24142..7fbf2fedbd 100644 --- a/TrustAgent/src/com/android/car/trust/SimpleBleServer.java +++ b/TrustAgent/src/com/android/car/trust/SimpleBleServer.java @@ -37,14 +37,13 @@ import android.os.Handler; import android.os.ParcelUuid; import android.util.Log; -import java.util.HashSet; -import java.util.Set; - /** * A generic service to start a BLE */ public abstract class SimpleBleServer extends Service { + private static final String TAG = SimpleBleServer.class.getSimpleName(); + private static final int BLE_RETRY_LIMIT = 5; private static final int BLE_RETRY_INTERVAL_MS = 1000; @@ -52,19 +51,15 @@ public abstract class SimpleBleServer extends Service { @Override public void onStartSuccess(AdvertiseSettings settingsInEffect) { super.onStartSuccess(settingsInEffect); - Log.d(Utils.LOG_TAG, "Successfully started advertising service"); - for (ConnectionCallback callback : mConnectionCallbacks) { - callback.onServerStarted(); - } + Log.d(TAG, "Successfully started advertising service"); + onAdvertiseStartSuccess(); } @Override public void onStartFailure(int errorCode) { super.onStartFailure(errorCode); - Log.e(Utils.LOG_TAG, "Failed to advertise, errorCode: " + errorCode); - for (ConnectionCallback callback : mConnectionCallbacks) { - callback.onServerStartFailed(errorCode); - } + Log.e(TAG, "Failed to advertise, errorCode: " + errorCode); + onAdvertiseStartFailure(errorCode); } }; @@ -73,46 +68,46 @@ public abstract class SimpleBleServer extends Service { @Override public void onConnectionStateChange(BluetoothDevice device, final int status, final int newState) { - Log.d(Utils.LOG_TAG, "GattServer connection change status: " + status + Log.d(TAG, "GattServer connection change status: " + status + " newState: " + newState + " device name: " + device.getName()); - if (newState == BluetoothProfile.STATE_CONNECTED) { - for (ConnectionCallback callback : mConnectionCallbacks) { - callback.onDeviceConnected(device); - } + switch (newState) { + case BluetoothProfile.STATE_CONNECTED: + onAdvertiseDeviceConnected(device); + break; + case BluetoothProfile.STATE_DISCONNECTED: + onAdvertiseDeviceDisconnected(device); + break; } } @Override public void onServiceAdded(final int status, BluetoothGattService service) { - Log.d(Utils.LOG_TAG, "Service added status: " + status + " uuid: " + service.getUuid()); + Log.d(TAG, "Service added status: " + status + " uuid: " + service.getUuid()); } @Override public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, final BluetoothGattCharacteristic characteristic) { - Log.d(Utils.LOG_TAG, "Read request for characteristic: " + characteristic.getUuid()); + Log.d(TAG, "Read request for characteristic: " + characteristic.getUuid()); mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue()); - SimpleBleServer. - this.onCharacteristicRead(device, requestId, offset, characteristic); + onCharacteristicRead(device, requestId, offset, characteristic); } @Override public void onCharacteristicWriteRequest(final BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { - Log.d(Utils.LOG_TAG, "Write request for characteristic: " + characteristic.getUuid()); + Log.d(TAG, "Write request for characteristic: " + characteristic.getUuid()); mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value); - - SimpleBleServer. - this.onCharacteristicWrite(device, requestId, characteristic, + onCharacteristicWrite(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value); } }; - private final Set<ConnectionCallback> mConnectionCallbacks = new HashSet<>(); + private final Handler mHandler = new Handler(); private BluetoothManager mBluetoothManager; private BluetoothLeAdvertiser mAdvertiser; @@ -127,19 +122,17 @@ public abstract class SimpleBleServer extends Service { * * @param advertiseUuid Service Uuid used in the {@link AdvertiseData} * @param service {@link BluetoothGattService} that will be discovered by clients - * @param handler {@link Handler} instance to run the retry logic */ - protected void start(ParcelUuid advertiseUuid, BluetoothGattService service, - Handler handler) { + protected void startAdvertising(ParcelUuid advertiseUuid, BluetoothGattService service) { if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - Log.e(Utils.LOG_TAG, "System does not support BLE"); + Log.e(TAG, "System does not support BLE"); return; } mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mGattServer = mBluetoothManager.openGattServer(this, mGattServerCallback); if (mGattServer == null) { - Log.e(Utils.LOG_TAG, "Gatt Server not created"); + Log.e(TAG, "Gatt Server not created"); return; } @@ -160,27 +153,41 @@ public abstract class SimpleBleServer extends Service { .build(); mAdvertiserStartCount = 0; - startAdvertisingInternally(settings, data, handler); + startAdvertisingInternally(settings, data); } - private void startAdvertisingInternally(AdvertiseSettings settings, - AdvertiseData data, Handler handler) { + private void startAdvertisingInternally(AdvertiseSettings settings, AdvertiseData data) { mAdvertiserStartCount += 1; mAdvertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser(); if (mAdvertiser == null && mAdvertiserStartCount < BLE_RETRY_LIMIT) { - handler.postDelayed(() -> startAdvertisingInternally(settings, data, handler), + mHandler.postDelayed(() -> startAdvertisingInternally(settings, data), BLE_RETRY_INTERVAL_MS); } else { - handler.removeCallbacks(null); + mHandler.removeCallbacks(null); mAdvertiser.startAdvertising(settings, data, mAdvertisingCallback); mAdvertiserStartCount = 0; } } + protected void stopAdvertising() { + if (mAdvertiser != null) { + mAdvertiser.stopAdvertising(mAdvertisingCallback); + } + } + /** - * Stops the advertiser and GATT server. This needs to be done to avoid leaks + * Notifies the characteristic change via {@link BluetoothGattServer} */ - protected void stop() { + protected void notifyCharacteristicChanged(BluetoothDevice device, + BluetoothGattCharacteristic characteristic, boolean confirm) { + if (mGattServer != null) { + mGattServer.notifyCharacteristicChanged(device, characteristic, confirm); + } + } + + @Override + public void onDestroy() { + // Stops the advertiser and GATT server. This needs to be done to avoid leaks if (mAdvertiser != null) { mAdvertiser.stopAdvertising(mAdvertisingCallback); mAdvertiser.cleanup(); @@ -193,73 +200,32 @@ public abstract class SimpleBleServer extends Service { mGattServer.cancelConnection(d); } } catch (UnsupportedOperationException e) { - Log.e(Utils.LOG_TAG, "Error getting connected devices", e); + Log.e(TAG, "Error getting connected devices", e); } finally { mGattServer.close(); } } - - mConnectionCallbacks.clear(); - } - - /** - * Notifies the characteristic change via {@link BluetoothGattServer} - */ - protected boolean notifyCharacteristicChanged(BluetoothDevice device, - BluetoothGattCharacteristic characteristic, boolean confirm) { - return (mGattServer != null) - && mGattServer.notifyCharacteristicChanged(device, characteristic, confirm); - } - - @Override - public void onDestroy() { - stop(); super.onDestroy(); } - public void registerConnectionCallback(ConnectionCallback callback) { - Log.d(Utils.LOG_TAG, "Adding connection listener"); - mConnectionCallbacks.add(callback); - } - - public void unregisterConnectionCallback(ConnectionCallback callback) { - mConnectionCallbacks.remove(callback); - } + // Delegate to subclass + protected void onAdvertiseStartSuccess() { } + protected void onAdvertiseStartFailure(int errorCode) { } + protected void onAdvertiseDeviceConnected(BluetoothDevice device) { } + protected void onAdvertiseDeviceDisconnected(BluetoothDevice device) { } /** * Triggered when this BleService receives a write request from a remote * device. Sub-classes should implement how to handle requests. */ - public abstract void onCharacteristicWrite(final BluetoothDevice device, int requestId, + protected abstract void onCharacteristicWrite(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value); /** * Triggered when this BleService receives a read request from a remote device. */ - public abstract void onCharacteristicRead(BluetoothDevice device, + protected abstract void onCharacteristicRead(BluetoothDevice device, int requestId, int offset, final BluetoothGattCharacteristic characteristic); - /** - * Callback that is notified when the status of the BLE server changes. - */ - public interface ConnectionCallback { - /** - * Called when the GATT server is started and BLE is successfully advertising. - */ - void onServerStarted(); - - /** - * Called when the BLE advertisement fails to start. - * - * @param errorCode Error code (see {@link AdvertiseCallback}#ADVERTISE_FAILED_* constants) - */ - void onServerStartFailed(int errorCode); - - /** - * Called when a device is connected. - * @param device {@link BluetoothDevice} that is connected - */ - void onDeviceConnected(BluetoothDevice device); - } } diff --git a/TrustAgent/src/com/android/car/trust/Utils.java b/TrustAgent/src/com/android/car/trust/Utils.java deleted file mode 100644 index 9f63cc4a22..0000000000 --- a/TrustAgent/src/com/android/car/trust/Utils.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2017 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 java.nio.ByteBuffer; - -public class Utils { - - public static final String LOG_TAG = "CarTrustAgentService"; - - public static byte[] getBytes(long l) { - ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); - buffer.putLong(0, l); - return buffer.array(); - } - - public static long getLong(byte[] bytes) { - ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); - buffer.put(bytes); - buffer.flip(); - return buffer.getLong(); - } -} |