aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--service/res/values/config.xml5
-rw-r--r--service/src/com/android/car/BluetoothDeviceConnectionPolicy.java15
-rw-r--r--service/src/com/android/car/FastPairProvider.java125
-rw-r--r--tests/carservice_test/Android.mk3
-rw-r--r--tests/carservice_test/AndroidManifest.xml3
-rw-r--r--tests/carservice_test/src/com/android/car/FastPairProviderTest.java102
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());
+ }
+}