/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace android; void writeString16(Parcel& parcel, const char* string) { if (string != nullptr) { parcel.writeString16(String16(string)); } else { parcel.writeInt32(-1); } } int main(int argc, char* const argv[]) { bool wantsUsage = false; int result = 0; /* Strip path off the program name. */ char* prog_name = basename(argv[0]); while (1) { int ic = getopt(argc, argv, "h?"); if (ic < 0) break; switch (ic) { case 'h': case '?': wantsUsage = true; break; default: aerr << prog_name << ": Unknown option -" << ic << endl; wantsUsage = true; result = 10; break; } } #ifdef VENDORSERVICES ProcessState::initWithDriver("/dev/vndbinder"); #endif #ifndef __ANDROID__ setDefaultServiceManager(createRpcDelegateServiceManager({.maxOutgoingConnections = 1})); #endif sp sm = defaultServiceManager(); fflush(stdout); if (sm == nullptr) { aerr << prog_name << ": 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 service = sm->checkService(String16(argv[optind])); aout << "Service " << argv[optind] << (service == nullptr ? ": not found" : ": found") << endl; } else { aerr << prog_name << ": No service specified for check" << endl; wantsUsage = true; result = 10; } } else if (strcmp(argv[optind], "list") == 0) { Vector services = sm->listServices(); aout << "Found " << services.size() << " services:" << endl; for (unsigned i = 0; i < services.size(); i++) { String16 name = services[i]; sp service = sm->checkService(name); aout << i << "\t" << name << ": [" << (service ? service->getInterfaceDescriptor() : String16()) << "]" << endl; } } else if (strcmp(argv[optind], "call") == 0) { optind++; if (optind+1 < argc) { int serviceArg = optind; sp service = sm->checkService(String16(argv[optind++])); String16 ifName = (service ? service->getInterfaceDescriptor() : String16()); int32_t code = atoi(argv[optind++]); if (service != nullptr && ifName.size() > 0) { Parcel data, reply; data.markForBinder(service); // 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 << prog_name << ": 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 << prog_name << ": 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 << prog_name << ": 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 << prog_name << ": 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 << prog_name << ": 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 << prog_name << ": 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 << prog_name << ": 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 << prog_name << ": 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 << prog_name << ": 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); (void)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 << prog_name << ": 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 << prog_name << ": unknown option " << argv[optind] << endl; wantsUsage = true; result = 10; break; } } service->transact(code, data, &reply); aout << "Result: " << reply << endl; } else { aerr << prog_name << ": Service " << argv[serviceArg] << " does not exist" << endl; result = 10; } } else { if (optind < argc) { aerr << prog_name << ": No service specified for call" << endl; } else { aerr << prog_name << ": No code specified for call" << endl; } wantsUsage = true; result = 10; } } else { aerr << prog_name << ": Unknown command " << argv[optind] << endl; wantsUsage = true; result = 10; } } if (wantsUsage) { aout << "Usage: " << prog_name << " [-h|-?]\n" " " << prog_name << " list\n" " " << prog_name << " check SERVICE\n" " " << prog_name << " 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 into the send parcel.\n" " nfd: Write the file descriptor n into the send parcel.\n" " afd: Write an ashmem file descriptor for a region containing the data from\n" " file f into the send parcel.\n"; // " intent: Write an Intent into 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; }