diff options
-rw-r--r-- | service/res/values/config.xml | 5 | ||||
-rw-r--r-- | service/src/com/android/car/BluetoothDeviceConnectionPolicy.java | 15 | ||||
-rw-r--r-- | service/src/com/android/car/FastPairProvider.java | 125 | ||||
-rw-r--r-- | tests/carservice_test/Android.mk | 3 | ||||
-rw-r--r-- | tests/carservice_test/AndroidManifest.xml | 3 | ||||
-rw-r--r-- | tests/carservice_test/src/com/android/car/FastPairProviderTest.java | 102 |
6 files changed, 252 insertions, 1 deletions
diff --git a/service/res/values/config.xml b/service/res/values/config.xml index 1b5430958c..5b8996cd92 100644 --- a/service/res/values/config.xml +++ b/service/res/values/config.xml @@ -125,4 +125,9 @@ strongly recommended that it be protected with the android.car.permission.STORAGE_MONITORING permission. --> <string name="intentReceiverForUnacceptableIoMetrics"></string> + + <!-- The Model ID to advertise Bluetooth Fast Pair connections with. Must be overlayed with + device specific model id. --> + <integer name="fastPairModelId">0x000000</integer> + </resources> diff --git a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java index 8b34ab7b75..e8fd37048b 100644 --- a/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java +++ b/service/src/com/android/car/BluetoothDeviceConnectionPolicy.java @@ -129,6 +129,9 @@ public class BluetoothDeviceConnectionPolicy { // Car Bluetooth Priority Settings Manager private final CarBluetoothService mCarBluetoothService; + // Fast Pair Provider to allow discovery of new phones + private final FastPairProvider mFastPairProvider; + // The Bluetooth profiles that the CarService will try to auto-connect on. private final List<Integer> mProfilesToConnect; private final List<Integer> mPrioritiesSupported; @@ -198,6 +201,7 @@ public class BluetoothDeviceConnectionPolicy { if (mBluetoothAdapter == null) { Log.w(TAG, "No Bluetooth Adapter Available"); } + mFastPairProvider = new FastPairProvider(mContext); } /** @@ -584,6 +588,8 @@ public class BluetoothDeviceConnectionPolicy { mCarCabinService.registerListener(mCabinEventListener); mCarSensorService.registerOrUpdateSensorListener( CarSensorManager.SENSOR_TYPE_IGNITION_STATE, 0, mCarSensorEventListener); + mCarSensorService.registerOrUpdateSensorListener( + CarSensorManager.SENSOR_TYPE_DRIVING_STATUS, 0, mCarSensorEventListener); mUserServiceHelper.registerServiceCallback(mServiceCallback); } @@ -640,6 +646,15 @@ public class BluetoothDeviceConnectionPolicy { if (event.intValues[0] == CarSensorEvent.IGNITION_STATE_START) { initiateConnection(); } + } else if (event.sensorType == CarSensorManager.SENSOR_TYPE_DRIVING_STATUS) { + if (DBG) { + Log.d(TAG, "Sensor value : " + event.intValues[0]); + } + if ((event.intValues[0] & CarSensorEvent.DRIVE_STATUS_NO_CONFIG) == CarSensorEvent.DRIVE_STATUS_NO_CONFIG) { + mFastPairProvider.stopAdvertising(); + } else { + mFastPairProvider.startAdvertising(); + } } } } diff --git a/service/src/com/android/car/FastPairProvider.java b/service/src/com/android/car/FastPairProvider.java new file mode 100644 index 0000000000..ff5973236f --- /dev/null +++ b/service/src/com/android/car/FastPairProvider.java @@ -0,0 +1,125 @@ +/* + * 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; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; + +import android.bluetooth.le.AdvertiseCallback; +import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.AdvertiseSettings; +import android.bluetooth.le.BluetoothLeAdvertiser; +import android.content.Context; +import android.content.res.Resources; +import android.os.ParcelUuid; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; + +/** + * An advertiser for the Bluetooth LE based Fast Pair service. + * FastPairProvider enables easy Bluetooth pairing between a peripheral and a phone participating in + * the Fast Pair Seeker role. When the seeker finds a compatible peripheral a notification prompts + * the user to begin pairing if desired. A peripheral should call startAdvertising when it is + * appropriate to pair, and stopAdvertising when pairing is complete or it is no longer appropriate + * to pair. + */ +class FastPairProvider { + + private static final String TAG = "FastPairProvider"; + private static final boolean DBG = Utils.DBG; + + // Service ID assigned for FastPair. + private static final ParcelUuid FastPairServiceUuid = ParcelUuid + .fromString("0000FE2C-0000-1000-8000-00805f9b34fb"); + + private AdvertiseSettings mSettings; + private AdvertiseData mData; + private BluetoothLeAdvertiser mBluetoothLeAdvertiser; + private AdvertiseCallback mAdvertiseCallback; + + + FastPairProvider(Context context) { + Resources res = context.getResources(); + int modelId = res.getInteger(R.integer.fastPairModelId); + if (modelId == 0) { + Log.w(TAG, "Model ID undefined, disabling"); + return; + } + + BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService( + Context.BLUETOOTH_SERVICE); + if (bluetoothManager != null) { + BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); + if (bluetoothAdapter != null) { + mBluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser(); + } + } + + AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder(); + settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY); + settingsBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH); + settingsBuilder.setConnectable(true); + settingsBuilder.setTimeout(0); + mSettings = settingsBuilder.build(); + + AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder(); + ByteBuffer modelIdBytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt( + modelId); + byte[] fastPairServiceData = Arrays.copyOfRange(modelIdBytes.array(), 0, 3); + dataBuilder.addServiceData(FastPairServiceUuid, fastPairServiceData); + dataBuilder.setIncludeTxPowerLevel(true).build(); + mData = dataBuilder.build(); + + mAdvertiseCallback = new FastPairAdvertiseCallback(); + } + + /* register the BLE advertisement using the model id provided during construction */ + boolean startAdvertising() { + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.startAdvertising(mSettings, mData, mAdvertiseCallback); + return true; + } + return false; + } + + /* unregister the BLE advertisement. */ + boolean stopAdvertising() { + if (mBluetoothLeAdvertiser != null) { + mBluetoothLeAdvertiser.stopAdvertising(mAdvertiseCallback); + return true; + } + return false; + } + + /* Callback to handle status when advertising starts. */ + private class FastPairAdvertiseCallback extends AdvertiseCallback { + @Override + public void onStartFailure(int errorCode) { + super.onStartFailure(errorCode); + if (DBG) Log.d(TAG, "Advertising failed"); + } + + @Override + public void onStartSuccess(AdvertiseSettings settingsInEffect) { + super.onStartSuccess(settingsInEffect); + if (DBG) Log.d(TAG, "Advertising successfully started"); + } + } +} diff --git a/tests/carservice_test/Android.mk b/tests/carservice_test/Android.mk index d544fd5663..aac6aa451b 100644 --- a/tests/carservice_test/Android.mk +++ b/tests/carservice_test/Android.mk @@ -40,10 +40,13 @@ LOCAL_STATIC_JAVA_LIBRARIES := junit LOCAL_STATIC_JAVA_LIBRARIES += car-service-lib-for-test \ vehicle-hal-support-lib \ car-systemtest \ + mockito-target-inline \ android-support-test \ android.hardware.automotive.vehicle-V2.0-java \ com.android.car.test.utils LOCAL_JAVA_LIBRARIES := android.car android.test.runner android.test.base +LOCAL_JNI_SHARED_LIBRARIES := libdexmakerjvmtiagent + include $(BUILD_PACKAGE) diff --git a/tests/carservice_test/AndroidManifest.xml b/tests/carservice_test/AndroidManifest.xml index de7276beed..3efe24c5ef 100644 --- a/tests/carservice_test/AndroidManifest.xml +++ b/tests/carservice_test/AndroidManifest.xml @@ -31,7 +31,8 @@ android:targetPackage="com.android.car.test" android:label="Tests for Car APIs"/> - <application android:label="CarServiceTest" android:name=".CarServiceTestApp"> + <application android:label="CarServiceTest" android:name=".CarServiceTestApp" + android:debuggable="true"> <uses-library android:name="android.test.runner" /> <service android:name="com.android.car.TestAppBlockingPolicyService" android:permission="android.car.permission.CONTROL_APP_BLOCKING"> diff --git a/tests/carservice_test/src/com/android/car/FastPairProviderTest.java b/tests/carservice_test/src/com/android/car/FastPairProviderTest.java new file mode 100644 index 0000000000..02c4cf1cae --- /dev/null +++ b/tests/carservice_test/src/com/android/car/FastPairProviderTest.java @@ -0,0 +1,102 @@ +/* + * 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; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.bluetooth.le.AdvertiseData; +import android.bluetooth.le.BluetoothLeAdvertiser; +import android.content.Context; +import android.content.res.Resources; +import android.os.ParcelUuid; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class FastPairProviderTest { + // Service ID assigned for FastPair. + private static final ParcelUuid FastPairServiceUuid = ParcelUuid + .fromString("0000FE2C-0000-1000-8000-00805f9b34fb"); + + @Mock + private BluetoothLeAdvertiser mBluetoothLeAdvertiser; + @Mock + private BluetoothManager mMockBluetoothManager; + @Mock + private BluetoothAdapter mMockBluetoothAdapter; + @Mock + private Context mMockContext; + @Mock + Resources mMockResources; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mMockContext.getSystemService(Context.BLUETOOTH_SERVICE)).thenReturn( + mMockBluetoothManager); + when(mMockBluetoothManager.getAdapter()).thenReturn(mMockBluetoothAdapter); + when(mMockContext.getResources()).thenReturn(mMockResources); + when(mMockResources.getInteger(R.integer.fastPairModelId)).thenReturn(0); + when(mMockBluetoothAdapter.getBluetoothLeAdvertiser()).thenReturn(mBluetoothLeAdvertiser); + } + + /** + * Verify that when a model id is set it gets serialized correctly. + */ + @Test + public void enabledViaModelIdTest() { + int modelId = 0xABCDEF; + byte[] modelIdBytes = new byte[]{(byte) 0xEF, (byte) 0xCD, (byte) 0xAB}; + when(mMockResources.getInteger(R.integer.fastPairModelId)).thenReturn(modelId); + ArgumentCaptor<AdvertiseData> advertiseDataCaptor = ArgumentCaptor.forClass( + AdvertiseData.class); + + FastPairProvider fastPairProvider = new FastPairProvider(mMockContext); + fastPairProvider.startAdvertising(); + + verify(mBluetoothLeAdvertiser).startAdvertising(any(), advertiseDataCaptor.capture(), + any()); + Assert.assertTrue(Arrays.equals(modelIdBytes, + advertiseDataCaptor.getValue().getServiceData().get(FastPairServiceUuid))); + } + + /** + * Verify that when the model id is 0 Fast Pair is disabled. + */ + @Test + public void disabledViaModelIdTest() { + FastPairProvider fastPairProvider = new FastPairProvider(mMockContext); + fastPairProvider.startAdvertising(); + verify(mBluetoothLeAdvertiser, never()).startAdvertising(any(), any(), any()); + } +} |