/* * Copyright (C) 2016 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. */ //#define LOG_NDEBUG 0 #define LOG_TAG "android_os_HwBinder" #include #include "android_os_HwBinder.h" #include "android_util_Binder.h" // for binder_report_exception #include "android_os_HwParcel.h" #include "android_os_HwRemoteBinder.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "core_jni_helpers.h" using android::AndroidRuntime; using android::hardware::hidl_vec; using android::hardware::hidl_string; using android::hardware::IPCThreadState; using android::hardware::ProcessState; template using Return = android::hardware::Return; #define PACKAGE_PATH "android/os" #define CLASS_NAME "HwBinder" #define CLASS_PATH PACKAGE_PATH "/" CLASS_NAME namespace android { static jclass gErrorClass; static struct fields_t { jfieldID contextID; jmethodID onTransactID; } gFields; struct JHwBinderHolder : public RefBase { JHwBinderHolder() {} sp get(JNIEnv *env, jobject obj) { Mutex::Autolock autoLock(mLock); sp binder = mBinder.promote(); if (binder == NULL) { binder = new JHwBinder(env, obj); mBinder = binder; } return binder; } private: Mutex mLock; wp mBinder; DISALLOW_COPY_AND_ASSIGN(JHwBinderHolder); }; // static void JHwBinder::InitClass(JNIEnv *env) { ScopedLocalRef clazz( env, FindClassOrDie(env, CLASS_PATH)); gFields.contextID = GetFieldIDOrDie(env, clazz.get(), "mNativeContext", "J"); gFields.onTransactID = GetMethodIDOrDie( env, clazz.get(), "onTransact", "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V"); } // static sp JHwBinder::SetNativeContext( JNIEnv *env, jobject thiz, const sp &context) { sp old = (JHwBinderHolder *)env->GetLongField(thiz, gFields.contextID); if (context != NULL) { context->incStrong(NULL /* id */); } if (old != NULL) { old->decStrong(NULL /* id */); } env->SetLongField(thiz, gFields.contextID, (long)context.get()); return old; } // static sp JHwBinder::GetNativeBinder( JNIEnv *env, jobject thiz) { JHwBinderHolder *holder = reinterpret_cast( env->GetLongField(thiz, gFields.contextID)); return holder->get(env, thiz); } JHwBinder::JHwBinder(JNIEnv *env, jobject thiz) { jclass clazz = env->GetObjectClass(thiz); CHECK(clazz != NULL); mObject = env->NewGlobalRef(thiz); } JHwBinder::~JHwBinder() { JNIEnv *env = AndroidRuntime::getJNIEnv(); env->DeleteGlobalRef(mObject); mObject = NULL; } status_t JHwBinder::onTransact( uint32_t code, const hardware::Parcel &data, hardware::Parcel *reply, uint32_t flags, TransactCallback callback) { JNIEnv *env = AndroidRuntime::getJNIEnv(); bool isOneway = (flags & IBinder::FLAG_ONEWAY) != 0; ScopedLocalRef replyObj(env, nullptr); sp replyContext = nullptr; ScopedLocalRef requestObj(env, JHwParcel::NewObject(env)); JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( const_cast(&data), false /* assumeOwnership */); if (!isOneway) { replyObj.reset(JHwParcel::NewObject(env)); replyContext = JHwParcel::GetNativeContext(env, replyObj.get()); replyContext->setParcel(reply, false /* assumeOwnership */); replyContext->setTransactCallback(callback); } env->CallVoidMethod( mObject, gFields.onTransactID, code, requestObj.get(), replyObj.get(), flags); if (env->ExceptionCheck()) { jthrowable excep = env->ExceptionOccurred(); env->ExceptionDescribe(); env->ExceptionClear(); binder_report_exception(env, excep, "Uncaught error or exception in hwbinder!"); env->DeleteLocalRef(excep); } status_t err = OK; if (!isOneway) { if (!replyContext->wasSent()) { // The implementation never finished the transaction. err = UNKNOWN_ERROR; // XXX special error code instead? reply->setDataPosition(0 /* pos */); } // Release all temporary storage now that scatter-gather data // has been consolidated, either by calling the TransactCallback, // if wasSent() == true or clearing the reply parcel (setDataOffset above). replyContext->getStorage()->release(env); // We cannot permanently pass ownership of "data" and "reply" over to their // Java object wrappers (we don't own them ourselves). replyContext->setParcel( NULL /* parcel */, false /* assumeOwnership */); } JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( NULL /* parcel */, false /* assumeOwnership */); return err; } bool validateCanUseHwBinder(const sp& binder) { if (binder != nullptr && binder->localBinder() != nullptr) { // untested/unsupported/inefficient // see b/129150021, doesn't work with scatter-gather // // explicitly disabling until it is supported // (note, even if this is fixed to work with scatter gather, we would also need // to convert this to the Java object rather than re-wrapping with a proxy) LOG(ERROR) << "Local Java Binder not supported."; return false; } return true; } } // namespace android //////////////////////////////////////////////////////////////////////////////// using namespace android; static void releaseNativeContext(void *nativeContext) { sp context = static_cast(nativeContext); if (context != NULL) { context->decStrong(NULL /* id */); } } static jlong JHwBinder_native_init(JNIEnv *env) { JHwBinder::InitClass(env); return reinterpret_cast(&releaseNativeContext); } static void JHwBinder_native_setup(JNIEnv *env, jobject thiz) { sp context = new JHwBinderHolder; JHwBinder::SetNativeContext(env, thiz, context); } static void JHwBinder_native_transact( JNIEnv * /* env */, jobject /* thiz */, jint /* code */, jobject /* requestObj */, jobject /* replyObj */, jint /* flags */) { CHECK(!"Should not be here"); } static void JHwBinder_native_registerService( JNIEnv *env, jobject thiz, jstring serviceNameObj) { ScopedUtfChars str(env, serviceNameObj); if (str.c_str() == nullptr) { return; // NPE will be pending. } sp binder = JHwBinder::GetNativeBinder(env, thiz); sp base = new hidl::base::V1_0::BpHwBase(binder); bool ok = hardware::details::registerAsServiceInternal(base, str.c_str()) == OK; if (ok) { LOG(INFO) << "HwBinder: Starting thread pool for " << str.c_str(); ::android::hardware::ProcessState::self()->startThreadPool(); } // avoiding richer error exceptions to stick with legacy behavior signalExceptionForError(env, (ok ? OK : UNKNOWN_ERROR), true /*canThrowRemoteException*/); } static jobject JHwBinder_native_getService( JNIEnv *env, jclass /* clazzObj */, jstring ifaceNameObj, jstring serviceNameObj, jboolean retry) { using ::android::hidl::base::V1_0::IBase; using ::android::hardware::details::getRawServiceInternal; std::string ifaceName; { ScopedUtfChars str(env, ifaceNameObj); if (str.c_str() == nullptr) { return nullptr; // NPE will be pending. } ifaceName = str.c_str(); } std::string serviceName; { ScopedUtfChars str(env, serviceNameObj); if (str.c_str() == nullptr) { return nullptr; // NPE will be pending. } serviceName = str.c_str(); } sp ret = getRawServiceInternal(ifaceName, serviceName, retry /* retry */, false /* getStub */); sp service = hardware::toBinder(ret); if (service == nullptr || !validateCanUseHwBinder(service)) { signalExceptionForError(env, NAME_NOT_FOUND); return nullptr; } LOG(INFO) << "HwBinder: Starting thread pool for getting: " << ifaceName << "/" << serviceName; ::android::hardware::ProcessState::self()->startThreadPool(); return JHwRemoteBinder::NewObject(env, service); } void JHwBinder_native_setTrebleTestingOverride(JNIEnv*, jclass, jboolean testingOverride) { hardware::details::setTrebleTestingOverride(testingOverride); } void JHwBinder_native_configureRpcThreadpool(JNIEnv *, jclass, jlong maxThreads, jboolean callerWillJoin) { CHECK(maxThreads > 0); ProcessState::self()->setThreadPoolConfiguration(maxThreads, callerWillJoin /*callerJoinsPool*/); } void JHwBinder_native_joinRpcThreadpool() { IPCThreadState::self()->joinThreadPool(); } static void JHwBinder_report_sysprop_change(JNIEnv * /*env*/, jclass /*clazz*/) { report_sysprop_change(); } static JNINativeMethod gMethods[] = { { "native_init", "()J", (void *)JHwBinder_native_init }, { "native_setup", "()V", (void *)JHwBinder_native_setup }, { "transact", "(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V", (void *)JHwBinder_native_transact }, { "registerService", "(Ljava/lang/String;)V", (void *)JHwBinder_native_registerService }, { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;", (void *)JHwBinder_native_getService }, { "setTrebleTestingOverride", "(Z)V", (void *)JHwBinder_native_setTrebleTestingOverride }, { "configureRpcThreadpool", "(JZ)V", (void *)JHwBinder_native_configureRpcThreadpool }, { "joinRpcThreadpool", "()V", (void *)JHwBinder_native_joinRpcThreadpool }, { "native_report_sysprop_change", "()V", (void *)JHwBinder_report_sysprop_change }, }; namespace android { int register_android_os_HwBinder(JNIEnv *env) { jclass errorClass = FindClassOrDie(env, "java/lang/Error"); gErrorClass = MakeGlobalRefOrDie(env, errorClass); return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods)); } } // namespace android