/* * 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_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 & TF_ONE_WAY) != 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(); // It is illegal to call IsInstanceOf if there is a pending exception. // Attempting to do so results in a JniAbort which crashes the entire process. if (env->IsInstanceOf(excep, gErrorClass)) { /* It's an error */ LOG(ERROR) << "Forcefully exiting"; exit(1); } else { LOG(ERROR) << "Uncaught exception!"; } 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; } } // 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); /* TODO(b/33440494) this is not right */ sp base = new hidl::base::V1_0::BpHwBase(binder); auto manager = hardware::defaultServiceManager(); if (manager == nullptr) { LOG(ERROR) << "Could not get hwservicemanager."; signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); return; } Return ret = manager->add(str.c_str(), base); bool ok = ret.isOk() && ret; if (ok) { LOG(INFO) << "HwBinder: Starting thread pool for " << str.c_str(); ::android::hardware::ProcessState::self()->startThreadPool(); } 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 == NULL) { signalExceptionForError(env, NAME_NOT_FOUND); return NULL; } LOG(INFO) << "HwBinder: Starting thread pool for getting: " << ifaceName << "/" << serviceName; ::android::hardware::ProcessState::self()->startThreadPool(); return JHwRemoteBinder::NewObject(env, service); } 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 }, { "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