main.cc revision 10b54c4b7f1a863a27eca4158f256062ec9c3770
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/logging.h>
21#include <base/macros.h>
22#include <base/strings/string_split.h>
23#include <base/strings/string_util.h>
24#include <binder/ProcessState.h>
25
26#include "service/adapter_state.h"
27#include "service/ipc/binder/IBluetooth.h"
28#include "service/ipc/binder/IBluetoothCallback.h"
29
30using namespace std;
31
32using android::sp;
33
34using ipc::binder::IBluetooth;
35
36namespace {
37
38#define COLOR_OFF         "\x1B[0m"
39#define COLOR_RED         "\x1B[0;91m"
40#define COLOR_GREEN       "\x1B[0;92m"
41#define COLOR_YELLOW      "\x1B[0;93m"
42#define COLOR_BLUE        "\x1B[0;94m"
43#define COLOR_MAGENTA     "\x1B[0;95m"
44#define COLOR_BOLDGRAY    "\x1B[1;30m"
45#define COLOR_BOLDWHITE   "\x1B[1;37m"
46#define COLOR_BOLDYELLOW  "\x1B[1;93m"
47
48const char kCommandDisable[] = "disable";
49const char kCommandEnable[] = "enable";
50const char kCommandGetState[] = "get-state";
51const char kCommandIsEnabled[] = "is-enabled";
52
53#define CHECK_ARGS_COUNT(args, op, num, msg) \
54  if (!(args.size() op num)) { \
55    PrintError(msg); \
56    return; \
57  }
58#define CHECK_NO_ARGS(args) \
59  CHECK_ARGS_COUNT(args, ==, 0, "Expected no arguments")
60
61// TODO(armansito): Clean up this code. Right now everything is in this
62// monolithic file. We should organize this into different classes for command
63// handling, console output/printing, callback handling, etc.
64// (See http://b/23387611)
65
66// Used to synchronize the printing of the command-line prompt and incoming
67// Binder callbacks.
68std::atomic_bool showing_prompt(false);
69
70void PrintPrompt() {
71  cout << COLOR_BLUE "[FCLI] " COLOR_OFF << flush;
72}
73
74class CLIBluetoothCallback : public ipc::binder::BnBluetoothCallback {
75 public:
76  CLIBluetoothCallback() = default;
77  ~CLIBluetoothCallback() override = default;
78
79  // IBluetoothCallback override:
80  void OnBluetoothStateChange(
81      bluetooth::AdapterState prev_state,
82      bluetooth::AdapterState new_state) override {
83    if (showing_prompt.load())
84      cout << endl;
85    cout << COLOR_BOLDWHITE "Adapter state changed: " COLOR_OFF
86         << COLOR_MAGENTA << AdapterStateToString(prev_state) << COLOR_OFF
87         << COLOR_BOLDWHITE " -> " COLOR_OFF
88         << COLOR_BOLDYELLOW << AdapterStateToString(new_state) << COLOR_OFF
89         << endl << endl;
90    if (showing_prompt.load())
91      PrintPrompt();
92  }
93
94 private:
95  DISALLOW_COPY_AND_ASSIGN(CLIBluetoothCallback);
96};
97
98void PrintError(const string& message) {
99  cout << COLOR_RED << message << COLOR_OFF << endl;
100}
101
102void PrintCommandStatus(bool status) {
103  cout << COLOR_BOLDWHITE "Command status: " COLOR_OFF
104       << (status ? (COLOR_GREEN "success") : (COLOR_RED "failure"))
105       << COLOR_OFF << endl << endl;
106}
107
108void PrintFieldAndValue(const string& field, const string& value) {
109  cout << COLOR_BOLDWHITE << field << ": " << COLOR_BOLDYELLOW << value
110       << COLOR_OFF << endl;
111}
112
113void PrintFieldAndBoolValue(const string& field, bool value) {
114  PrintFieldAndValue(field, (value ? "true" : "false"));
115}
116
117void HandleDisable(IBluetooth* bt_iface, const vector<string>& args) {
118  CHECK_NO_ARGS(args);
119  PrintCommandStatus(bt_iface->Disable());
120}
121
122void HandleEnable(IBluetooth* bt_iface, const vector<string>& args) {
123  CHECK_NO_ARGS(args);
124  PrintCommandStatus(bt_iface->Enable());
125}
126
127void HandleGetState(IBluetooth* bt_iface, const vector<string>& args) {
128  CHECK_NO_ARGS(args);
129  bluetooth::AdapterState state = static_cast<bluetooth::AdapterState>(
130      bt_iface->GetState());
131  PrintFieldAndValue("Adapter state", bluetooth::AdapterStateToString(state));
132}
133
134void HandleIsEnabled(IBluetooth* bt_iface, const vector<string>& args) {
135  CHECK_NO_ARGS(args);
136  bool enabled = bt_iface->IsEnabled();
137  PrintFieldAndBoolValue("Adapter enabled", enabled);
138}
139
140void HandleGetLocalAddress(IBluetooth* bt_iface, const vector<string>& args) {
141  CHECK_NO_ARGS(args);
142  string address = bt_iface->GetAddress();
143  PrintFieldAndValue("Adapter address", address);
144}
145
146void HandleSetLocalName(IBluetooth* bt_iface, const vector<string>& args) {
147  CHECK_ARGS_COUNT(args, >=, 1, "No name was given");
148
149  std::string name;
150  for (const auto& arg : args)
151    name += arg + " ";
152
153  base::TrimWhitespaceASCII(name, base::TRIM_TRAILING, &name);
154
155  PrintCommandStatus(bt_iface->SetName(name));
156}
157
158void HandleGetLocalName(IBluetooth* bt_iface, const vector<string>& args) {
159  CHECK_NO_ARGS(args);
160  string name = bt_iface->GetName();
161  PrintFieldAndValue("Adapter name", name);
162}
163
164void HandleAdapterInfo(IBluetooth* bt_iface, const vector<string>& args) {
165  CHECK_NO_ARGS(args);
166
167  cout << COLOR_BOLDWHITE "Adapter Properties: " COLOR_OFF << endl;
168
169  PrintFieldAndValue("\tAddress", bt_iface->GetAddress());
170  PrintFieldAndValue("\tState", bluetooth::AdapterStateToString(
171      static_cast<bluetooth::AdapterState>(bt_iface->GetState())));
172  PrintFieldAndValue("\tName", bt_iface->GetName());
173  PrintFieldAndBoolValue("\tMulti-Adv. supported",
174                         bt_iface->IsMultiAdvertisementSupported());
175}
176
177void HandleSupportsMultiAdv(IBluetooth* bt_iface, const vector<string>& args) {
178  CHECK_NO_ARGS(args);
179
180  bool status = bt_iface->IsMultiAdvertisementSupported();
181  PrintFieldAndBoolValue("Multi-advertisement support", status);
182}
183
184void HandleHelp(IBluetooth* bt_iface, const vector<string>& args);
185
186struct {
187  string command;
188  void (*func)(IBluetooth*, const vector<string>& args);
189  string help;
190} kCommandMap[] = {
191  { "help", HandleHelp, "\t\t\tDisplay this message" },
192  { "disable", HandleDisable, "\t\t\tDisable Bluetooth" },
193  { "enable", HandleEnable, "\t\t\tEnable Bluetooth" },
194  { "get-state", HandleGetState, "\t\tGet the current adapter state" },
195  { "is-enabled", HandleIsEnabled, "\t\tReturn if Bluetooth is enabled" },
196  { "get-local-address", HandleGetLocalAddress,
197    "\tGet the local adapter address" },
198  { "set-local-name", HandleSetLocalName, "\t\tSet the local adapter name" },
199  { "get-local-name", HandleGetLocalName, "\t\tGet the local adapter name" },
200  { "adapter-info", HandleAdapterInfo, "\t\tPrint adapter properties" },
201  { "supports-multi-adv", HandleSupportsMultiAdv,
202    "\tWhether multi-advertisement is currently supported" },
203  {},
204};
205
206void HandleHelp(IBluetooth* /* bt_iface */, const vector<string>& /* args */) {
207  cout << endl;
208  for (int i = 0; kCommandMap[i].func; i++)
209    cout << "\t" << kCommandMap[i].command << kCommandMap[i].help << endl;
210  cout << endl;
211}
212
213}  // namespace
214
215int main() {
216  sp<IBluetooth> bt_iface = IBluetooth::getClientInterface();
217  if (!bt_iface.get()) {
218    LOG(ERROR) << "Failed to obtain handle on IBluetooth";
219    return EXIT_FAILURE;
220  }
221
222  // Initialize the Binder process thread pool. We have to set this up,
223  // otherwise, incoming callbacks from IBluetoothCallback will block the main
224  // thread (in other words, we have to do this as we are a "Binder server").
225  android::ProcessState::self()->startThreadPool();
226
227  // Register Adapter state-change callback
228  sp<CLIBluetoothCallback> callback = new CLIBluetoothCallback();
229  bt_iface->RegisterCallback(callback);
230
231  cout << COLOR_BOLDWHITE << "Fluoride Command-Line Interface\n" << COLOR_OFF
232       << endl
233       << "Type \"help\" to see possible commands.\n"
234       << endl;
235
236  while (true) {
237    string command;
238
239    PrintPrompt();
240
241    showing_prompt = true;
242    getline(cin, command);
243    showing_prompt = false;
244
245    vector<string> args;
246    base::SplitString(command, ' ', &args);
247
248    if (args.empty())
249      continue;
250
251    // The first argument is the command while the remaning are what we pass to
252    // the handler functions.
253    command = args[0];
254    args.erase(args.begin());
255
256    bool command_handled = false;
257    for (int i = 0; kCommandMap[i].func && !command_handled; i++) {
258      if (command == kCommandMap[i].command) {
259        kCommandMap[i].func(bt_iface.get(), args);
260        command_handled = true;
261      }
262    }
263
264    if (!command_handled)
265      cout << "Unrecognized command: " << command << endl;
266  }
267
268  return EXIT_SUCCESS;
269}
270