You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
380 lines
15 KiB
380 lines
15 KiB
/*
|
|
* 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;
|
|
}
|
|
|