/* * 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 <getopt.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/time.h> using namespace android; void writeString16(Parcel& parcel, const char* string) { if (string != NULL) { 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 != NULL) { 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[]) { sp<IServiceManager> sm = defaultServiceManager(); fflush(stdout); if (sm == NULL) { aerr << "service: Unable to get default service manager!" << endl; return 20; } 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; } } 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 == NULL ? ": 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 != NULL && 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(NULL); } else if (strcmp(argv[optind], "intent") == 0) { char* action = NULL; char* dataArg = NULL; char* type = NULL; int launchFlags = 0; char* component = NULL; int categoryCount = 0; char* categories[16]; char* context1 = NULL; optind++; while (optind < argc) { char* key = strtok_r(argv[optind], "=", &context1); char* value = strtok_r(NULL, "=", &context1); // we have reached the end of the XXX=XXX args. if (key == NULL) 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 = NULL; int categoryCount = 0; categories[categoryCount] = strtok_r(value, ",", &context2); while (categories[categoryCount] != NULL) { categoryCount++; categories[categoryCount] = strtok_r(NULL, ",", &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 ] ...\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"; // " 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; }