blob: 22f5d73ba1bbc54debfbe2844d786838a19f3851 [file] [edit]
/*
* Copyright (C) 2022 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_TAG "ImsStackN"
#define LOG_NIDEBUG 0
#define LOG_NDDEBUG 0
#include <string.h>
#include <stdio.h>
#include <android/file_descriptor_jni.h>
#include <binder/Parcel.h>
#include <nativehelper/JNIHelp.h>
#include <utils/Log.h>
#include <utils/threads.h>
#include "DeviceConfig.h"
#include "INativeThreadMethods.h"
#include "ServiceThread.h"
#include "ServiceTrace.h"
#include "CoreInterfaceFactory.h"
#include "ImsMain.h"
#include "JniObjectId.h"
#include "JniSystem.h"
#include "NativeCommands.h"
using namespace android;
__IMS_TRACE_TAG_USER_DECL__("JNI");
#define JNI_IMS_OK (0)
#define JNI_IMS_ERROR (-1)
static jclass s_classJniIms;
static jmethodID s_methodSendDataToJava;
static jmethodID s_methodSendDataToJavaForSystem;
static const char* s_szClassJniImsPath = "com/android/imsstack/jni/JniIms";
static JavaVM* s_javaVm = NULL;
static JavaVM* GetJavaVm()
{
return s_javaVm;
}
static int SendDataToJava(long nNativeObject, const android::Parcel& objParcel)
{
JNIEnv* env;
jlong jNativeObject = nNativeObject;
#if defined(__JNI_DEBUG__)
IMS_TRACE_D("SendDataToJava: object=%" PFLS_x, nNativeObject, 0, 0);
#endif
if ((s_classJniIms == NULL) || (s_methodSendDataToJava == NULL))
{
IMS_TRACE_E(0, "SendDataToJava: Method is null", 0, 0, 0);
return 0;
}
JavaVM* jvm = GetJavaVm();
if (jvm->AttachCurrentThread(&env, NULL) != JNI_OK)
{
IMS_TRACE_E(0, "SendDataToJava: AttachCurrentThread fail", 0, 0, 0);
return 0;
}
jbyteArray jData = env->NewByteArray(objParcel.dataSize());
jbyte* pBuffer = env->GetByteArrayElements(jData, NULL);
if (pBuffer != NULL)
{
memcpy(pBuffer, objParcel.data(), objParcel.dataSize());
env->ReleaseByteArrayElements(jData, pBuffer, 0);
env->CallStaticIntMethod(s_classJniIms, s_methodSendDataToJava, jNativeObject, jData);
}
env->DeleteLocalRef(jData);
return 1;
}
static int SendDataToJavaForSystem(
long nNativeObject, const android::Parcel& parcelIn, android::Parcel& parcelOut, int fd)
{
JNIEnv* env;
jlong jNativeObject = nNativeObject;
if ((s_classJniIms == NULL) || (s_methodSendDataToJavaForSystem == NULL))
{
IMS_TRACE_E(0, "SendDataToJavaForSystem: Method is null", 0, 0, 0);
return 0;
}
JavaVM* jvm = GetJavaVm();
if (jvm->AttachCurrentThread(&env, NULL) != JNI_OK)
{
IMS_TRACE_E(0, "SendDataToJavaForSystem: AttachCurrentThread fail", 0, 0, 0);
return 0;
}
jbyteArray jResultData = NULL;
jbyteArray jData = env->NewByteArray(parcelIn.dataSize());
jbyte* pBuffer = env->GetByteArrayElements(jData, NULL);
if (pBuffer != NULL)
{
memcpy(pBuffer, parcelIn.data(), parcelIn.dataSize());
env->ReleaseByteArrayElements(jData, pBuffer, 0);
jobject fileDescriptor = null;
if (fd > 0)
{
fileDescriptor = AFileDescriptor_create(env);
AFileDescriptor_setFd(env, fileDescriptor, fd);
}
jResultData = (jbyteArray)env->CallStaticObjectMethod(s_classJniIms,
s_methodSendDataToJavaForSystem, jNativeObject, jData, fileDescriptor);
}
env->DeleteLocalRef(jData);
if (jResultData == NULL)
{
IMS_TRACE_I("SendDataToJavaForSystem: Result is null", 0, 0, 0);
parcelOut.writeInt32(0);
parcelOut.setDataPosition(0);
return 1;
}
pBuffer = env->GetByteArrayElements(jResultData, NULL);
if (pBuffer != NULL)
{
int nBuffSize = env->GetArrayLength(jResultData);
if (nBuffSize == 0)
{
IMS_TRACE_D("SendDataToJavaForSystem: Result(buffer-length) is zero", 0, 0, 0);
parcelOut.writeInt32(1);
}
else
{
parcelOut.setData(reinterpret_cast<const uint8_t*>(pBuffer), nBuffSize);
}
env->ReleaseByteArrayElements(jResultData, pBuffer, 0);
}
else
{
IMS_TRACE_D("SendDataToJavaForSystem: Result(buffer) is null", 0, 0, 0);
parcelOut.writeInt32(0);
}
env->DeleteLocalRef(jResultData);
parcelOut.setDataPosition(0);
return 1;
}
static IMS_UINTP GetCommandParam(IN JNIEnv* env, IN jint cmd, IN jbyteArray jData)
{
if (jData == NULL)
{
return 0;
}
jbyte* pData = env->GetByteArrayElements(jData, NULL);
int nDataLen = env->GetArrayLength(jData);
if (nDataLen == 0)
{
env->ReleaseByteArrayElements(jData, pData, 0);
return 0;
}
android::Parcel objData;
objData.setData(reinterpret_cast<const uint8_t*>(pData), nDataLen);
objData.setDataPosition(0);
env->ReleaseByteArrayElements(jData, pData, 0);
IMS_UINTP pnParam = 0;
switch (cmd)
{
case NativeCommands::CMD_SET_DEVICE_CONFIG:
{
__DeviceConfig* pConfig = new __DeviceConfig();
pConfig->nSupportedSimCount = objData.readInt32();
pConfig->nActiveSimCount = objData.readInt32();
pConfig->nImsEmergencyEnabled = objData.readInt32();
pConfig->nVoLteEnabled = objData.readInt32();
pConfig->nVtEnabled = objData.readInt32();
pConfig->nWfcEnabled = objData.readInt32();
pnParam = reinterpret_cast<IMS_UINTP>(pConfig);
break;
}
default:
// no-op
break;
}
return pnParam;
}
static void ReleaseCommandParam(IN jint cmd, IN IMS_UINTP pnParam)
{
if (pnParam == 0)
{
return;
}
switch (cmd)
{
case NativeCommands::CMD_SET_DEVICE_CONFIG:
{
__DeviceConfig* pConfig = reinterpret_cast<__DeviceConfig*>(pnParam);
delete pConfig;
break;
}
default:
// no-op
break;
}
}
static void JniAttachNativeThread(const char* threadName)
{
IMS_TRACE_D("JniAttachNativeThread: name=%s", threadName, 0, 0);
JavaVM* jvm = GetJavaVm();
if (jvm == NULL)
{
return;
}
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_4;
args.name = threadName;
args.group = NULL;
JNIEnv* env;
jint result = jvm->AttachCurrentThread(&env, static_cast<void*>(&args));
if (result != JNI_OK)
{
IMS_TRACE_E(0, "JniAttachNativeThread: Attach failed - %s(%d)", __IMS_FUNC__, result, 0);
return;
}
}
static void JniDetachNativeThread()
{
#if defined(__JNI_DEBUG__)
IMS_TRACE_D("JniDetachNativeThread:", 0, 0, 0);
#endif
JavaVM* jvm = GetJavaVm();
if (jvm == NULL)
{
return;
}
jint result = jvm->DetachCurrentThread();
if (result != JNI_OK)
{
IMS_TRACE_E(0, "JniDetachNativeThread : Detach failed", 0, 0, 0);
return;
}
}
class NativeThreadMethods : public INativeThreadMethods
{
public:
inline NativeThreadMethods() {}
~NativeThreadMethods() override = default;
NativeThreadMethods(const NativeThreadMethods&) = delete;
NativeThreadMethods& operator=(const NativeThreadMethods&) = delete;
public:
inline void AttachNativeThread(IN const IMS_CHAR* pszName) override
{
JniAttachNativeThread(pszName);
}
inline void DetachNativeThread() override { JniDetachNativeThread(); }
};
static NativeThreadMethods s_objNativeThreadMethods;
static void JniIms_nativeInit(JNIEnv* /*env*/, jobject /*object*/)
{
IMS_TRACE_I("JniIms_nativeInit", 0, 0, 0);
ImsMain::Initialize();
ThreadService::SetNativeThreadMethods(&s_objNativeThreadMethods);
ImsMain::Start();
}
static void JniIms_nativeDeInit(JNIEnv* /*env*/, jobject /*object*/)
{
IMS_TRACE_I("JniIms_nativeDeInit", 0, 0, 0);
ImsMain::Stop();
ImsMain::Uninitialize();
}
static int JniIms_nativeSendCommand(
JNIEnv* env, jobject /*object*/, jint cmd, jint slotId, jbyteArray jData)
{
IMS_UINTP pnParam = GetCommandParam(env, cmd, jData);
ImsMain::SendCommand(cmd, slotId, pnParam);
ReleaseCommandParam(cmd, pnParam);
return JNI_IMS_OK;
}
static jlong JniIms_nativeGetInterface(
JNIEnv* /*env*/, jobject /*object*/, jint interfaceType, jint slotId)
{
BaseService* pService = IMS_NULL;
if (interfaceType == JniObjectId::SYSTEM)
{
pService = new JniSystem(SendDataToJavaForSystem);
}
else
{
pService = CoreInterfaceFactory::GetInterface(interfaceType, SendDataToJava, slotId);
}
IMS_TRACE_I("JniIms_nativeGetInterface: interface=%d, object=%p", interfaceType, pService, 0);
return static_cast<jlong>(reinterpret_cast<long>(pService));
}
static void JniIms_nativeReleaseInterface(JNIEnv* /*env*/, jobject /*object*/, jlong jNativeObject)
{
long nNativeObject = INT64_TO_SINTP(jNativeObject);
#if defined(__IMS_CLANG__)
IMS_TRACE_I("JniIms_nativeReleaseInterface: object=%lx", nNativeObject, 0, 0);
#else
IMS_TRACE_I("JniIms_nativeReleaseInterface: object=%" PFLS_x, nNativeObject, 0, 0);
#endif
BaseService* pService = reinterpret_cast<BaseService*>(nNativeObject);
if (pService != IMS_NULL)
{
pService->Destroy();
}
}
static jint JniIms_nativeSendData(
JNIEnv* env, jobject /*object*/, jlong jNativeObject, jbyteArray jData)
{
long nNativeObject = INT64_TO_SINTP(jNativeObject);
#if defined(__JNI_DEBUG__)
IMS_TRACE_D("JniIms_nativeSendData: object=%" PFLS_x, nNativeObject, 0, 0);
#endif
BaseService* pService = reinterpret_cast<BaseService*>(nNativeObject);
if (pService == IMS_NULL)
{
IMS_TRACE_D("JniIms_nativeSendData: BaseService is null", 0, 0, 0);
return JNI_IMS_ERROR;
}
jbyte* pBuff = env->GetByteArrayElements(jData, NULL);
int nBuffSize = env->GetArrayLength(jData);
android::Parcel parcel;
parcel.setData(reinterpret_cast<const uint8_t*>(pBuff), nBuffSize);
parcel.setDataPosition(0);
int nResult = pService->SendData(parcel);
env->ReleaseByteArrayElements(jData, pBuff, 0);
return (nResult == 1) ? JNI_IMS_OK : JNI_IMS_ERROR;
}
static jbyteArray JniIms_nativeSendDataForSystem(
JNIEnv* env, jobject /*object*/, jlong jNativeObject, jbyteArray jData)
{
long nNativeObject = INT64_TO_SINTP(jNativeObject);
android::Parcel parcelOut;
BaseService* pService = reinterpret_cast<BaseService*>(nNativeObject);
if (pService == IMS_NULL)
{
IMS_TRACE_D("JniIms_nativeSendDataForSystem: BaseService is null", 0, 0, 0);
}
else
{
android::Parcel parcel;
jbyte* pBuff = env->GetByteArrayElements(jData, NULL);
int nBuffSize = env->GetArrayLength(jData);
parcel.setData(reinterpret_cast<const uint8_t*>(pBuff), nBuffSize);
parcel.setDataPosition(0);
pService->SendData(parcel, parcelOut);
env->ReleaseByteArrayElements(jData, pBuff, 0);
}
if (parcelOut.dataSize() == 0) // error case
{
parcelOut.writeInt32(1);
}
jbyteArray byteArray = env->NewByteArray(parcelOut.dataSize());
env->SetByteArrayRegion(byteArray, 0, parcelOut.dataSize(),
reinterpret_cast<jbyte*>(const_cast<uint8_t*>(parcelOut.data())));
return byteArray;
}
// clang-format off
static JNINativeMethod s_jniImsMethods[] = {
/* name, signature, funcPtr */
{ "nativeInit", "()V", (void*)JniIms_nativeInit },
{ "nativeDeInit", "()V", (void*)JniIms_nativeDeInit },
{ "nativeGetInterface", "(II)J", (void*)JniIms_nativeGetInterface },
{ "nativeReleaseInterface", "(J)V", (void*)JniIms_nativeReleaseInterface },
{ "nativeSendData", "(J[B)I", (void*)JniIms_nativeSendData },
{ "nativeSendDataForSystem", "(J[B)[B", (void*)JniIms_nativeSendDataForSystem },
{ "nativeSendCommand", "(II[B)I", (void*)JniIms_nativeSendCommand },
};
// clang-format on
jint IMSInterface_OnLoad(JavaVM* vm, JNIEnv* env)
{
s_javaVm = vm;
jclass jclassIms = env->FindClass(s_szClassJniImsPath);
if (jclassIms == NULL)
{
ALOGE("IMSInterface_OnLoad: FindClass failed");
return -1;
}
s_classJniIms = (jclass)env->NewGlobalRef(jclassIms);
if (jniRegisterNativeMethods(
env, s_szClassJniImsPath, s_jniImsMethods, NELEM(s_jniImsMethods)) < 0)
{
ALOGE("IMSInterface_OnLoad: RegisterNatives failed");
return -1;
}
s_methodSendDataToJava = env->GetStaticMethodID(s_classJniIms, "sendDataToJava", "(J[B)I");
s_methodSendDataToJavaForSystem = env->GetStaticMethodID(
s_classJniIms, "sendDataToJavaForSystem", "(J[BLjava/io/FileDescriptor;)[B");
if ((s_methodSendDataToJava == NULL) || (s_methodSendDataToJavaForSystem == NULL))
{
ALOGE("IMSInterface_OnLoad: GetStaticMethodID failed");
return -1;
}
return JNI_VERSION_1_4;
}