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