diff options
Diffstat (limited to 'adapter/HidlBinderAdapter.cpp')
-rw-r--r-- | adapter/HidlBinderAdapter.cpp | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/adapter/HidlBinderAdapter.cpp b/adapter/HidlBinderAdapter.cpp new file mode 100644 index 0000000..ed6dad1 --- /dev/null +++ b/adapter/HidlBinderAdapter.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2017 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 <hidladapter/HidlBinderAdapter.h> + +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/hidl/base/1.0/IBase.h> +#include <android/hidl/manager/1.0/IServiceManager.h> +#include <hidl/HidlTransportSupport.h> + +#include <unistd.h> +#include <iostream> +#include <map> +#include <string> + +namespace android { +namespace hardware { +namespace details { + +using android::base::SetProperty; +using android::base::WaitForProperty; + +const static std::string kDeactivateProp = "test.hidl.adapters.deactivated"; + +int usage(const std::string& me) { + std::cerr << "usage: " << me + << " [-p|P] [-n instance-name] interface-name instance-name number-of-threads." + << std::endl; + std::cerr << " -p: stop based on property " << kDeactivateProp << "and reset it." + << std::endl; + std::cerr << " -P: stop based on interface specific property " << kDeactivateProp + << ".<fq-name>.<instance-name>" << std::endl; + std::cerr + << " -n instance-name: register as a different instance name (does not de-register)" + << std::endl; + return EINVAL; +} + +enum class StopMethod { + NONE, + ALL, + SPECIFIC, +}; + +struct Args { + StopMethod stopMethod = StopMethod::NONE; + std::string interface; // e.x. IFoo + std::string instanceName; // e.x. default + int threadNumber; + std::string registerInstanceName; // e.x. default +}; + +bool processArguments(int argc, char** argv, Args* args) { + int c; + while ((c = getopt(argc, argv, "pPn:")) != -1) { + switch (c) { + case 'p': { + args->stopMethod = StopMethod::ALL; + break; + } + case 'P': { + args->stopMethod = StopMethod::SPECIFIC; + break; + } + case 'n': { + args->registerInstanceName = optarg; + break; + } + default: { return false; } + } + } + + argc -= optind; + argv += optind; + + if (argc != 3) { + std::cerr << "ERROR: requires exactly three positional arguments for " + "interface, instance name, and number of threads, but " + << argc << " provided." << std::endl; + return false; + } + + args->interface = argv[0]; + args->instanceName = argv[1]; + args->threadNumber = std::stoi(argv[2]); + + if (args->threadNumber <= 0) { + std::cerr << "ERROR: invalid thread number " << args->threadNumber + << " must be a positive integer." << std::endl; + return false; + } + + if (args->registerInstanceName.empty()) { + args->registerInstanceName = args->instanceName; + } + + return true; +} + +// only applies for -p argument +void waitForAdaptersDeactivated(const std::string& property) { + using std::literals::chrono_literals::operator""s; + + while (!WaitForProperty(property, "true", 30s)) { + // Log this so that when using this option on testing devices, there is + // a clear indication if adapters are not properly stopped + LOG(WARNING) << "Adapter use in progress. Waiting for stop based on 'true' " << property; + } + + SetProperty(property, "false"); +} + +int adapterMain(const std::string& package, int argc, char** argv, + const AdaptersFactory& adapters) { + using android::hardware::configureRpcThreadpool; + using android::hidl::base::V1_0::IBase; + using android::hidl::manager::V1_0::IServiceManager; + + const std::string& me = argc > 0 ? argv[0] : "(error)"; + + Args args; + if (!processArguments(argc, argv, &args)) { + return usage(me); + } + + std::string interfaceName = package + "::" + args.interface; + + auto it = adapters.find(interfaceName); + if (it == adapters.end()) { + std::cerr << "ERROR: could not resolve " << interfaceName << "." << std::endl; + return 1; + } + + std::cout << "Trying to adapt down " << interfaceName << "/" << args.instanceName << " to " + << args.registerInstanceName << std::endl; + + configureRpcThreadpool(args.threadNumber, false /* callerWillJoin */); + + sp<IServiceManager> manager = IServiceManager::getService(); + if (manager == nullptr) { + std::cerr << "ERROR: could not retrieve service manager." << std::endl; + return 1; + } + + sp<IBase> implementation = manager->get(interfaceName, args.instanceName).withDefault(nullptr); + if (implementation == nullptr) { + std::cerr << "ERROR: could not retrieve desired implementation" << std::endl; + return 1; + } + + sp<IBase> adapter = it->second(implementation); + if (adapter == nullptr) { + std::cerr << "ERROR: could not create adapter." << std::endl; + return 1; + } + + bool replaced = manager->add(args.registerInstanceName, adapter).withDefault(false); + if (!replaced) { + std::cerr << "ERROR: could not register the service with the service manager." << std::endl; + return 1; + } + + switch (args.stopMethod) { + case StopMethod::NONE: { + std::cout << "Press any key to disassociate adapter." << std::endl; + getchar(); + break; + }; + case StopMethod::SPECIFIC: { + const std::string property = + kDeactivateProp + "." + interfaceName + "." + args.registerInstanceName; + std::cout << "Set " << property << " to true to deactivate." << std::endl; + waitForAdaptersDeactivated(property); + break; + }; + case StopMethod::ALL: { + std::cout << "Set " << kDeactivateProp << " to true to deactivate." << std::endl; + waitForAdaptersDeactivated(kDeactivateProp); + break; + }; + } + + // automatically unregistered on process exit if it is a new instance name + if (args.registerInstanceName == args.instanceName) { + bool restored = manager->add(args.instanceName, implementation).withDefault(false); + if (!restored) { + std::cerr << "ERROR: could not re-register interface with the service manager." + << std::endl; + return 1; + } + } + + std::cout << "Success." << std::endl; + + return 0; +} + +// If an interface is adapted to 1.0, it can then not be adapted to 1.1 in the same process. +// This poses a problem in the following scenario: +// auto interface = new V1_1::implementation::IFoo; +// hidlObject1_0->foo(interface) // adaptation set at 1.0 +// hidlObject1_1->bar(interface) // adaptation still is 1.0 +// This could be solved by keeping a map of IBase,fqName -> IBase, but then you end up +// with multiple names for the same interface. +sp<IBase> adaptWithDefault(const sp<IBase>& something, + const std::function<sp<IBase>()>& makeDefault) { + static std::map<sp<IBase>, sp<IBase>> sAdapterMap; + + if (something == nullptr) { + return something; + } + + auto it = sAdapterMap.find(something); + if (it == sAdapterMap.end()) { + it = sAdapterMap.insert(it, {something, makeDefault()}); + } + + return it->second; +} + +} // namespace details +} // namespace hardware +} // namespace android |