blob: 18b6b58a9e627a07079f12d06c344ec704408681 [file] [log] [blame]
/*
* Copyright 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <binder/Parcel.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/TextOutput.h>
#include <cutils/ashmem.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace android;
void writeString16(Parcel& parcel, const char* string)
{
if (string != nullptr)
{
parcel.writeString16(String16(string));
}
else
{
parcel.writeInt32(-1);
}
}
// get the name of the generic interface we hold a reference to
static String16 get_interface_name(sp<IBinder> service)
{
if (service != nullptr) {
Parcel data, reply;
status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply);
if (err == NO_ERROR) {
return reply.readString16();
}
}
return String16();
}
static String8 good_old_string(const String16& src)
{
String8 name8;
char ch8[2];
ch8[1] = 0;
for (unsigned j = 0; j < src.size(); j++) {
char16_t ch = src[j];
if (ch < 128) ch8[0] = (char)ch;
name8.append(ch8);
}
return name8;
}
int main(int argc, char* const argv[])
{
bool wantsUsage = false;
int result = 0;
while (1) {
int ic = getopt(argc, argv, "h?");
if (ic < 0)
break;
switch (ic) {
case 'h':
case '?':
wantsUsage = true;
break;
default:
aerr << "service: Unknown option -" << ic << endl;
wantsUsage = true;
result = 10;
break;
}
}
#ifdef VENDORSERVICES
ProcessState::initWithDriver("/dev/vndbinder");
#endif
sp<IServiceManager> sm = defaultServiceManager();
fflush(stdout);
if (sm == nullptr) {
aerr << "service: Unable to get default service manager!" << endl;
return 20;
}
if (optind >= argc) {
wantsUsage = true;
} else if (!wantsUsage) {
if (strcmp(argv[optind], "check") == 0) {
optind++;
if (optind < argc) {
sp<IBinder> service = sm->checkService(String16(argv[optind]));
aout << "Service " << argv[optind] <<
(service == nullptr ? ": not found" : ": found") << endl;
} else {
aerr << "service: No service specified for check" << endl;
wantsUsage = true;
result = 10;
}
}
else if (strcmp(argv[optind], "list") == 0) {
Vector<String16> services = sm->listServices();
aout << "Found " << services.size() << " services:" << endl;
for (unsigned i = 0; i < services.size(); i++) {
String16 name = services[i];
sp<IBinder> service = sm->checkService(name);
aout << i
<< "\t" << good_old_string(name)
<< ": [" << good_old_string(get_interface_name(service)) << "]"
<< endl;
}
} else if (strcmp(argv[optind], "call") == 0) {
optind++;
if (optind+1 < argc) {
int serviceArg = optind;
sp<IBinder> service = sm->checkService(String16(argv[optind++]));
String16 ifName = get_interface_name(service);
int32_t code = atoi(argv[optind++]);
if (service != nullptr && ifName.size() > 0) {
Parcel data, reply;
// the interface name is first
data.writeInterfaceToken(ifName);
// then the rest of the call arguments
while (optind < argc) {
if (strcmp(argv[optind], "i32") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no integer supplied for 'i32'" << endl;
wantsUsage = true;
result = 10;
break;
}
data.writeInt32(atoi(argv[optind++]));
} else if (strcmp(argv[optind], "i64") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no integer supplied for 'i64'" << endl;
wantsUsage = true;
result = 10;
break;
}
data.writeInt64(atoll(argv[optind++]));
} else if (strcmp(argv[optind], "s16") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no string supplied for 's16'" << endl;
wantsUsage = true;
result = 10;
break;
}
data.writeString16(String16(argv[optind++]));
} else if (strcmp(argv[optind], "f") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no number supplied for 'f'" << endl;
wantsUsage = true;
result = 10;
break;
}
data.writeFloat(atof(argv[optind++]));
} else if (strcmp(argv[optind], "d") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no number supplied for 'd'" << endl;
wantsUsage = true;
result = 10;
break;
}
data.writeDouble(atof(argv[optind++]));
} else if (strcmp(argv[optind], "null") == 0) {
optind++;
data.writeStrongBinder(nullptr);
} else if (strcmp(argv[optind], "fd") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no path supplied for 'fd'" << endl;
wantsUsage = true;
result = 10;
break;
}
const char *path = argv[optind++];
int fd = open(path, O_RDONLY);
if (fd < 0) {
aerr << "service: could not open '" << path << "'" << endl;
wantsUsage = true;
result = 10;
break;
}
data.writeFileDescriptor(fd, true /* take ownership */);
} else if (strcmp(argv[optind], "afd") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no path supplied for 'afd'" << endl;
wantsUsage = true;
result = 10;
break;
}
const char *path = argv[optind++];
int fd = open(path, O_RDONLY);
struct stat statbuf;
if (fd < 0 || fstat(fd, &statbuf) != 0) {
aerr << "service: could not open or stat '" << path << "'" << endl;
wantsUsage = true;
result = 10;
break;
}
int afd = ashmem_create_region("test", statbuf.st_size);
void* ptr = mmap(NULL, statbuf.st_size,
PROT_READ | PROT_WRITE, MAP_SHARED, afd, 0);
read(fd, ptr, statbuf.st_size);
close(fd);
data.writeFileDescriptor(afd, true /* take ownership */);
} else if (strcmp(argv[optind], "nfd") == 0) {
optind++;
if (optind >= argc) {
aerr << "service: no file descriptor supplied for 'nfd'" << endl;
wantsUsage = true;
result = 10;
break;
}
data.writeFileDescriptor(
atoi(argv[optind++]), true /* take ownership */);
} else if (strcmp(argv[optind], "intent") == 0) {
char* action = nullptr;
char* dataArg = nullptr;
char* type = nullptr;
int launchFlags = 0;
char* component = nullptr;
int categoryCount = 0;
char* categories[16];
char* context1 = nullptr;
optind++;
while (optind < argc)
{
char* key = strtok_r(argv[optind], "=", &context1);
char* value = strtok_r(nullptr, "=", &context1);
// we have reached the end of the XXX=XXX args.
if (key == nullptr) break;
if (strcmp(key, "action") == 0)
{
action = value;
}
else if (strcmp(key, "data") == 0)
{
dataArg = value;
}
else if (strcmp(key, "type") == 0)
{
type = value;
}
else if (strcmp(key, "launchFlags") == 0)
{
launchFlags = atoi(value);
}
else if (strcmp(key, "component") == 0)
{
component = value;
}
else if (strcmp(key, "categories") == 0)
{
char* context2 = nullptr;
categories[categoryCount] = strtok_r(value, ",", &context2);
while (categories[categoryCount] != nullptr)
{
categoryCount++;
categories[categoryCount] = strtok_r(nullptr, ",", &context2);
}
}
optind++;
}
writeString16(data, action);
writeString16(data, dataArg);
writeString16(data, type);
data.writeInt32(launchFlags);
writeString16(data, component);
if (categoryCount > 0)
{
data.writeInt32(categoryCount);
for (int i = 0 ; i < categoryCount ; i++)
{
writeString16(data, categories[i]);
}
}
else
{
data.writeInt32(0);
}
// for now just set the extra field to be null.
data.writeInt32(-1);
} else {
aerr << "service: unknown option " << argv[optind] << endl;
wantsUsage = true;
result = 10;
break;
}
}
service->transact(code, data, &reply);
aout << "Result: " << reply << endl;
} else {
aerr << "service: Service " << argv[serviceArg]
<< " does not exist" << endl;
result = 10;
}
} else {
if (optind < argc) {
aerr << "service: No service specified for call" << endl;
} else {
aerr << "service: No code specified for call" << endl;
}
wantsUsage = true;
result = 10;
}
} else {
aerr << "service: Unknown command " << argv[optind] << endl;
wantsUsage = true;
result = 10;
}
}
if (wantsUsage) {
aout << "Usage: service [-h|-?]\n"
" service list\n"
" service check SERVICE\n"
" service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR | null"
" | fd f | nfd n | afd f ] ...\n"
"Options:\n"
" i32: Write the 32-bit integer N into the send parcel.\n"
" i64: Write the 64-bit integer N into the send parcel.\n"
" f: Write the 32-bit single-precision number N into the send parcel.\n"
" d: Write the 64-bit double-precision number N into the send parcel.\n"
" s16: Write the UTF-16 string STR into the send parcel.\n"
" null: Write a null binder into the send parcel.\n"
" fd: Write a file descriptor for the file f to the send parcel.\n"
" nfd: Write file descriptor n to the send parcel.\n"
" afd: Write an ashmem file descriptor for a region containing the data from"
" file f to the send parcel.\n";
// " intent: Write and Intent int the send parcel. ARGS can be\n"
// " action=STR data=STR type=STR launchFlags=INT component=STR categories=STR[,STR,...]\n";
return result;
}
return result;
}