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

Commit 6d8739c4 authored by shihchienc's avatar shihchienc
Browse files

[LE Audio] Deregister notif when ccc fail of CSIS

Currently when ccc failed for csis, we did not deregister
as what LEA client did, provide this logic. Also, improve
the logging for ccc registration to better know which
device is done.

Bug: 298599400
Test: manual
Test: atest -c bluetooth_le_audio_client_test bluetooth_vc_test bluetooth_csis_test
Change-Id: I4aa1ac7d1593ea85b6e2265590fe2f727c88373f
parent 7f7d8d59
Loading
Loading
Loading
Loading
+52 −25
Original line number Diff line number Diff line
@@ -904,8 +904,6 @@ class CsisClientImpl : public CsisClient {

  void OnGattWriteCcc(uint16_t conn_id, tGATT_STATUS status, uint16_t handle,
                      void* user_data) {
    LOG(INFO) << __func__ << " handle=" << loghex(handle);

    auto device = FindDeviceByConnId(conn_id);
    if (device == nullptr) {
      LOG(INFO) << __func__ << " unknown conn_id=" << loghex(conn_id);
@@ -917,6 +915,29 @@ class CsisClientImpl : public CsisClient {
      LOG_INFO("Database out of sync for %s",
               ADDRESS_TO_LOGGABLE_CSTR(device->addr));
      ClearDeviceInformationAndStartSearch(device);
      return;
    }

    if (status == GATT_SUCCESS) {
      LOG_INFO("Successfully registered on ccc: 0x%04x, device: %s", handle,
               ADDRESS_TO_LOGGABLE_CSTR(device->addr));
      return;
    }

    LOG_ERROR(
        "Failed to register for indications: 0x%04x, device: %s, status: "
        "0x%02x",
        handle, ADDRESS_TO_LOGGABLE_CSTR(device->addr), status);

    auto val_handle = device->FindValueHandleByCccHandle(handle);
    if (!val_handle) {
      LOG_ERROR("Unknown ccc handle: 0x%04x, device: %s", handle,
                ADDRESS_TO_LOGGABLE_CSTR(device->addr));
      return;
    }

    if (val_handle != GAP_INVALID_HANDLE) {
      BTA_GATTC_DeregisterForNotifications(gatt_if_, device->addr, val_handle);
    }
  }

@@ -1637,9 +1658,9 @@ class CsisClientImpl : public CsisClient {
                          const gatt::Service* service,
                          const bluetooth::Uuid& context_uuid,
                          bool is_last_instance) {
    DLOG(INFO) << __func__ << " service handle: " << loghex(service->handle)
               << " end handle: " << loghex(service->end_handle)
               << " uuid: " << context_uuid;
    LOG_DEBUG("service handle: 0x%04x, end handle: 0x%04x, uuid: %s",
              service->handle, service->end_handle,
              context_uuid.ToString().c_str());

    auto csis_inst = std::make_shared<CsisInstance>(
        (uint16_t)service->handle, (uint16_t)service->end_handle, context_uuid);
@@ -1649,6 +1670,8 @@ class CsisClientImpl : public CsisClient {
    if (group_id != bluetooth::groups::kGroupUnknown)
      csis_inst->SetGroupId(group_id);

    device->SetCsisInstance(csis_inst->svc_data.start_handle, csis_inst);

    /* Initially validate and store GATT service discovery data */
    for (const gatt::Characteristic& charac : service->characteristics) {
      if (charac.uuid == kCsisLockUuid) {
@@ -1656,8 +1679,8 @@ class CsisClientImpl : public CsisClient {
        uint16_t ccc_handle =
            FindCccHandle(device->conn_id, charac.value_handle);
        if (ccc_handle == GAP_INVALID_HANDLE) {
          DLOG(ERROR) << __func__
                      << ": no HAS Active Preset CCC descriptor found!";
          LOG_ERROR("no HAS Active Preset CCC descriptor found!");
          device->RemoveCsisInstance(group_id);
          return false;
        }
        csis_inst->svc_data.lock_handle.val_hdl = charac.value_handle;
@@ -1666,15 +1689,17 @@ class CsisClientImpl : public CsisClient {
        SubscribeForNotifications(device->conn_id, device->addr,
                                  charac.value_handle, ccc_handle);

        DLOG(INFO) << __func__ << " Lock UUID found handle: "
                   << loghex(csis_inst->svc_data.lock_handle.val_hdl)
                   << " ccc handle: "
                   << loghex(csis_inst->svc_data.lock_handle.ccc_hdl);
        LOG_DEBUG(
            "Lock UUID found handle: 0x%04x, ccc handle: 0x%04x, device: %s",
            csis_inst->svc_data.lock_handle.val_hdl,
            csis_inst->svc_data.lock_handle.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(device->addr));
      } else if (charac.uuid == kCsisRankUuid) {
        csis_inst->svc_data.rank_handle = charac.value_handle;

        DLOG(INFO) << __func__ << " Rank UUID found handle: "
                   << loghex(csis_inst->svc_data.rank_handle);
        LOG_DEBUG("Rank UUID found handle: 0x%04x, device: %s",
                  csis_inst->svc_data.rank_handle,
                  ADDRESS_TO_LOGGABLE_CSTR(device->addr));
      } else if (charac.uuid == kCsisSirkUuid) {
        /* Find the optional CCC descriptor */
        uint16_t ccc_handle =
@@ -1686,10 +1711,11 @@ class CsisClientImpl : public CsisClient {
          SubscribeForNotifications(device->conn_id, device->addr,
                                    charac.value_handle, ccc_handle);

        DLOG(INFO) << __func__ << " SIRK UUID found handle: "
                   << loghex(csis_inst->svc_data.sirk_handle.val_hdl)
                   << " ccc handle: "
                   << loghex(csis_inst->svc_data.sirk_handle.ccc_hdl);
        LOG_DEBUG(
            "SIRK UUID found handle: 0x%04x, ccc handle: 0x%04x, device: %s",
            csis_inst->svc_data.sirk_handle.val_hdl,
            csis_inst->svc_data.sirk_handle.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(device->addr));
      } else if (charac.uuid == kCsisSizeUuid) {
        /* Find the optional CCC descriptor */
        uint16_t ccc_handle =
@@ -1701,10 +1727,11 @@ class CsisClientImpl : public CsisClient {
          SubscribeForNotifications(device->conn_id, device->addr,
                                    charac.value_handle, ccc_handle);

        DLOG(INFO) << __func__ << " Size UUID found handle: "
                   << loghex(csis_inst->svc_data.size_handle.val_hdl)
                   << " ccc handle: "
                   << loghex(csis_inst->svc_data.size_handle.ccc_hdl);
        LOG_DEBUG(
            "Size UUID found handle: 0x%04x, ccc handle: 0x%04x, device: %s",
            csis_inst->svc_data.size_handle.val_hdl,
            csis_inst->svc_data.size_handle.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(device->addr));
      }
    }

@@ -1714,9 +1741,9 @@ class CsisClientImpl : public CsisClient {
    if (csis_inst->svc_data.sirk_handle.val_hdl == GAP_INVALID_HANDLE) {
      /* We have some characteristics but all dependencies are not satisfied */
      LOG(ERROR) << __func__ << " Service has a broken structure.";
      device->RemoveCsisInstance(group_id);
      return false;
    }
    device->SetCsisInstance(csis_inst->svc_data.start_handle, csis_inst);

    bool notify_after_sirk_read = false;
    bool notify_after_lock_read = false;
@@ -2118,10 +2145,10 @@ class CsisClientImpl : public CsisClient {
    UINT16_TO_STREAM(value_ptr, GATT_CHAR_CLIENT_CONFIG_NOTIFICATION);
    BtaGattQueue::WriteDescriptor(
        conn_id, ccc_handle, std::move(value), GATT_WRITE,
        [](uint16_t conn_id, tGATT_STATUS status, uint16_t value_handle,
           uint16_t len, const uint8_t* value, void* user_data) {
        [](uint16_t conn_id, tGATT_STATUS status, uint16_t handle, uint16_t len,
           const uint8_t* value, void* user_data) {
          if (instance)
            instance->OnGattWriteCcc(conn_id, status, value_handle, user_data);
            instance->OnGattWriteCcc(conn_id, status, handle, user_data);
        },
        nullptr);
  }
+54 −0
Original line number Diff line number Diff line
@@ -534,6 +534,42 @@ class CsisClientTest : public ::testing::Test {
    TestAppUnregister();
  }

  void TestGattWriteCCC(uint16_t ccc_handle, GattStatus status,
                        int deregister_times) {
    SetSampleDatabaseCsis(1, 1);
    TestAppRegister();
    TestConnect(test_address);
    InjectConnectedEvent(test_address, 1);

    auto WriteDescriptorCbGenerator = [](tGATT_STATUS status,
                                         uint16_t ccc_handle) {
      return [status, ccc_handle](uint16_t conn_id, uint16_t handle,
                                  std::vector<uint8_t> value,
                                  tGATT_WRITE_TYPE write_type,
                                  GATT_WRITE_OP_CB cb, void* cb_data) -> void {
        if (cb) {
          if (ccc_handle) {
            handle = ccc_handle;
          }
          cb(conn_id, status, handle, value.size(), value.data(), cb_data);
        }
      };
    };

    // sirk, size, lock
    EXPECT_CALL(gatt_queue, WriteDescriptor(_, _, _, _, _, _))
        .Times(3)
        .WillOnce(Invoke(WriteDescriptorCbGenerator(GATT_SUCCESS, 0)))
        .WillOnce(Invoke(WriteDescriptorCbGenerator(GATT_SUCCESS, 0)))
        .WillOnce(Invoke(WriteDescriptorCbGenerator(status, ccc_handle)));

    EXPECT_CALL(gatt_interface, DeregisterForNotifications(_, _, _))
        .Times(deregister_times);

    GetSearchCompleteEvent(1);
    Mock::VerifyAndClearExpectations(&gatt_interface);
  }

  void GetDisconnectedEvent(const RawAddress& address, uint16_t conn_id) {
    tBTA_GATTC_CLOSE event_data = {
        .conn_id = conn_id,
@@ -682,6 +718,24 @@ TEST_F(CsisClientTest, test_discovery_csis_broken) {
  TestAppUnregister();
}

TEST_F(CsisClientTest, test_ccc_reg_fail_handle_not_found) {
  // service handle range: 0x0001 ~ 0x0030
  uint16_t not_existed_ccc_handle = 0x0031;
  TestGattWriteCCC(not_existed_ccc_handle, GATT_INVALID_HANDLE, 0);
}

TEST_F(CsisClientTest, test_ccc_reg_fail_handle_found) {
  // kCsisLockUuid ccc handle
  uint16_t existed_ccc_hande = 0x0028;
  TestGattWriteCCC(existed_ccc_hande, GATT_INVALID_HANDLE, 1);
}

TEST_F(CsisClientTest, test_ccc_reg_fail_out_of_sync) {
  // kCsisLockUuid ccc handle
  uint16_t ccc_handle = 0x0028;
  TestGattWriteCCC(ccc_handle, GATT_DATABASE_OUT_OF_SYNC, 0);
}

class CsisClientCallbackTest : public CsisClientTest {
 protected:
  const RawAddress test_address = GetTestAddress(0);
+17 −0
Original line number Diff line number Diff line
@@ -220,6 +220,23 @@ class CsisDevice : public GattServiceDevice {
    csis_instances_.clear();
  }

  uint16_t FindValueHandleByCccHandle(uint16_t ccc_handle) {
    uint16_t val_handle = 0;
    for (const auto& [_, inst] : csis_instances_) {
      if (inst->svc_data.sirk_handle.ccc_hdl == ccc_handle) {
        val_handle = inst->svc_data.sirk_handle.val_hdl;
      } else if (inst->svc_data.lock_handle.ccc_hdl == ccc_handle) {
        val_handle = inst->svc_data.lock_handle.val_hdl;
      } else if (inst->svc_data.size_handle.ccc_hdl == ccc_handle) {
        val_handle = inst->svc_data.size_handle.val_hdl;
      }
      if (val_handle) {
        break;
      }
    }
    return val_handle;
  }

  std::shared_ptr<CsisInstance> GetCsisInstanceByOwningHandle(uint16_t handle) {
    uint16_t hdl = 0;
    for (const auto& [h, inst] : csis_instances_) {
+70 −44
Original line number Diff line number Diff line
@@ -2481,19 +2481,23 @@ class LeAudioClientImpl : public LeAudioClient {

    for (const gatt::Service& tmp : *services) {
      if (tmp.uuid == le_audio::uuid::kPublishedAudioCapabilityServiceUuid) {
        LOG(INFO) << "Found Audio Capability service, handle: "
                  << loghex(tmp.handle);
        LOG_INFO("Found Audio Capability service, handle: 0x%04x, device: %s",
                 tmp.handle, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
        pac_svc = &tmp;
      } else if (tmp.uuid == le_audio::uuid::kAudioStreamControlServiceUuid) {
        LOG(INFO) << "Found Audio Stream Endpoint service, handle: "
                  << loghex(tmp.handle);
        LOG_INFO(
            "Found Audio Stream Endpoint service, handle: 0x%04x, device: %s",
            tmp.handle, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
        ase_svc = &tmp;
      } else if (tmp.uuid == bluetooth::csis::kCsisServiceUuid) {
        LOG(INFO) << "Found CSIS service, handle: " << loghex(tmp.handle)
                  << " is primary? " << tmp.is_primary;
        LOG_INFO(
            "Found CSIS service, handle: 0x%04x, is primary: %d, device: %s",
            tmp.handle, tmp.is_primary,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
        if (tmp.is_primary) csis_primary_handles.push_back(tmp.handle);
      } else if (tmp.uuid == le_audio::uuid::kCapServiceUuid) {
        LOG(INFO) << "Found CAP Service, handle: " << loghex(tmp.handle);
        LOG_INFO("Found CAP service, handle: 0x%04x, device: %s", tmp.handle,
                 ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));

        /* Try to find context for CSIS instances */
        for (auto& included_srvc : tmp.included_services) {
@@ -2506,8 +2510,10 @@ class LeAudioClientImpl : public LeAudioClient {
          }
        }
      } else if (tmp.uuid == le_audio::uuid::kTelephonyMediaAudioServiceUuid) {
        LOG_INFO(", Found Telephony and Media Audio service, handle: %04x",
                 tmp.handle);
        LOG_INFO(
            "Found Telephony and Media Audio service, handle: 0x%04x, device: "
            "%s",
            tmp.handle, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
        tmas_svc = &tmp;
      }
    }
@@ -2560,9 +2566,11 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->snk_pacs_.push_back(std::make_tuple(
            hdl_pair, std::vector<struct le_audio::types::acs_ac_record>()));

        LOG(INFO) << "Found Sink PAC characteristic, handle: "
                  << loghex(charac.value_handle)
                  << ", ccc handle: " << loghex(hdl_pair.ccc_hdl);
        LOG_INFO(
            "Found Sink PAC characteristic, handle: 0x%04x, ccc handle: "
            "0x%04x, addr: %s",
            charac.value_handle, hdl_pair.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      } else if (charac.uuid ==
                 le_audio::uuid::
                     kSourcePublishedAudioCapabilityCharacteristicUuid) {
@@ -2592,9 +2600,11 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->src_pacs_.push_back(std::make_tuple(
            hdl_pair, std::vector<struct le_audio::types::acs_ac_record>()));

        LOG(INFO) << "Found Source PAC characteristic, handle: "
                  << loghex(charac.value_handle)
                  << ", ccc handle: " << loghex(hdl_pair.ccc_hdl);
        LOG_INFO(
            "Found Source PAC characteristic, handle: 0x%04x, ccc handle: "
            "0x%04x, addr: %s",
            charac.value_handle, hdl_pair.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      } else if (charac.uuid ==
                 le_audio::uuid::kSinkAudioLocationCharacteristicUuid) {
        leAudioDevice->snk_audio_locations_hdls_.val_hdl = charac.value_handle;
@@ -2620,9 +2630,12 @@ class LeAudioClientImpl : public LeAudioClient {
            conn_id, leAudioDevice->snk_audio_locations_hdls_.val_hdl,
            OnGattReadRspStatic, NULL);

        LOG(INFO) << "Found Sink audio locations characteristic, handle: "
                  << loghex(charac.value_handle) << ", ccc handle: "
                  << loghex(leAudioDevice->snk_audio_locations_hdls_.ccc_hdl);
        LOG_INFO(
            "Found Sink audio locations characteristic, handle: 0x%04x, ccc "
            "handle: 0x%04x, addr: %s",
            charac.value_handle,
            leAudioDevice->snk_audio_locations_hdls_.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      } else if (charac.uuid ==
                 le_audio::uuid::kSourceAudioLocationCharacteristicUuid) {
        leAudioDevice->src_audio_locations_hdls_.val_hdl = charac.value_handle;
@@ -2648,9 +2661,12 @@ class LeAudioClientImpl : public LeAudioClient {
            conn_id, leAudioDevice->src_audio_locations_hdls_.val_hdl,
            OnGattReadRspStatic, NULL);

        LOG(INFO) << "Found Source audio locations characteristic, handle: "
                  << loghex(charac.value_handle) << ", ccc handle: "
                  << loghex(leAudioDevice->src_audio_locations_hdls_.ccc_hdl);
        LOG_INFO(
            "Found Source audio locations characteristic, handle: 0x%04x, ccc "
            "handle: 0x%04x, addr: %s",
            charac.value_handle,
            leAudioDevice->src_audio_locations_hdls_.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      } else if (charac.uuid ==
                 le_audio::uuid::kAudioContextAvailabilityCharacteristicUuid) {
        leAudioDevice->audio_avail_hdls_.val_hdl = charac.value_handle;
@@ -2676,9 +2692,11 @@ class LeAudioClientImpl : public LeAudioClient {
            conn_id, leAudioDevice->audio_avail_hdls_.val_hdl,
            OnGattReadRspStatic, NULL);

        LOG(INFO) << "Found Audio Availability Context characteristic, handle: "
                  << loghex(charac.value_handle) << ", ccc handle: "
                  << loghex(leAudioDevice->audio_avail_hdls_.ccc_hdl);
        LOG_INFO(
            "Found Audio Availability Context characteristic, handle: 0x%04x, "
            "ccc handle: 0x%04x, addr: %s",
            charac.value_handle, leAudioDevice->audio_avail_hdls_.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      } else if (charac.uuid ==
                 le_audio::uuid::kAudioSupportedContextCharacteristicUuid) {
        leAudioDevice->audio_supp_cont_hdls_.val_hdl = charac.value_handle;
@@ -2703,9 +2721,11 @@ class LeAudioClientImpl : public LeAudioClient {
            conn_id, leAudioDevice->audio_supp_cont_hdls_.val_hdl,
            OnGattReadRspStatic, NULL);

        LOG(INFO) << "Found Audio Supported Context characteristic, handle: "
                  << loghex(charac.value_handle) << ", ccc handle: "
                  << loghex(leAudioDevice->audio_supp_cont_hdls_.ccc_hdl);
        LOG_INFO(
            "Found Audio Supported Context characteristic, handle: 0x%04x, ccc "
            "handle: 0x%04x, addr: %s",
            charac.value_handle, leAudioDevice->audio_supp_cont_hdls_.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      }
    }

@@ -2739,10 +2759,11 @@ class LeAudioClientImpl : public LeAudioClient {
        leAudioDevice->ases_.emplace_back(charac.value_handle, ccc_handle,
                                          direction);

        LOG(INFO) << "Found ASE characteristic, handle: "
                  << loghex(charac.value_handle)
                  << ", ccc handle: " << loghex(ccc_handle)
                  << ", direction: " << direction;
        LOG_INFO(
            "Found ASE characteristic, handle: 0x%04x, ccc handle: 0x%04x, "
            "direction: %d, addr: %s",
            charac.value_handle, ccc_handle, direction,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      } else if (charac.uuid ==
                 le_audio::uuid::
                     kAudioStreamEndpointControlPointCharacteristicUuid) {
@@ -2763,9 +2784,11 @@ class LeAudioClientImpl : public LeAudioClient {
          return;
        }

        LOG(INFO) << "Found ASE Control Point characteristic, handle: "
                  << loghex(charac.value_handle) << ", ccc handle: "
                  << loghex(leAudioDevice->ctp_hdls_.ccc_hdl);
        LOG_INFO(
            "Found ASE characteristic, handle: 0x%04x, ccc handle: 0x%04x, "
            "addr: %s",
            charac.value_handle, leAudioDevice->ctp_hdls_.ccc_hdl,
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      }
    }

@@ -2781,9 +2804,11 @@ class LeAudioClientImpl : public LeAudioClient {
                                           OnGattReadRspStatic, NULL);

          LOG_INFO(
              ", Found Telephony and Media Profile characteristic, "
              "handle: %04x",
              leAudioDevice->tmap_role_hdl_);
              "Found Telephony and Media Profile characteristic, handle: "
              "0x%04x, "
              "device: %s",
              leAudioDevice->tmap_role_hdl_,
              ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
        }
      }
    }
@@ -2848,8 +2873,8 @@ class LeAudioClientImpl : public LeAudioClient {
    }

    if (status == GATT_SUCCESS) {
      LOG(INFO) << __func__
                << ", successfully registered on ccc: " << loghex(hdl);
      LOG_INFO("Successfully registered on ccc: 0x%04x, device: %s", hdl,
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));

      if (leAudioDevice->ctp_hdls_.ccc_hdl == hdl &&
          leAudioDevice->known_service_handles_ &&
@@ -2862,9 +2887,10 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    LOG(ERROR) << __func__
               << ", Failed to register for indications: " << loghex(hdl)
               << ", status: " << loghex((int)(status));
    LOG_ERROR(
        "Failed to register for indications: 0x%04x, device: %s, status: "
        "0x%02x",
        hdl, ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), status);

    ase_it =
        std::find_if(leAudioDevice->ases_.begin(), leAudioDevice->ases_.end(),
@@ -2873,8 +2899,8 @@ class LeAudioClientImpl : public LeAudioClient {
                     });

    if (ase_it == leAudioDevice->ases_.end()) {
      LOG(ERROR) << __func__
                 << ", unknown ccc handle: " << static_cast<int>(hdl);
      LOG_ERROR("Unknown ccc handle: 0x%04x, device: %s", hdl,
                ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
      return;
    }