1 /*
2  * Copyright (C) 2022 The Android Open Source Project
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 <android-base/file.h>
18 #include <android-base/logging.h>
19 #include <binder/BpBinder.h>
20 #include <binder/IServiceManager.h>
21 #include <binder/Parcel.h>
22 #include <binder/RecordedTransaction.h>
23 #include <signal.h>
24 #include <iostream>
25 #include <fstream>
26 #include <sstream>
27 #include "include/Analyzer.h"
28 
29 using android::IBinder;
30 using android::NO_ERROR;
31 using android::sp;
32 using android::status_t;
33 using android::String16;
34 using android::String8;
35 using android::aidl::Analyzer;
36 using android::base::unique_fd;
37 using android::binder::debug::RecordedTransaction;
38 using std::string;
39 namespace {
40 
41 static std::atomic_uint gCtrlCCount = 0;
42 static constexpr unsigned kCtrlCLimit = 3;
43 static const char kStandardRecordingPath[] = "/data/local/recordings/";
44 
getRecordingPath(const string & serviceName)45 string getRecordingPath(const string& serviceName) {
46   // Service names may contain '/', replace them with '.' to avoid interpreting as path
47   string filename = serviceName;
48   std::replace(filename.begin(), filename.end(), '/', '.');
49   return kStandardRecordingPath + filename;
50 }
51 
startRecording(const string & serviceName)52 status_t startRecording(const string& serviceName) {
53   sp<IBinder> binder =
54       android::defaultServiceManager()->checkService(String16(serviceName.c_str()));
55   if (binder == nullptr) {
56     return android::BAD_VALUE;
57   }
58 
59   auto filePath = getRecordingPath(serviceName);
60   if (auto mkdir_return = mkdir(kStandardRecordingPath, 0666);
61       mkdir_return != 0 && errno != EEXIST) {
62     std::cout << "Failed to create recordings directory.\n";
63     return android::NO_ERROR;
64   }
65 
66   int openFlags = O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC | O_BINARY;
67   unique_fd fd(open(filePath.c_str(), openFlags, 0666));
68   if (!fd.ok()) {
69     std::cout << "Failed to open file for recording with error: " << strerror(errno) << '\n';
70     return android::BAD_VALUE;
71   }
72 
73   // TODO (b/245804633): this still requires setenforce 0, but nothing above does
74   if (status_t err = binder->remoteBinder()->startRecordingBinder(fd); err != android::NO_ERROR) {
75     auto checkSE = std::ifstream("/sys/fs/selinux/enforce");
76     bool recommendSetenforce = false;
77     if ((checkSE.rdstate() & std::ifstream::failbit) != 0) {
78       std::cout << "Failed to determine selinux state.";
79       recommendSetenforce = true;
80     } else {
81       char seState = checkSE.get();
82       if (checkSE.good()) {
83         if (seState == '1') {
84           std::cout << "SELinux must be permissive.";
85           recommendSetenforce = true;
86         } else if (seState == '0') {
87           std::cout << "SELinux is permissive. Failing for some other reason.\n";
88         }
89       } else {
90         std::cout << "Failed to determine SELinux state.";
91         recommendSetenforce = true;
92       }
93     }
94     if (recommendSetenforce) {
95       std::cout << " Try running:\n\n  setenforce 0\n\n";
96     }
97     std::cout << "Failed to start recording with error: " << android::statusToString(err) << '\n';
98     return err;
99   } else {
100     std::cout << "Recording started successfully.\n";
101     return android::NO_ERROR;
102   }
103 }
104 
stopRecording(const string & serviceName)105 status_t stopRecording(const string& serviceName) {
106   sp<IBinder> binder =
107       android::defaultServiceManager()->checkService(String16(serviceName.c_str()));
108   if (binder == nullptr) {
109     return android::BAD_VALUE;
110   }
111 
112   if (status_t err = binder->remoteBinder()->stopRecordingBinder(); err != NO_ERROR) {
113     std::cout << "Failed to stop recording with error: " << err << '\n';
114     return err;
115   }
116 
117   std::cout << "Recording stopped successfully.\n";
118   return NO_ERROR;
119 }
120 
printTransaction(const RecordedTransaction & transaction)121 void printTransaction(const RecordedTransaction& transaction) {
122   auto& analyzers = Analyzer::getAnalyzers();
123 
124   auto analyzer = analyzers.find(transaction.getInterfaceName());
125   if (analyzer != analyzers.end()) {
126     (analyzer->second)
127         ->getAnalyzeFunction()(transaction.getCode(), transaction.getDataParcel(),
128                                transaction.getReplyParcel());
129   } else {
130     std::cout << "No analyzer:";
131     std::cout << "  interface: " << transaction.getInterfaceName() << "\n";
132     std::cout << "  code: " << transaction.getCode() << "\n";
133     std::cout << "  data: " << transaction.getDataParcel().dataSize() << " bytes\n";
134     std::cout << "  reply: " << transaction.getReplyParcel().dataSize() << " bytes\n";
135   }
136   std::cout << "  status: " << transaction.getReturnedStatus() << "\n\n";
137 }
138 
inspectRecording(const string & path)139 status_t inspectRecording(const string& path) {
140   auto& analyzers = Analyzer::getAnalyzers();
141 
142   unique_fd fd(open(path.c_str(), O_RDONLY));
143   if (!fd.ok()) {
144     std::cout << "Failed to open recording file with error: " << strerror(errno) << '\n';
145     return android::BAD_VALUE;
146   }
147 
148   int i = 1;
149   while (auto transaction = RecordedTransaction::fromFile(fd)) {
150     std::cout << "Transaction " << i << ":\n";
151     printTransaction(transaction.value());
152     i++;
153   }
154   return NO_ERROR;
155 }
156 
incrementCtrlCCount(int signum)157 void incrementCtrlCCount(int signum) {
158   if (++gCtrlCCount > kCtrlCLimit) {
159     std::cout
160         << "Ctrl+C multiple times, but could not quit application. If recording still running, you "
161            "might stop it manually.\n";
162     exit(signum);
163   }
164 }
165 
listenToFile(const string & serviceName)166 status_t listenToFile(const string& serviceName) {
167   auto filePath = getRecordingPath(serviceName);
168   unique_fd listenFd(open(filePath.c_str(), O_RDONLY));
169   if (!listenFd.ok()) {
170     std::cout << "Failed to open listening file with error: " << strerror(errno) << '\n';
171     return android::BAD_VALUE;
172   }
173 
174   auto& analyzers = Analyzer::getAnalyzers();
175 
176   signal(SIGINT, incrementCtrlCCount);
177   std::cout << "Starting to listen:\n";
178   int i = 1;
179   while (gCtrlCCount == 0) {
180     auto transaction = RecordedTransaction::fromFile(listenFd);
181     if (!transaction) {
182       sleep(1);
183       continue;
184     }
185     std::cout << "Transaction " << i << ":\n";
186     printTransaction(transaction.value());
187     i++;
188   }
189   return NO_ERROR;
190 }
191 
replayFile(const sp<IBinder> & binder,const string & path)192 status_t replayFile(const sp<IBinder>& binder, const string& path) {
193   auto& analyzers = Analyzer::getAnalyzers();
194 
195   unique_fd fd(open(path.c_str(), O_RDONLY));
196   if (!fd.ok()) {
197     std::cout << "Failed to open recording file with error: " << strerror(errno) << '\n';
198     return android::BAD_VALUE;
199   }
200 
201   int failureCount = 0;
202   int i = 1;
203   while (auto transaction = RecordedTransaction::fromFile(fd)) {
204     std::cout << "Replaying Transaction " << i << ":\n";
205     printTransaction(transaction.value());
206 
207     android::Parcel send, reply;
208     send.setData(transaction->getDataParcel().data(), transaction->getDataParcel().dataSize());
209     android::status_t status = binder->remoteBinder()->transact(transaction->getCode(), send,
210                                                                 &reply, transaction->getFlags());
211     if (status != transaction->getReturnedStatus()) {
212       std::cout << "Failure: Expected status " << transaction->getReturnedStatus()
213                 << " but received status " << status << "\n\n";
214       failureCount++;
215     } else {
216       std::cout << "Transaction replayed correctly."
217                 << "\n\n";
218     }
219     i++;
220   }
221   std::cout << i << " transactions replayed.\n";
222   if (failureCount > 0) {
223     std::cout << failureCount << " transactions had unexpected status. See logs for details.\n";
224     return android::UNKNOWN_ERROR;
225   } else {
226     return NO_ERROR;
227   }
228 }
229 
listAvailableInterfaces(int,char **)230 status_t listAvailableInterfaces(int, char**) {
231   auto& analyzers = Analyzer::getAnalyzers();
232   std::cout << "Available Interfaces (" << analyzers.size() << "):\n";
233   for (auto a = analyzers.begin(); a != analyzers.end(); a++) {
234     std::cout << "  " << a->second->getInterfaceName() << '\n';
235   }
236   return NO_ERROR;
237 }
238 
239 struct AnalyzerCommand {
240   std::function<status_t(int, char*[])> command;
241   std::string overview;
242   std::string compactArguments;
243   std::string helpDetail;
244 };
245 
246 status_t helpCommandEntryPoint(int argc, char* argv[]);
247 
248 const AnalyzerCommand helpCommand = {helpCommandEntryPoint, "Show help information.", "<command>",
249                                      ""};
250 
251 const AnalyzerCommand listCommand = {listAvailableInterfaces,
252                                      "Prints a list of available interfaces.", "", ""};
253 
startRecordingAllBinders()254 status_t startRecordingAllBinders() {
255   auto services = android::defaultServiceManager()->listServices();
256   for (auto service : services) {
257     std::string serviceName = String8(service.c_str()).c_str();
258     // Print failed service name. Don't exit early because it would leave the previous successful
259     // services recording.
260     if (status_t result = startRecording(serviceName); result != NO_ERROR) {
261       std::cout << "Failed to start binder recording on service : " << service << std::endl;
262     }
263   }
264 
265   return NO_ERROR;
266 }
267 
startCommandEntryPoint(int argc,char * argv[])268 status_t startCommandEntryPoint(int argc, char* argv[]) {
269   if (argc != 3) {
270     helpCommandEntryPoint(argc, argv);
271     return android::BAD_VALUE;
272   }
273 
274   string startOption = argv[2];
275   if (startOption == "--all") {
276     return startRecordingAllBinders();
277   }
278   return startRecording(startOption);
279 }
280 
281 const AnalyzerCommand startCommand = {startCommandEntryPoint,
282                                       "Start recording Binder transactions from a given service. "
283                                       "Use --all to start recoding all binders.",
284                                       "<service>, --all",
285                                       "  <service>\tService to record. See 'dumpsys -l'"};
286 
stopRecordingAllBinders()287 status_t stopRecordingAllBinders() {
288   auto services = android::defaultServiceManager()->listServices();
289   for (auto service : services) {
290     std::string serviceName = String8(service.c_str()).c_str();
291     // Print failed service name. Don't exit early because it would leave the other recordings on.
292     if (status_t result = stopRecording(serviceName); result != NO_ERROR) {
293       std::cout << "Failed to stop binder recording on service : " << service << std::endl;
294     }
295   }
296 
297   return NO_ERROR;
298 }
299 
stopCommandEntryPoint(int argc,char * argv[])300 status_t stopCommandEntryPoint(int argc, char* argv[]) {
301   if (argc != 3) {
302     helpCommandEntryPoint(argc, argv);
303     return android::BAD_VALUE;
304   }
305   string stopOption = argv[2];
306   if (stopOption == "--all") {
307     stopRecordingAllBinders();
308   }
309 
310   return stopRecording(stopOption);
311 }
312 
313 const AnalyzerCommand stopCommand = {
314     stopCommandEntryPoint,
315     "Stops recording Binder transactions from a given process. (See 'start') Use --all to stop "
316     "recoding all binders",
317     "<service>, --all",
318     "  <service>\tService to stop recording; <service> argument to previous 'start' command."};
319 
inspectCommandEntryPoint(int argc,char * argv[])320 status_t inspectCommandEntryPoint(int argc, char* argv[]) {
321   if (argc != 3) {
322     helpCommandEntryPoint(argc, argv);
323     return android::BAD_VALUE;
324   }
325   std::string path = kStandardRecordingPath + string(argv[2]);
326 
327   return inspectRecording(path);
328 }
329 
330 const AnalyzerCommand inspectCommand = {
331     inspectCommandEntryPoint,
332     "Writes the binder transactions in <file-name> to stdout in a human-friendly format.",
333     "<file-name>",
334     "  <file-name>\tA recording in /data/local/recordings/, and the name of the service"};
335 
listenCommandEntryPoint(int argc,char * argv[])336 status_t listenCommandEntryPoint(int argc, char* argv[]) {
337   if (argc != 3) {
338     helpCommandEntryPoint(argc, argv);
339     return android::BAD_VALUE;
340   }
341 
342   string serviceName = argv[2];
343   if (status_t startErr = startRecording(serviceName); startErr != NO_ERROR) {
344     return startErr;
345   }
346 
347   status_t listenStatus = listenToFile(serviceName);
348 
349   if (status_t stopErr = stopRecording(serviceName); stopErr != NO_ERROR) {
350     return stopErr;
351   }
352 
353   return listenStatus;
354 }
355 
356 const AnalyzerCommand listenCommand = {
357     listenCommandEntryPoint,
358     "Starts recording binder transactions in <service> and writes transactions to "
359     "stdout.",
360     "<service>", "  <service>\t?\n"};
361 
replayFunction(int argc,char * argv[])362 int replayFunction(int argc, char* argv[]) {
363   if (argc != 4) {
364     return helpCommandEntryPoint(argc, argv);
365   }
366 
367   sp<IBinder> binder = android::defaultServiceManager()->checkService(String16(argv[2]));
368   std::string path = kStandardRecordingPath + string(argv[3]);
369 
370   return replayFile(binder, path);
371 }
372 
373 const AnalyzerCommand replayCommand = {
374     replayFunction, "No overview", "<service> <file-name>",
375     "  <service>\t?\n"
376     "  <file-name>\tThe name of a file in /data/local/recordings/"};
377 
378 auto& commands = *new std::map<std::string, AnalyzerCommand>{
379     {"start", startCommand},   {"stop", stopCommand},     {"inspect", inspectCommand},
380     {"listen", listenCommand}, {"replay", replayCommand}, {"help", helpCommand}};
381 
printGeneralHelp(std::string & toolName)382 void printGeneralHelp(std::string& toolName) {
383   std::cout << "USAGE: " << toolName << " <command> [<args>]\n\n";
384   std::cout << "COMMANDS:\n";
385   // Display overview this many characters from the start of a line.
386   // Subtract the length of the command name to calculate padding.
387   const size_t commandOverviewDisplayAlignment = 12;
388   for (const auto& command : commands) {
389     if (command.first == "help") {
390       continue;
391     }
392     std::cout << "  " << command.first
393               << std::string(commandOverviewDisplayAlignment - command.first.length(), ' ')
394               << command.second.overview << "\n";
395   }
396   std::cout << "\n  See '" << toolName << " help <command>' for detailed help.\n";
397 }
398 
helpCommandEntryPoint(int argc,char * argv[])399 status_t helpCommandEntryPoint(int argc, char* argv[]) {
400   std::string toolName = argv[0];
401 
402   if (argc < 2) {
403     printGeneralHelp(toolName);
404     return 0;
405   }
406 
407   std::string commandName = argv[1];
408 
409   if (commandName == "help") {
410     if (argc < 3) {
411       printGeneralHelp(toolName);
412       return 0;
413     }
414     commandName = argv[2];
415   } else {
416     commandName = argv[1];
417   }
418 
419   auto command = commands.find(commandName);
420   if (command == commands.end()) {
421     std::cout << "Unrecognized command: " << commandName << "\n";
422     printGeneralHelp(toolName);
423     return -1;
424   }
425 
426   std::cout << "OVERVIEW: " << command->second.overview << "\n\n";
427   std::cout << "USAGE: " << toolName << " " << commandName << " "
428             << command->second.compactArguments << "\n\n";
429   std::cout << "ARGUMENTS:\n" << command->second.helpDetail << "\n";
430 
431   return 0;
432 }
433 
434 }  // namespace
435 
main(int argc,char * argv[])436 int main(int argc, char* argv[]) {
437   std::string toolName = argv[0];
438 
439   auto& analyzers = Analyzer::getAnalyzers();
440   if (analyzers.size() >= 1) {
441     commands["list"] = listCommand;
442   }
443 
444   if (argc < 2 ||
445       (argc >= 2 && ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0)))) {
446     // General help
447     printGeneralHelp(toolName);
448     return 0;
449   }
450 
451   auto command = commands.find(argv[1]);
452   if (command == commands.end()) {
453     std::cout << "Unrecognized command: " << argv[1] << "\n";
454     printGeneralHelp(toolName);
455     return -1;
456   }
457 
458   return command->second.command(argc, argv);
459 }
460