Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fde3f8de authored by Jakub Pawlowski's avatar Jakub Pawlowski
Browse files

Bluetooth 5 AdvertisingSet implementation (2/4)

This patch wires up fist methods of AdvertisingSet, making it possible
to start advertising and stop advertising. It also replaces legacy
implemementation with calls to new implementation.

Bug: 30622771
Test: sl4a ConcurrentBleAdvertisingTest
Change-Id: I35e7db5df1945d101402299f78dd541e346d548c
parent f4e995fd
Loading
Loading
Loading
Loading
+174 −105
Original line number Diff line number Diff line
@@ -142,9 +142,6 @@ static jmethodID method_onConfigureMTU;
static jmethodID method_onScanFilterConfig;
static jmethodID method_onScanFilterParamsConfigured;
static jmethodID method_onScanFilterEnableDisabled;
static jmethodID method_onAdvertiserRegistered;
static jmethodID method_onAdvertiserStarted;
static jmethodID method_onMultiAdvEnable;
static jmethodID method_onClientCongestion;
static jmethodID method_onBatchScanStorageConfigured;
static jmethodID method_onBatchScanStartStopped;
@@ -175,12 +172,19 @@ static jmethodID method_onNotificationSent;
static jmethodID method_onServerCongestion;
static jmethodID method_onServerMtuChanged;

/**
 * Advertiser callback methods
 */
static jmethodID method_onAdvertisingSetStarted;
static jmethodID method_onAdvertisingSetEnabled;

/**
 * Static variables
 */

static const btgatt_interface_t* sGattIf = NULL;
static jobject mCallbacksObj = NULL;
static jobject mAdvertiseCallbacksObj = NULL;

/**
 * BTA client callbacks
@@ -523,32 +527,6 @@ static const btgatt_client_callbacks_t sGattClientCallbacks = {
    NULL  /* services_added_cb */
};

/**
 * Advertiser callbacks
 */
void ble_advertiser_register_cb(bt_uuid_t uuid, uint8_t advertiser_id,
                                uint8_t status) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAdvertiserRegistered,
                               status, advertiser_id, UUID_PARAMS(&uuid));
}

void ble_advertiser_enable_cb(bool enable, uint8_t advertiser_id,
                              uint8_t status) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onMultiAdvEnable, status,
                               advertiser_id, enable);
}

void ble_advertiser_start_cb(uint8_t advertiser_id, uint8_t status) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
  sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onAdvertiserStarted,
                               status, advertiser_id);
}

/**
 * BTA server callbacks
 */
@@ -777,12 +755,6 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
      env->GetMethodID(clazz, "onScanFilterParamsConfigured", "(IIII)V");
  method_onScanFilterEnableDisabled =
      env->GetMethodID(clazz, "onScanFilterEnableDisabled", "(III)V");
  method_onAdvertiserRegistered =
      env->GetMethodID(clazz, "onAdvertiserRegistered", "(IIJJ)V");
  method_onAdvertiserStarted =
      env->GetMethodID(clazz, "onAdvertiserStarted", "(II)V");
  method_onMultiAdvEnable =
      env->GetMethodID(clazz, "onAdvertiseInstanceEnabled", "(IIZ)V");
  method_onClientCongestion =
      env->GetMethodID(clazz, "onClientCongestion", "(IZ)V");
  method_onBatchScanStorageConfigured =
@@ -1349,69 +1321,6 @@ static void gattConnectionParameterUpdateNative(JNIEnv* env, jobject object,
                                         latency, timeout);
}

static void registerAdvertiserNative(JNIEnv* env, jobject object,
                                     jlong app_uuid_lsb, jlong app_uuid_msb) {
  if (!sGattIf) return;

  bt_uuid_t uuid;
  set_uuid(uuid.uu, app_uuid_msb, app_uuid_lsb);
  sGattIf->advertiser->RegisterAdvertiser(
      base::Bind(&ble_advertiser_register_cb, uuid));
}

static void startAdvertiserNative(
    JNIEnv* env, jobject object, jint advertiser_id,
    jint advertising_event_properties, jint min_interval, jint max_interval,
    jint chnl_map, jint tx_power, jint primary_advertising_phy,
    jint secondary_advertising_phy, jint scan_request_notification_enable,
    jbyteArray adv_data, jbyteArray scan_resp, jint timeout_s) {
  if (!sGattIf) return;

  AdvertiseParameters params;
  params.advertising_event_properties = advertising_event_properties;
  params.min_interval = min_interval;
  params.max_interval = max_interval;
  params.channel_map = chnl_map;
  params.tx_power = tx_power;
  params.primary_advertising_phy = primary_advertising_phy;
  params.secondary_advertising_phy = secondary_advertising_phy;
  params.scan_request_notification_enable = scan_request_notification_enable;

  jbyte* adv_data_data = env->GetByteArrayElements(adv_data, NULL);
  uint16_t adv_data_len = (uint16_t)env->GetArrayLength(adv_data);
  std::vector<uint8_t> data_vec(adv_data_data, adv_data_data + adv_data_len);
  env->ReleaseByteArrayElements(adv_data, adv_data_data, JNI_ABORT);

  jbyte* scan_resp_data = env->GetByteArrayElements(scan_resp, NULL);
  uint16_t scan_resp_len = (uint16_t)env->GetArrayLength(scan_resp);
  std::vector<uint8_t> scan_resp_vec(scan_resp_data,
                                     scan_resp_data + scan_resp_len);
  env->ReleaseByteArrayElements(scan_resp, scan_resp_data, JNI_ABORT);

  sGattIf->advertiser->StartAdvertising(
      advertiser_id, base::Bind(&ble_advertiser_start_cb, advertiser_id),
      params, data_vec, scan_resp_vec, timeout_s,
      base::Bind(&ble_advertiser_enable_cb, false, advertiser_id));
}

static void unregisterAdvertiserNative(JNIEnv* env, jobject object,
                                       jint advertiser_id) {
  if (!sGattIf) return;

  sGattIf->advertiser->Unregister(advertiser_id);
}

static void gattClientEnableAdvNative(JNIEnv* env, jobject object,
                                      jint advertiser_id, jboolean enable,
                                      jint timeout_s) {
  if (!sGattIf) return;

  sGattIf->advertiser->Enable(
      advertiser_id, enable,
      base::Bind(&ble_advertiser_enable_cb, enable, advertiser_id), timeout_s,
      base::Bind(&ble_advertiser_enable_cb, false, advertiser_id));
}

void batchscan_cfg_storage_cb(uint8_t client_if, uint8_t status) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
@@ -1634,6 +1543,163 @@ static void gattServerSendResponseNative(JNIEnv* env, jobject object,
  sGattIf->server->send_response(conn_id, trans_id, status, &response);
}

static void advertiseClassInitNative(JNIEnv* env, jclass clazz) {
  method_onAdvertisingSetStarted =
      env->GetMethodID(clazz, "onAdvertisingSetStarted", "(III)V");
  method_onAdvertisingSetEnabled =
      env->GetMethodID(clazz, "onAdvertisingSetEnabled", "(IZI)V");
}

static void advertiseInitializeNative(JNIEnv* env, jobject object) {
  if (mAdvertiseCallbacksObj != NULL) {
    ALOGW("Cleaning up Advertise callback object");
    env->DeleteGlobalRef(mAdvertiseCallbacksObj);
    mAdvertiseCallbacksObj = NULL;
  }

  mAdvertiseCallbacksObj = env->NewGlobalRef(object);
}

static void advertiseCleanupNative(JNIEnv* env, jobject object) {
  if (mAdvertiseCallbacksObj != NULL) {
    env->DeleteGlobalRef(mAdvertiseCallbacksObj);
    mAdvertiseCallbacksObj = NULL;
  }
}

static AdvertiseParameters parseParams(JNIEnv* env, jobject i, bool isScannable,
                                       int* timeout) {
  AdvertiseParameters p;

  jclass clazz = env->GetObjectClass(i);
  jmethodID methodId;

  methodId = env->GetMethodID(clazz, "isConnectable", "()Z");
  jboolean isConnectable = env->CallBooleanMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "isLegacy", "()Z");
  jboolean isLegacy = env->CallBooleanMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "isAnonymous", "()Z");
  jboolean isAnonymous = env->CallBooleanMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "includeTxPower", "()Z");
  jboolean includeTxPower = env->CallBooleanMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "getPrimaryPhy", "()I");
  uint8_t primaryPhy = env->CallIntMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "getSecondaryPhy", "()I");
  uint8_t secondaryPhy = env->CallIntMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "getInterval", "()I");
  uint32_t interval = env->CallIntMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "getTxPowerLevel", "()I");
  int8_t txPowerLevel = env->CallIntMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "getTimeout", "()I");
  *timeout = env->CallIntMethod(i, methodId);

  uint16_t props = 0;
  if (isConnectable) props |= 0x01;
  if (isScannable || (isLegacy && isConnectable)) props |= 0x02;
  if (isLegacy) props |= 0x10;
  if (isAnonymous) props |= 0x20;
  if (includeTxPower) props |= 0x40;

  p.advertising_event_properties = props;
  p.min_interval = interval;
  p.max_interval = interval + 50;
  p.channel_map = 0x07; /* all channels */
  p.tx_power = txPowerLevel;
  p.primary_advertising_phy = primaryPhy;
  p.secondary_advertising_phy = secondaryPhy;
  p.scan_request_notification_enable = false;
  return p;
}

static PeriodicAdvertisingParameters parsePeriodicParams(JNIEnv* env,
                                                         jobject i) {
  PeriodicAdvertisingParameters p;

  if (i == NULL) {
    p.enable = false;
    return p;
  }

  jclass clazz = env->GetObjectClass(i);
  jmethodID methodId;

  methodId = env->GetMethodID(clazz, "getEnable", "()B");
  jboolean enable = env->CallBooleanMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "getIncludeTxPower", "()B");
  jboolean includeTxPower = env->CallBooleanMethod(i, methodId);
  methodId = env->GetMethodID(clazz, "getInterval", "()I");
  jboolean interval = env->CallBooleanMethod(i, methodId);

  p.enable = enable;
  p.min_interval = interval;
  p.max_interval = interval + 16; /* 20ms difference betwen min and max */
  uint16_t props = 0;
  if (includeTxPower) props |= 0x40;
  p.periodic_advertising_properties = props;
  return p;
}

static void ble_advertising_set_started_cb(int reg_id, uint8_t advertiser_id,
                                           uint8_t status) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
  sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj,
                               method_onAdvertisingSetStarted, reg_id,
                               advertiser_id, status);
}

static void ble_advertising_set_timeout_cb(uint8_t advertiser_id,
                                           uint8_t status) {
  CallbackEnv sCallbackEnv(__func__);
  if (!sCallbackEnv.valid()) return;
  sCallbackEnv->CallVoidMethod(mAdvertiseCallbacksObj,
                               method_onAdvertisingSetEnabled, advertiser_id,
                               false, status);
}

static void startAdvertisingSetNative(JNIEnv* env, jobject object,
                                      jobject parameters, jbyteArray adv_data,
                                      jbyteArray scan_resp,
                                      jobject periodic_parameters,
                                      jbyteArray periodic_data, jint reg_id) {
  if (!sGattIf) return;

  jbyte* scan_resp_data = env->GetByteArrayElements(scan_resp, NULL);
  uint16_t scan_resp_len = (uint16_t)env->GetArrayLength(scan_resp);
  std::vector<uint8_t> scan_resp_vec(scan_resp_data,
                                     scan_resp_data + scan_resp_len);
  env->ReleaseByteArrayElements(scan_resp, scan_resp_data, JNI_ABORT);

  int timeout;
  AdvertiseParameters params =
      parseParams(env, parameters, (scan_resp_len != 0), &timeout);
  PeriodicAdvertisingParameters periodicParams =
      parsePeriodicParams(env, periodic_parameters);

  jbyte* adv_data_data = env->GetByteArrayElements(adv_data, NULL);
  uint16_t adv_data_len = (uint16_t)env->GetArrayLength(adv_data);
  std::vector<uint8_t> data_vec(adv_data_data, adv_data_data + adv_data_len);
  env->ReleaseByteArrayElements(adv_data, adv_data_data, JNI_ABORT);

  jbyte* periodic_data_data = env->GetByteArrayElements(periodic_data, NULL);
  uint16_t periodic_data_len = (uint16_t)env->GetArrayLength(periodic_data);
  std::vector<uint8_t> periodic_data_vec(
      periodic_data_data, periodic_data_data + periodic_data_len);
  env->ReleaseByteArrayElements(periodic_data, periodic_data_data, JNI_ABORT);

  sGattIf->advertiser->StartAdvertisingSet(
      base::Bind(&ble_advertising_set_started_cb, reg_id), params, data_vec,
      scan_resp_vec, periodicParams, periodic_data_vec, timeout,
      base::Bind(ble_advertising_set_timeout_cb));
}

static void stopAdvertisingSetNative(JNIEnv* env, jobject object,
                                     jint advertiser_id) {
  if (!sGattIf) return;

  sGattIf->advertiser->Unregister(advertiser_id);
}

static void gattTestNative(JNIEnv* env, jobject object, jint command,
                           jlong uuid1_lsb, jlong uuid1_msb, jstring bda1,
                           jint p1, jint p2, jint p3, jint p4, jint p5) {
@@ -1662,11 +1728,14 @@ static void gattTestNative(JNIEnv* env, jobject object, jint command,

// JNI functions defined in AdvertiseManager class.
static JNINativeMethod sAdvertiseMethods[] = {
    {"registerAdvertiserNative", "(JJ)V", (void*)registerAdvertiserNative},
    {"unregisterAdvertiserNative", "(I)V", (void*)unregisterAdvertiserNative},
    {"gattClientEnableAdvNative", "(IZI)V", (void*)gattClientEnableAdvNative},
    {"startAdvertiserNative", "(IIIIIIIII[B[BI)V",
     (void*)startAdvertiserNative},
    {"classInitNative", "()V", (void*)advertiseClassInitNative},
    {"initializeNative", "()V", (void*)advertiseInitializeNative},
    {"cleanupNative", "()V", (void*)advertiseCleanupNative},
    {"startAdvertisingSetNative",
     "(Landroid/bluetooth/le/AdvertisingSetParameters;[B[BLandroid/bluetooth/"
     "le/PeriodicAdvertisingParameters;[BI)V",
     (void*)startAdvertisingSetNative},
    {"stopAdvertisingSetNative", "(I)V", (void*)stopAdvertisingSetNative},
};

// JNI functions defined in ScanManager class.
@@ -1772,8 +1841,8 @@ int register_com_android_bluetooth_gatt(JNIEnv* env) {
      env, "com/android/bluetooth/gatt/ScanManager$ScanNative", sScanMethods,
      NELEM(sScanMethods));
  register_success &= jniRegisterNativeMethods(
      env, "com/android/bluetooth/gatt/AdvertiseManager$AdvertiseNative",
      sAdvertiseMethods, NELEM(sAdvertiseMethods));
      env, "com/android/bluetooth/gatt/AdvertiseManager", sAdvertiseMethods,
      NELEM(sAdvertiseMethods));
  return register_success &
         jniRegisterNativeMethods(env, "com/android/bluetooth/gatt/GattService",
                                  sMethods, NELEM(sMethods));
+0 −76
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.bluetooth.gatt;

import android.annotation.Nullable;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;

import java.util.Objects;

/**
 * Helper class that represents a client for Bluetooth LE advertise operations.
 *
 * @hide
 */
class AdvertiseClient {
    int advertiserId;
    // Associated application died.
    boolean appDied;
    AdvertiseSettings settings;
    AdvertiseData advertiseData;
    @Nullable
    AdvertiseData scanResponse;

    /**
     * @param advertiserId - Identifier of the advertiser.
     */
    AdvertiseClient(int advertiserId) {
        this.advertiserId = advertiserId;
    }

    /**
     * @param advertiserId - Identifier of the advertiser.
     * @param settings - Settings for the advertising.
     * @param advertiseData - Advertise data broadcasted over the air.
     * @param scanResponse - Response of scan request, could be null.
     */
    AdvertiseClient(int advertiserId, AdvertiseSettings settings, AdvertiseData advertiseData,
            AdvertiseData scanResponse) {
        this.advertiserId = advertiserId;
        this.settings = settings;
        this.advertiseData = advertiseData;
        this.scanResponse = scanResponse;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        AdvertiseClient other = (AdvertiseClient) obj;
        return advertiserId == other.advertiserId;
    }

    @Override
    public int hashCode() {
        return Objects.hash(advertiserId);
    }
}
+0 −61
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ package com.android.bluetooth.gatt;

import android.bluetooth.BluetoothUuid;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.os.ParcelUuid;
import android.util.Log;
import com.android.bluetooth.Utils;
@@ -28,23 +27,6 @@ class AdvertiseHelper {

  private static final String TAG = "AdvertiseHelper";

  // Advertise interval for different modes.
  private static final int ADVERTISING_INTERVAL_HIGH_MILLS = 1000;
  private static final int ADVERTISING_INTERVAL_MEDIUM_MILLS = 250;
  private static final int ADVERTISING_INTERVAL_LOW_MILLS = 100;

  private static final int ADVERTISING_TX_POWER_MIN = 0;
  private static final int ADVERTISING_TX_POWER_LOW = 1;
  private static final int ADVERTISING_TX_POWER_MID = 2;
  private static final int ADVERTISING_TX_POWER_UPPER = 3;
  // Note this is not exposed to the Java API.
  private static final int ADVERTISING_TX_POWER_MAX = 4;

  // Note we don't expose connectable directed advertising to API.
  private static final int ADVERTISING_EVENT_TYPE_LEGACY_CONNECTABLE = 0x13;
  private static final int ADVERTISING_EVENT_TYPE_LEGACY_SCANNABLE = 0x12;
  private static final int ADVERTISING_EVENT_TYPE_LEGACY_NON_CONNECTABLE = 0x10;

  private static final int DEVICE_NAME_MAX = 18;

  private static final int COMPLETE_LIST_16_BIT_SERVICE_UUIDS = 0X03;
@@ -184,47 +166,4 @@ class AdvertiseHelper {

    return ret.toByteArray();
  }

  // Convert settings tx power level to stack tx power level.
  public static int getTxPowerLevel(AdvertiseSettings settings) {
    switch (settings.getTxPowerLevel()) {
      case AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW:
        return ADVERTISING_TX_POWER_MIN;
      case AdvertiseSettings.ADVERTISE_TX_POWER_LOW:
        return ADVERTISING_TX_POWER_LOW;
      case AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM:
        return ADVERTISING_TX_POWER_MID;
      case AdvertiseSettings.ADVERTISE_TX_POWER_HIGH:
        return ADVERTISING_TX_POWER_UPPER;
      default:
        // Shouldn't happen, just in case.
        return ADVERTISING_TX_POWER_MID;
    }
  }

  // Convert advertising event type to stack values.
  public static int getAdvertisingEventProperties(AdvertiseClient client) {
    AdvertiseSettings settings = client.settings;
    if (settings.isConnectable()) {
      return ADVERTISING_EVENT_TYPE_LEGACY_CONNECTABLE;
    }
    return client.scanResponse == null
        ? ADVERTISING_EVENT_TYPE_LEGACY_NON_CONNECTABLE
        : ADVERTISING_EVENT_TYPE_LEGACY_SCANNABLE;
  }

  // Convert advertising milliseconds to advertising units(one unit is 0.625 millisecond).
  public static long getAdvertisingIntervalUnit(AdvertiseSettings settings) {
    switch (settings.getMode()) {
      case AdvertiseSettings.ADVERTISE_MODE_LOW_POWER:
        return Utils.millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
      case AdvertiseSettings.ADVERTISE_MODE_BALANCED:
        return Utils.millsToUnit(ADVERTISING_INTERVAL_MEDIUM_MILLS);
      case AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY:
        return Utils.millsToUnit(ADVERTISING_INTERVAL_LOW_MILLS);
      default:
        // Shouldn't happen, just in case.
        return Utils.millsToUnit(ADVERTISING_INTERVAL_HIGH_MILLS);
    }
  }
}
+129 −173

File changed.

Preview size limit exceeded, changes collapsed.

+18 −172

File changed.

Preview size limit exceeded, changes collapsed.