1/*
2 * Copyright (C) 2016 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#define LOG_TAG "incident"
18
19#include "incident_sections.h"
20
21#include <android/os/BnIncidentReportStatusListener.h>
22#include <android/os/IIncidentManager.h>
23#include <android/os/IncidentReportArgs.h>
24#include <binder/IPCThreadState.h>
25#include <binder/IServiceManager.h>
26#include <utils/Looper.h>
27
28#include <fcntl.h>
29#include <getopt.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <unistd.h>
33
34using namespace android;
35using namespace android::base;
36using namespace android::binder;
37using namespace android::os;
38
39// ================================================================================
40class StatusListener : public BnIncidentReportStatusListener {
41public:
42    StatusListener();
43    virtual ~StatusListener();
44
45    virtual Status onReportStarted();
46    virtual Status onReportSectionStatus(int32_t section, int32_t status);
47    virtual Status onReportServiceStatus(const String16& service, int32_t status);
48    virtual Status onReportFinished();
49    virtual Status onReportFailed();
50};
51
52StatusListener::StatusListener()
53{
54}
55
56StatusListener::~StatusListener()
57{
58}
59
60Status
61StatusListener::onReportStarted()
62{
63    return Status::ok();
64}
65
66Status
67StatusListener::onReportSectionStatus(int32_t section, int32_t status)
68{
69    fprintf(stderr, "section %d status %d\n", section, status);
70    return Status::ok();
71}
72
73Status
74StatusListener::onReportServiceStatus(const String16& service, int32_t status)
75{
76    fprintf(stderr, "service '%s' status %d\n", String8(service).string(), status);
77    return Status::ok();
78}
79
80Status
81StatusListener::onReportFinished()
82{
83    fprintf(stderr, "done\n");
84    exit(0);
85    return Status::ok();
86}
87
88Status
89StatusListener::onReportFailed()
90{
91    fprintf(stderr, "failed\n");
92    exit(1);
93    return Status::ok();
94}
95
96// ================================================================================
97static IncidentSection const*
98find_section(const char* name)
99{
100    size_t low = 0;
101    size_t high = INCIDENT_SECTION_COUNT - 1;
102
103    while (low <= high) {
104        size_t mid = (low + high) >> 1;
105        IncidentSection const* section = INCIDENT_SECTIONS + mid;
106
107        int cmp = strcmp(section->name, name);
108        if (cmp < 0) {
109            low = mid + 1;
110        } else if (cmp > 0) {
111            high = mid - 1;
112        } else {
113            return section;
114        }
115    }
116    return NULL;
117}
118
119// ================================================================================
120static void
121usage(FILE* out)
122{
123    fprintf(out, "usage: incident OPTIONS [SECTION...]\n");
124    fprintf(out, "\n");
125    fprintf(out, "Takes an incident report.\n");
126    fprintf(out, "\n");
127    fprintf(out, "OPTIONS\n");
128    fprintf(out, "  -b           (default) print the report to stdout (in proto format)\n");
129    fprintf(out, "  -d           send the report into dropbox\n");
130    fprintf(out, "\n");
131    fprintf(out, "  SECTION     the field numbers of the incident report fields to include\n");
132    fprintf(out, "\n");
133}
134
135int
136main(int argc, char** argv)
137{
138    Status status;
139    IncidentReportArgs args;
140    enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT;
141
142    // Parse the args
143    int opt;
144    while ((opt = getopt(argc, argv, "bhd")) != -1) {
145        switch (opt) {
146            case 'b':
147                destination = DEST_STDOUT;
148                break;
149            case 'h':
150                usage(stdout);
151                return 0;
152            case 'd':
153                destination = DEST_DROPBOX;
154                break;
155            default:
156                usage(stderr);
157                return 1;
158        }
159    }
160
161    if (optind == argc) {
162        args.setAll(true);
163    } else {
164        for (int i=optind; i<argc; i++) {
165            const char* arg = argv[i];
166            char* end;
167            if (arg[0] != '\0') {
168                int section = strtol(arg, &end, 0);
169                if (*end == '\0') {
170                    args.addSection(section);
171                } else {
172                    IncidentSection const* ic = find_section(arg);
173                    if (ic == NULL) {
174                        fprintf(stderr, "Invalid section: %s\n", arg);
175                        return 1;
176                    }
177                    args.addSection(ic->id);
178                }
179            }
180        }
181    }
182
183
184
185    // Start the thread pool.
186    sp<ProcessState> ps(ProcessState::self());
187    ps->startThreadPool();
188    ps->giveThreadPoolName();
189
190    // Look up the service
191    sp<IIncidentManager> service = interface_cast<IIncidentManager>(
192            defaultServiceManager()->getService(android::String16("incident")));
193    if (service == NULL) {
194        fprintf(stderr, "Couldn't look up the incident service\n");
195        return 1;
196    }
197
198    // Construct the stream
199    int fds[2];
200    pipe(fds);
201
202    unique_fd readEnd(fds[0]);
203    unique_fd writeEnd(fds[1]);
204
205    if (destination == DEST_STDOUT) {
206        // Call into the service
207        sp<StatusListener> listener(new StatusListener());
208        status = service->reportIncidentToStream(args, listener, writeEnd);
209
210        if (!status.isOk()) {
211            fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
212        }
213
214        // Wait for the result and print out the data they send.
215        //IPCThreadState::self()->joinThreadPool();
216
217        while (true) {
218            int amt = splice(fds[0], NULL, STDOUT_FILENO, NULL, 4096, 0);
219            fprintf(stderr, "spliced %d bytes\n", amt);
220            if (amt < 0) {
221                return errno;
222            } else if (amt == 0) {
223                return 0;
224            }
225        }
226    } else {
227        status = service->reportIncident(args);
228        if (!status.isOk()) {
229            fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
230            return 1;
231        } else {
232            return 0;
233        }
234    }
235
236}
237