blob: 46cad66431e26be9c9abb2a9456998159048cda2 [file] [log] [blame]
/*
* Copyright (C) 2021 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.bluetooth;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertisingSet;
import android.bluetooth.le.AdvertisingSetCallback;
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Context;
import android.os.ParcelUuid;
import android.util.IndentingPrintWriter;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
/**
* The FastPairAdvertiser is responsible for the BLE advertisement of either the model ID while
* in pairing mode or the stored account keys while not in pairing mode.
*
* Note that two different advertisers should be created and only one should be advertising at a
* time.
*/
class FastPairAdvertiser {
// Service ID assigned for FastPair.
public static final ParcelUuid FastPairServiceUuid = ParcelUuid
.fromString("0000FE2C-0000-1000-8000-00805f9b34fb");
private static final String TAG = FastPairAdvertiser.class.getSimpleName();
private static final boolean DBG = FastPairUtils.DBG;
private final Callbacks mCallbacks;
private final Context mContext;
private final byte[] mFastPairModelData;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothLeAdvertiser mBluetoothLeAdvertiser;
private AdvertisingSetParameters mAdvertisingSetParameters;
private AdvertiseData mData;
private boolean mAdvertising = false;
interface Callbacks {
/**
* Notify the Resolvable Private Address of the BLE advertiser.
*
* @param device The current LE address
*/
void onRpaUpdated(BluetoothDevice device);
}
FastPairAdvertiser(Context context, int modelId, Callbacks callbacks) {
mContext = context;
mCallbacks = callbacks;
ByteBuffer modelIdBytes = ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(
modelId);
mFastPairModelData = Arrays.copyOfRange(modelIdBytes.array(), 1, 4);
initializeBluetoothLeAdvertiser();
}
/**
* Advertise the model id when in pairing mode.
*/
void advertiseModelId() {
if (DBG) {
Log.d(TAG, "AdvertiseModelId");
}
mAdvertisingSetParameters = new AdvertisingSetParameters.Builder()
.setLegacyMode(true)
.setInterval(AdvertisingSetParameters.INTERVAL_LOW)
.setScannable(true)
.setConnectable(true)
.build();
mData = new AdvertiseData.Builder()
.addServiceUuid(FastPairServiceUuid)
.addServiceData(FastPairServiceUuid, mFastPairModelData)
.setIncludeTxPowerLevel(true)
.build();
startAdvertising();
}
/**
* Advertise the stored account keys while not in pairing mode
*/
void advertiseAccountKeys() {
if (DBG) {
Log.d(TAG, "AdvertiseAccountKeys");
}
mAdvertisingSetParameters = new AdvertisingSetParameters.Builder()
.setLegacyMode(true)
.setInterval(AdvertisingSetParameters.INTERVAL_MEDIUM)
.setScannable(true)
.setConnectable(true)
.build();
mData = new AdvertiseData.Builder()
.addServiceUuid(FastPairServiceUuid)
.addServiceData(FastPairServiceUuid,
FastPairUtils.getAccountKeyAdvertisement(mContext))
.setIncludeTxPowerLevel(true)
.build();
startAdvertising();
}
/**
* Stop advertising when it is time to shut down.
*/
void stopAdvertising() {
if (DBG) {
Log.d(TAG, "stoppingAdvertising");
}
if (mBluetoothLeAdvertiser == null) return;
mBluetoothLeAdvertiser.stopAdvertisingSet(mAdvertisingSetCallback);
}
/**
* Attempt to set mBluetoothLeAdvertiser from the BluetoothAdapter
*
* Returns
* true if mBluetoothLeAdvertiser is set
* false if mBluetoothLeAdvertiser is still null
*/
private boolean initializeBluetoothLeAdvertiser() {
if (mBluetoothLeAdvertiser != null) return true;
BluetoothManager bluetoothManager = mContext.getSystemService(BluetoothManager.class);
if (bluetoothManager == null) return false;
mBluetoothAdapter = bluetoothManager.getAdapter();
if (mBluetoothAdapter == null) return false;
mBluetoothLeAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
return (mBluetoothLeAdvertiser != null);
}
/**
* Acquire the LE advertiser from the Bluetooth adapter, and if available start the configured
* advertiser.
*/
private void startAdvertising() {
if (!initializeBluetoothLeAdvertiser()) return;
if (!mAdvertising) {
if (DBG) Log.d(TAG, "startingAdvertising");
mBluetoothLeAdvertiser.startAdvertisingSet(mAdvertisingSetParameters, mData, null, null,
null, mAdvertisingSetCallback);
}
}
/* Callback to handle changes in advertising. */
private AdvertisingSetCallback mAdvertisingSetCallback = new AdvertisingSetCallback() {
@Override
public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
int status) {
if (DBG) {
Log.d(TAG, "onAdvertisingSetStarted(): txPower:" + txPower + " , status: "
+ status);
}
mAdvertising = true;
if (advertisingSet == null) return;
advertisingSet.getOwnAddress();
}
@Override
public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
if (DBG) Log.d(TAG, "onAdvertisingSetStopped():");
mAdvertising = false;
}
@Override
public void onOwnAddressRead(AdvertisingSet advertisingSet, int addressType,
String address) {
if (DBG) Log.d(TAG, "onOwnAddressRead Type= " + addressType + " Address= " + address);
mCallbacks.onRpaUpdated(mBluetoothAdapter.getRemoteDevice(address));
}
};
public void dump(IndentingPrintWriter writer) {
if (mAdvertising) {
writer.println("Currently advertising : " + mData);
}
}
}