summaryrefslogtreecommitdiff
path: root/system/main/shim/link_policy.cc
blob: 2d662f35625a9f37f4fb0b75b26781175633eacb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*
 * Copyright 2020 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.
 */

#include "main/shim/link_policy.h"

#include <base/bind.h>
#include <base/location.h>
#include <base/strings/stringprintf.h>

#include <cstdint>
#include <memory>

#include "device/include/interop.h"
#include "hci/controller.h"
#include "main/shim/controller.h"
#include "main/shim/dumpsys.h"
#include "main/shim/stack.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"  // UNUSED_ATTR
#include "stack/btm/btm_int_types.h"
#include "stack/include/btm_api.h"
#include "stack/include/btm_api_types.h"
#include "stack/include/btm_ble_api_types.h"
#include "stack/include/hci_error_code.h"
#include "types/raw_address.h"

bt_status_t do_in_main_thread(const base::Location& from_here,
                              base::OnceClosure task);

void btm_cont_rswitch_from_handle(uint16_t hci_handle);
void btm_pm_proc_mode_change(tHCI_STATUS hci_status, uint16_t hci_handle,
                             tHCI_MODE mode, uint16_t interval);
void btm_sco_chk_pend_unpark(tHCI_STATUS hci_status, uint16_t hci_handle);
void l2c_OnHciModeChangeSendPendingPackets(RawAddress remote);
void process_ssr_event(tHCI_STATUS status, uint16_t handle,
                       UNUSED_ATTR uint16_t max_tx_lat, uint16_t max_rx_lat);
tACL_CONN* acl_get_connection_from_handle(uint16_t handle);

extern tBTM_CB btm_cb;

namespace {

tBTM_STATUS set_active_mode(tACL_CONN& p_acl) {
  bluetooth::shim::Stack::GetInstance()->LinkPolicy()->ExitSniffMode(
      p_acl.hci_handle);
  return BTM_SUCCESS;
}

tBTM_STATUS set_hold_mode(tACL_CONN& p_acl, uint16_t max, uint16_t min) {
  bluetooth::shim::Stack::GetInstance()->LinkPolicy()->HoldMode(
      p_acl.hci_handle, max, min);
  return BTM_SUCCESS;
}

tBTM_STATUS set_sniff_mode(tACL_CONN& p_acl, uint16_t max_interval,
                           uint16_t min_interval, uint16_t attempt,
                           uint16_t timeout) {
  bluetooth::shim::Stack::GetInstance()->LinkPolicy()->SniffMode(
      p_acl.hci_handle, max_interval, min_interval, attempt, timeout);
  return BTM_SUCCESS;
}

bool controller_supports_link_policy_mode(const tBTM_PM_MODE& mode,
                                          bool interop_check) {
  switch (mode) {
    case BTM_PM_MD_ACTIVE:  // Active mode is always supported
      break;
    case BTM_PM_MD_PARK:  // Park mode no longer supported
      return false;
    case BTM_PM_MD_SNIFF:
      if (!controller_get_interface()->supports_sniff_mode() || interop_check)
        return false;
      break;
    case BTM_PM_MD_HOLD:
      if (!controller_get_interface()->supports_hold_mode() || interop_check)
        return false;
      break;
    default:
      LOG_ERROR("Unknown mode:%u", mode);
      return false;
  }
  return true;
}

}  // namespace

bool bluetooth::shim::RegisterLinkPolicyClient(tBTM_PM_STATUS_CBACK* p_cb) {
  if (std::find(btm_cb.acl_cb_.link_policy.clients.begin(),
                btm_cb.acl_cb_.link_policy.clients.end(),
                p_cb) != btm_cb.acl_cb_.link_policy.clients.end()) {
    LOG_ERROR("Link policy client already registered");
    return false;
  }
  btm_cb.acl_cb_.link_policy.clients.push_back(p_cb);
  return true;
}

bool bluetooth::shim::UnregisterLinkPolicyClient(tBTM_PM_STATUS_CBACK* p_cb) {
  auto cb = std::find(btm_cb.acl_cb_.link_policy.clients.begin(),
                      btm_cb.acl_cb_.link_policy.clients.end(), p_cb);
  if (cb == btm_cb.acl_cb_.link_policy.clients.end()) {
    LOG_ERROR("Link policy client already unregistered");
    return false;
  }
  btm_cb.acl_cb_.link_policy.clients.erase(cb);
  return true;
}

tBTM_STATUS bluetooth::shim::BTM_SetPowerMode(uint16_t handle,
                                              const tBTM_PM_PWR_MD& new_mode) {
  tACL_CONN* p_acl = acl_get_connection_from_handle(handle);
  if (p_acl == nullptr) {
    return BTM_UNKNOWN_ADDR;
  }

  if (!controller_supports_link_policy_mode(
          new_mode.mode,
          interop_match_addr(INTEROP_DISABLE_SNIFF, &p_acl->remote_addr))) {
    return BTM_MODE_UNSUPPORTED;
  }

  if (p_acl->policy.Mode() == new_mode.mode) {
    LOG_INFO("Controller already in mode:%s[0x%02x]",
             power_mode_state_text(p_acl->policy.Mode()).c_str(),
             p_acl->policy.Mode());
  }

  if (p_acl->policy.mode.IsPending()) {
    LOG_INFO("Link policy mode is pending");
  }

  LOG_INFO("Switching mode from %s(0x%x) to %s(0x%x)",
           power_mode_state_text(p_acl->policy.Mode()).c_str(),
           p_acl->policy.Mode(), power_mode_state_text(new_mode.mode).c_str(),
           new_mode.mode);

  p_acl->policy.mode.pending_ = new_mode.mode;
  switch (new_mode.mode) {
    case BTM_PM_MD_ACTIVE:
      set_active_mode(*p_acl);
      return BTM_SUCCESS;
      break;
    case BTM_PM_MD_SNIFF:
      set_sniff_mode(*p_acl, new_mode.max, new_mode.min, new_mode.attempt,
                     new_mode.timeout);
      return BTM_SUCCESS;
      break;
    case BTM_PM_MD_HOLD:
      return set_hold_mode(*p_acl, new_mode.max, new_mode.min);
      break;
  }
  return BTM_MODE_UNSUPPORTED;
}

static bool is_encryption_pause_supported(const tACL_CONN& p_acl) {
  CHECK(p_acl.peer_lmp_feature_valid[0])
      << "Checked before remote feature read has complete";
  return HCI_ATOMIC_ENCRYPT_SUPPORTED(p_acl.peer_lmp_feature_pages[0]) &&
         controller_get_interface()->supports_encryption_pause();
}

void bluetooth::shim::btm_pm_on_mode_change(tHCI_STATUS status, uint16_t handle,
                                            tHCI_MODE hci_mode,
                                            uint16_t interval) {
  tBTM_PM_MODE new_mode = HCI_TO_BTM_POWER_MODE(hci_mode);

  LOG_DEBUG(
      "For now pointing back again to legacy status:%s handle:0x%04x "
      "new_mode:%u interval:%u",
      hci_error_code_text(status).c_str(), handle, new_mode, interval);

  tACL_CONN* p_acl = acl_get_connection_from_handle(handle);
  if (p_acl == nullptr) {
    LOG_ERROR("Received mode change for unknown acl handle:0x%04x", handle);
    return;
  }

  tBTM_PM_MODE pending = p_acl->policy.mode.Pending();
  p_acl->policy.mode.pending_ = BTM_PM_MD_UNKNOWN;

  if (status == HCI_SUCCESS) {
    BTM_LogHistory(
        "Power", p_acl->remote_addr, "Mode change",
        base::StringPrintf("%s[0x%02x] ==> %s[0x%02x] pending:%s",
                           power_mode_state_text(p_acl->policy.Mode()).c_str(),
                           p_acl->policy.Mode(),
                           power_mode_state_text(new_mode).c_str(), new_mode,
                           power_mode_state_text(pending).c_str()));
    LOG_INFO("Power mode switched from %s[%hhu] to %s[%hhu] pending:%s",
             power_mode_state_text(p_acl->policy.Mode()).c_str(),
             p_acl->policy.Mode(), power_mode_state_text(new_mode).c_str(),
             new_mode, power_mode_state_text(pending).c_str());
    p_acl->policy.mode.mode_ = new_mode;

    if (new_mode == (BTM_PM_ST_ACTIVE) || new_mode == (BTM_PM_ST_SNIFF)) {
      l2c_OnHciModeChangeSendPendingPackets(p_acl->remote_addr);
    }

    /*check if sco disconnect  is waiting for the mode change */
    btm_sco_disc_chk_pend_for_modechange(handle);

    if (p_acl->is_switch_role_mode_change()) {
      if (p_acl->is_encrypted && !is_encryption_pause_supported(*p_acl)) {
        p_acl->set_encryption_off();
        p_acl->set_switch_role_encryption_off();
      } else {
        p_acl->set_switch_role_in_progress();
        p_acl->rs_disc_pending = BTM_SEC_RS_PENDING;
        bluetooth::legacy::hci::GetInterface().StartRoleSwitch(
            p_acl->remote_addr, HCI_ROLE_CENTRAL);
      }
    }
  }

  btm_sco_chk_pend_unpark(status, handle);
  // btm_pm_proc_mode_change(status, handle, new_mode, interval);

  for (auto client_callback : btm_cb.acl_cb_.link_policy.clients) {
    (*client_callback)(p_acl->remote_addr, new_mode, interval, status);
  }

  LOG_DEBUG(
      "Notified mode change registered clients cnt:%zu peer:%s "
      "status:%s",
      btm_cb.acl_cb_.link_policy.clients.size(),
      PRIVATE_ADDRESS(p_acl->remote_addr), hci_error_code_text(status).c_str());
}

tBTM_STATUS bluetooth::shim::BTM_SetSsrParams(uint16_t handle, uint16_t max_lat,
                                              uint16_t min_rmt_to,
                                              uint16_t min_loc_to) {
  LOG_DEBUG("Sending gd power mode SSR Params");
  tACL_CONN* p_acl = acl_get_connection_from_handle(handle);
  if (p_acl == nullptr) {
    return BTM_UNKNOWN_ADDR;
  }

  p_acl->policy.sniff_subrating.pending_ = true;
  bluetooth::shim::Stack::GetInstance()->LinkPolicy()->SniffSubrating(
      handle, max_lat, min_rmt_to, min_loc_to);
  return BTM_SUCCESS;
}

void bluetooth::shim::btm_pm_on_sniff_subrating(
    tHCI_STATUS status, uint16_t handle, uint16_t maximum_transmit_latency,
    UNUSED_ATTR uint16_t maximum_receive_latency,
    uint16_t minimum_remote_timeout, uint16_t minimum_local_timeout) {
  LOG_DEBUG("For now pointing back again to legacy");
  tACL_CONN* p_acl = acl_get_connection_from_handle(handle);
  if (p_acl == nullptr) {
    LOG_ERROR("Received mode change for unknown acl handle:0x%04x", handle);
    return;
  }

  p_acl->policy.sniff_subrating.pending_ = false;
  if (status == HCI_SUCCESS) {
    BTM_LogHistory(
        "Power", p_acl->remote_addr, "Sniff Subrating",
        base::StringPrintf(
            "max_xmit_latency:%.2fs remote_timeout:%.2fs local_timeout:%.2fs",
            ticks_to_seconds(maximum_transmit_latency),
            ticks_to_seconds(minimum_remote_timeout),
            ticks_to_seconds(minimum_local_timeout)));
  }

  const bool use_ssr =
      (p_acl->policy.mode.Interval() != maximum_receive_latency) ? true : false;

  for (auto client_callback : btm_cb.acl_cb_.link_policy.clients) {
    (*client_callback)(p_acl->remote_addr, BTM_PM_STS_SSR, (use_ssr) ? 1 : 0,
                       status);
  }

  LOG_DEBUG(
      "Notified sniff subrating registered clients cnt:%zu peer:%s use_ssr:%s "
      "status:%s",
      btm_cb.acl_cb_.link_policy.clients.size(),
      PRIVATE_ADDRESS(p_acl->remote_addr), logbool(use_ssr).c_str(),
      hci_error_code_text(status).c_str());
}