main.cc revision 234138e2606dd7a54fbcc540643511abc0a3598d
1//
2//  Copyright (C) 2015 Google, Inc.
3//
4//  Licensed under the Apache License, Version 2.0 (the "License");
5//  you may not use this file except in compliance with the License.
6//  You may obtain a copy of the License at:
7//
8//  http://www.apache.org/licenses/LICENSE-2.0
9//
10//  Unless required by applicable law or agreed to in writing, software
11//  distributed under the License is distributed on an "AS IS" BASIS,
12//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13//  See the License for the specific language governing permissions and
14//  limitations under the License.
15//
16
17#include <iostream>
18#include <string>
19
20#include <base/at_exit.h>
21#include <base/command_line.h>
22#include <base/logging.h>
23#include <base/macros.h>
24#include <base/strings/string_split.h>
25#include <base/strings/string_util.h>
26#include <binder/IPCThreadState.h>
27#include <binder/ProcessState.h>
28
29#include <bluetooth/adapter_state.h>
30#include <bluetooth/binder/IBluetooth.h>
31#include <bluetooth/binder/IBluetoothCallback.h>
32#include <bluetooth/low_energy_constants.h>
33
34using namespace std;
35
36using android::sp;
37
38using ipc::binder::IBluetooth;
39using ipc::binder::IBluetoothLowEnergy;
40
41namespace {
42
43#define COLOR_OFF         "\x1B[0m"
44#define COLOR_RED         "\x1B[0;91m"
45#define COLOR_GREEN       "\x1B[0;92m"
46#define COLOR_YELLOW      "\x1B[0;93m"
47#define COLOR_BLUE        "\x1B[0;94m"
48#define COLOR_MAGENTA     "\x1B[0;95m"
49#define COLOR_BOLDGRAY    "\x1B[1;30m"
50#define COLOR_BOLDWHITE   "\x1B[1;37m"
51#define COLOR_BOLDYELLOW  "\x1B[1;93m"
52
53const char kCommandDisable[] = "disable";
54const char kCommandEnable[] = "enable";
55const char kCommandGetState[] = "get-state";
56const char kCommandIsEnabled[] = "is-enabled";
57
58#define CHECK_ARGS_COUNT(args, op, num, msg) \
59  if (!(args.size() op num)) { \
60    PrintError(msg); \
61    return; \
62  }
63#define CHECK_NO_ARGS(args) \
64  CHECK_ARGS_COUNT(args, ==, 0, "Expected no arguments")
65
66// TODO(armansito): Clean up this code. Right now everything is in this
67// monolithic file. We should organize this into different classes for command
68// handling, console output/printing, callback handling, etc.
69// (See http://b/23387611)
70
71// Used to synchronize the printing of the command-line prompt and incoming
72// Binder callbacks.
73std::atomic_bool showing_prompt(false);
74
75// The registered IBluetoothLowEnergy client handle. If |ble_registering| is
76// true then an operation to register the client is in progress.
77std::atomic_bool ble_registering(false);
78std::atomic_int ble_client_if(0);
79
80// True if the remote process has died and we should exit.
81std::atomic_bool should_exit(false);
82
83void PrintPrompt() {
84  cout << COLOR_BLUE "[FCLI] " COLOR_OFF << flush;
85}
86
87void PrintError(const string& message) {
88  cout << COLOR_RED << message << COLOR_OFF << endl;
89}
90
91void PrintOpStatus(const std::string& op, bool status) {
92  cout << COLOR_BOLDWHITE << op << " status: " COLOR_OFF
93       << (status ? (COLOR_GREEN "success") : (COLOR_RED "failure"))
94       << COLOR_OFF << endl << endl;
95}
96
97class CLIBluetoothCallback : public ipc::binder::BnBluetoothCallback {
98 public:
99  CLIBluetoothCallback() = default;
100  ~CLIBluetoothCallback() override = default;
101
102  // IBluetoothCallback overrides:
103  void OnBluetoothStateChange(
104      bluetooth::AdapterState prev_state,
105      bluetooth::AdapterState new_state) override {
106    if (showing_prompt.load())
107      cout << endl;
108    cout << COLOR_BOLDWHITE "Adapter state changed: " COLOR_OFF
109         << COLOR_MAGENTA << AdapterStateToString(prev_state) << COLOR_OFF
110         << COLOR_BOLDWHITE " -> " COLOR_OFF
111         << COLOR_BOLDYELLOW << AdapterStateToString(new_state) << COLOR_OFF
112         << endl << endl;
113    if (showing_prompt.load())
114      PrintPrompt();
115  }
116
117 private:
118  DISALLOW_COPY_AND_ASSIGN(CLIBluetoothCallback);
119};
120
121class CLIBluetoothLowEnergyCallback
122    : public ipc::binder::BnBluetoothLowEnergyCallback {
123 public:
124  CLIBluetoothLowEnergyCallback() = default;
125  ~CLIBluetoothLowEnergyCallback() = default;
126
127  // IBluetoothLowEnergyCallback overrides:
128  void OnClientRegistered(int status, int client_if) override {
129    if (showing_prompt.load())
130      cout << endl;
131    if (status != bluetooth::BLE_STATUS_SUCCESS) {
132      PrintError("Failed to register BLE client");
133    } else {
134      ble_client_if = client_if;
135      cout << COLOR_BOLDWHITE "Registered BLE client with ID: " COLOR_OFF
136           << COLOR_GREEN << client_if << COLOR_OFF << endl << endl;
137    }
138    if (showing_prompt.load())
139      PrintPrompt();
140
141    ble_registering = false;
142  }
143
144  void OnMultiAdvertiseCallback(
145      int status, bool is_start,
146      const bluetooth::AdvertiseSettings& /* settings */) {
147    if (showing_prompt.load())
148      cout << endl;
149
150    std::string op = is_start ? "start" : "stop";
151
152    PrintOpStatus("Advertising " + op, status == bluetooth::BLE_STATUS_SUCCESS);
153
154    if (showing_prompt.load())
155      PrintPrompt();
156  }
157
158 private:
159  DISALLOW_COPY_AND_ASSIGN(CLIBluetoothLowEnergyCallback);
160};
161
162void PrintCommandStatus(bool status) {
163  PrintOpStatus("Command", status);
164}
165
166void PrintFieldAndValue(const string& field, const string& value) {
167  cout << COLOR_BOLDWHITE << field << ": " << COLOR_BOLDYELLOW << value
168       << COLOR_OFF << endl;
169}
170
171void PrintFieldAndBoolValue(const string& field, bool value) {
172  PrintFieldAndValue(field, (value ? "true" : "false"));
173}
174
175void HandleDisable(IBluetooth* bt_iface, const vector<string>& args) {
176  CHECK_NO_ARGS(args);
177  PrintCommandStatus(bt_iface->Disable());
178}
179
180void HandleEnable(IBluetooth* bt_iface, const vector<string>& args) {
181  CHECK_NO_ARGS(args);
182  PrintCommandStatus(bt_iface->Enable());
183}
184
185void HandleGetState(IBluetooth* bt_iface, const vector<string>& args) {
186  CHECK_NO_ARGS(args);
187  bluetooth::AdapterState state = static_cast<bluetooth::AdapterState>(
188      bt_iface->GetState());
189  PrintFieldAndValue("Adapter state", bluetooth::AdapterStateToString(state));
190}
191
192void HandleIsEnabled(IBluetooth* bt_iface, const vector<string>& args) {
193  CHECK_NO_ARGS(args);
194  bool enabled = bt_iface->IsEnabled();
195  PrintFieldAndBoolValue("Adapter enabled", enabled);
196}
197
198void HandleGetLocalAddress(IBluetooth* bt_iface, const vector<string>& args) {
199  CHECK_NO_ARGS(args);
200  string address = bt_iface->GetAddress();
201  PrintFieldAndValue("Adapter address", address);
202}
203
204void HandleSetLocalName(IBluetooth* bt_iface, const vector<string>& args) {
205  CHECK_ARGS_COUNT(args, >=, 1, "No name was given");
206
207  std::string name;
208  for (const auto& arg : args)
209    name += arg + " ";
210
211  base::TrimWhitespaceASCII(name, base::TRIM_TRAILING, &name);
212
213  PrintCommandStatus(bt_iface->SetName(name));
214}
215
216void HandleGetLocalName(IBluetooth* bt_iface, const vector<string>& args) {
217  CHECK_NO_ARGS(args);
218  string name = bt_iface->GetName();
219  PrintFieldAndValue("Adapter name", name);
220}
221
222void HandleAdapterInfo(IBluetooth* bt_iface, const vector<string>& args) {
223  CHECK_NO_ARGS(args);
224
225  cout << COLOR_BOLDWHITE "Adapter Properties: " COLOR_OFF << endl;
226
227  PrintFieldAndValue("\tAddress", bt_iface->GetAddress());
228  PrintFieldAndValue("\tState", bluetooth::AdapterStateToString(
229      static_cast<bluetooth::AdapterState>(bt_iface->GetState())));
230  PrintFieldAndValue("\tName", bt_iface->GetName());
231  PrintFieldAndBoolValue("\tMulti-Adv. supported",
232                         bt_iface->IsMultiAdvertisementSupported());
233}
234
235void HandleSupportsMultiAdv(IBluetooth* bt_iface, const vector<string>& args) {
236  CHECK_NO_ARGS(args);
237
238  bool status = bt_iface->IsMultiAdvertisementSupported();
239  PrintFieldAndBoolValue("Multi-advertisement support", status);
240}
241
242void HandleRegisterBLE(IBluetooth* bt_iface, const vector<string>& args) {
243  CHECK_NO_ARGS(args);
244
245  if (ble_registering.load()) {
246    PrintError("In progress");
247    return;
248  }
249
250  if (ble_client_if.load()) {
251    PrintError("Already registered");
252    return;
253  }
254
255  sp<IBluetoothLowEnergy> ble_iface = bt_iface->GetLowEnergyInterface();
256  if (!ble_iface.get()) {
257    PrintError("Failed to obtain handle to Bluetooth Low Energy interface");
258    return;
259  }
260
261  bool status = ble_iface->RegisterClient(new CLIBluetoothLowEnergyCallback());
262  ble_registering = status;
263  PrintCommandStatus(status);
264}
265
266void HandleUnregisterBLE(IBluetooth* bt_iface, const vector<string>& args) {
267  CHECK_NO_ARGS(args);
268
269  if (!ble_client_if.load()) {
270    PrintError("Not registered");
271    return;
272  }
273
274  sp<IBluetoothLowEnergy> ble_iface = bt_iface->GetLowEnergyInterface();
275  if (!ble_iface.get()) {
276    PrintError("Failed to obtain handle to Bluetooth Low Energy interface");
277    return;
278  }
279
280  ble_iface->UnregisterClient(ble_client_if.load());
281  ble_client_if = 0;
282  PrintCommandStatus(true);
283}
284
285void HandleUnregisterAllBLE(IBluetooth* bt_iface, const vector<string>& args) {
286  CHECK_NO_ARGS(args);
287
288  sp<IBluetoothLowEnergy> ble_iface = bt_iface->GetLowEnergyInterface();
289  if (!ble_iface.get()) {
290    PrintError("Failed to obtain handle to Bluetooth Low Energy interface");
291    return;
292  }
293
294  ble_iface->UnregisterAll();
295  PrintCommandStatus(true);
296}
297
298void HandleStartAdv(IBluetooth* bt_iface, const vector<string>& args) {
299  bool include_name = false;
300  bool include_tx_power = false;
301  bool connectable = false;
302  bool set_manufacturer_data = false;
303
304  for (const auto& arg : args) {
305    if (arg == "-n")
306      include_name = true;
307    else if (arg == "-t")
308      include_tx_power = true;
309    else if (arg == "-c")
310      connectable = true;
311    else if (arg == "-m")
312      set_manufacturer_data = true;
313    else if (arg == "-h") {
314      const char* kUsage =
315          "Usage: start-adv [flags]\n"
316          "\n"
317          "Flags\n"
318          "\t-n\tInclude device name\n"
319          "\t-t\tInclude TX power\n"
320          "\t-c\tSend connectable adv. packets (default is non-connectable)\n"
321          "\t-m\tInclude random manufacturer data\n"
322          "\t-h\tShow this help message\n";
323      cout << kUsage << endl;
324      return;
325    }
326    else {
327      PrintError("Unrecognized option: " + arg);
328      return;
329    }
330  }
331
332  if (!ble_client_if.load()) {
333    PrintError("BLE not registered");
334    return;
335  }
336
337  sp<IBluetoothLowEnergy> ble_iface = bt_iface->GetLowEnergyInterface();
338  if (!ble_iface.get()) {
339    PrintError("Failed to obtain handle to Bluetooth Low Energy interface");
340    return;
341  }
342
343  std::vector<uint8_t> data;
344  if (set_manufacturer_data) {
345    data = {{
346      0x07, bluetooth::kEIRTypeManufacturerSpecificData,
347      0xe0, 0x00,
348      'T', 'e', 's', 't'
349    }};
350  }
351
352  base::TimeDelta timeout;
353
354  bluetooth::AdvertiseSettings settings(
355      bluetooth::AdvertiseSettings::MODE_LOW_POWER,
356      timeout,
357      bluetooth::AdvertiseSettings::TX_POWER_LEVEL_MEDIUM,
358      connectable);
359
360  bluetooth::AdvertiseData adv_data(data);
361  adv_data.set_include_device_name(include_name);
362  adv_data.set_include_tx_power_level(include_tx_power);
363
364  bluetooth::AdvertiseData scan_rsp;
365
366  bool status = ble_iface->StartMultiAdvertising(ble_client_if.load(),
367                                                 adv_data, scan_rsp, settings);
368  PrintCommandStatus(status);
369}
370
371void HandleStopAdv(IBluetooth* bt_iface, const vector<string>& args) {
372  if (!ble_client_if.load()) {
373    PrintError("BLE not registered");
374    return;
375  }
376
377  sp<IBluetoothLowEnergy> ble_iface = bt_iface->GetLowEnergyInterface();
378  if (!ble_iface.get()) {
379    PrintError("Failed to obtain handle to Bluetooth Low Energy interface");
380    return;
381  }
382
383  bool status = ble_iface->StopMultiAdvertising(ble_client_if.load());
384  PrintCommandStatus(status);
385}
386
387void HandleHelp(IBluetooth* bt_iface, const vector<string>& args);
388
389struct {
390  string command;
391  void (*func)(IBluetooth*, const vector<string>& args);
392  string help;
393} kCommandMap[] = {
394  { "help", HandleHelp, "\t\t\tDisplay this message" },
395  { "disable", HandleDisable, "\t\t\tDisable Bluetooth" },
396  { "enable", HandleEnable, "\t\t\tEnable Bluetooth" },
397  { "get-state", HandleGetState, "\t\tGet the current adapter state" },
398  { "is-enabled", HandleIsEnabled, "\t\tReturn if Bluetooth is enabled" },
399  { "get-local-address", HandleGetLocalAddress,
400    "\tGet the local adapter address" },
401  { "set-local-name", HandleSetLocalName, "\t\tSet the local adapter name" },
402  { "get-local-name", HandleGetLocalName, "\t\tGet the local adapter name" },
403  { "adapter-info", HandleAdapterInfo, "\t\tPrint adapter properties" },
404  { "supports-multi-adv", HandleSupportsMultiAdv,
405    "\tWhether multi-advertisement is currently supported" },
406  { "register-ble", HandleRegisterBLE,
407    "\t\tRegister with the Bluetooth Low Energy interface" },
408  { "unregister-ble", HandleUnregisterBLE,
409    "\t\tUnregister from the Bluetooth Low Energy interface" },
410  { "unregister-all-ble", HandleUnregisterAllBLE,
411    "\tUnregister all clients from the Bluetooth Low Energy interface" },
412  { "start-adv", HandleStartAdv, "\t\tStart advertising (-h for options)" },
413  { "stop-adv", HandleStopAdv, "\t\tStop advertising" },
414  {},
415};
416
417void HandleHelp(IBluetooth* /* bt_iface */, const vector<string>& /* args */) {
418  cout << endl;
419  for (int i = 0; kCommandMap[i].func; i++)
420    cout << "\t" << kCommandMap[i].command << kCommandMap[i].help << endl;
421  cout << endl;
422}
423
424}  // namespace
425
426class BluetoothDeathRecipient : public android::IBinder::DeathRecipient {
427 public:
428  BluetoothDeathRecipient() = default;
429  ~BluetoothDeathRecipient() override = default;
430
431  // android::IBinder::DeathRecipient override:
432  void binderDied(const android::wp<android::IBinder>& /* who */) override {
433    if (showing_prompt.load())
434      cout << endl;
435    cout << COLOR_BOLDWHITE "The Bluetooth daemon has died" COLOR_OFF << endl;
436    cout << "\nPress 'ENTER' to exit." << endl;
437    if (showing_prompt.load())
438      PrintPrompt();
439
440    android::IPCThreadState::self()->stopProcess();
441    should_exit = true;
442  }
443
444 private:
445  DISALLOW_COPY_AND_ASSIGN(BluetoothDeathRecipient);
446};
447
448int main(int argc, char* argv[]) {
449  base::AtExitManager exit_manager;
450  base::CommandLine::Init(argc, argv);
451  logging::LoggingSettings log_settings;
452
453  if (!logging::InitLogging(log_settings)) {
454    LOG(ERROR) << "Failed to set up logging";
455    return EXIT_FAILURE;
456  }
457
458  sp<IBluetooth> bt_iface = IBluetooth::getClientInterface();
459  if (!bt_iface.get()) {
460    LOG(ERROR) << "Failed to obtain handle on IBluetooth";
461    return EXIT_FAILURE;
462  }
463
464  sp<BluetoothDeathRecipient> dr(new BluetoothDeathRecipient());
465  if (android::IInterface::asBinder(bt_iface.get())->linkToDeath(dr) !=
466      android::NO_ERROR) {
467    LOG(ERROR) << "Failed to register DeathRecipient for IBluetooth";
468    return EXIT_FAILURE;
469  }
470
471  // Initialize the Binder process thread pool. We have to set this up,
472  // otherwise, incoming callbacks from IBluetoothCallback will block the main
473  // thread (in other words, we have to do this as we are a "Binder server").
474  android::ProcessState::self()->startThreadPool();
475
476  // Register Adapter state-change callback
477  sp<CLIBluetoothCallback> callback = new CLIBluetoothCallback();
478  bt_iface->RegisterCallback(callback);
479
480  cout << COLOR_BOLDWHITE << "Fluoride Command-Line Interface\n" << COLOR_OFF
481       << endl
482       << "Type \"help\" to see possible commands.\n"
483       << endl;
484
485  while (true) {
486    string command;
487
488    PrintPrompt();
489
490    showing_prompt = true;
491    getline(cin, command);
492    showing_prompt = false;
493
494    if (should_exit.load())
495      return EXIT_SUCCESS;
496
497    vector<string> args;
498    base::SplitString(command, ' ', &args);
499
500    if (args.empty())
501      continue;
502
503    // The first argument is the command while the remaning are what we pass to
504    // the handler functions.
505    command = args[0];
506    args.erase(args.begin());
507
508    bool command_handled = false;
509    for (int i = 0; kCommandMap[i].func && !command_handled; i++) {
510      if (command == kCommandMap[i].command) {
511        kCommandMap[i].func(bt_iface.get(), args);
512        command_handled = true;
513      }
514    }
515
516    if (!command_handled)
517      cout << "Unrecognized command: " << command << endl;
518  }
519
520  return EXIT_SUCCESS;
521}
522