Android源码中添加JNI实战

前言

本文接着上一篇博文Android源码中添加AIDL服务实战,讲解如何在android源码中添加自己的JNI代码,并修改上一篇博文中自定义的服务,实现调用自己添加的JNI函数,最终在相机拍照时调用服务,验证JNI函数是否别正确调用。

JNI是java和C++两种语言直接沟通的桥梁,因为C++语言的优势,android源码中很多底层功能会使用C++实现,然后通过JNI让上层的java语言调用。这里不过多赘述,本篇博文和上一篇博文紧密联系,因此,建议阅读本篇文章前先阅读上篇博文:
Android源码中添加AIDL服务实战

一、添加JNI

1. 编写JNI代码

在frameworks/base/services/core/jni目录下添加com_android_server_HelloworldService.cpp

具体代码可以参考该目录下其它程序,结构比较固定,照葫芦画瓢就可以,主要包括以下三部分:

  1. 自定义的功能函数,后续要被上层调用
  2. 将自定义函数添加到数组static JNINativeMethod sMethods[]中
  3. 提供注册函数register_android_server_*,后续在onload.cpp中调用,具体实现很固定,可以参考其它程序,主要是jniRegisterNativeMethods函数调用
#define LOG_TAG "MyCustomService"
#include <nativehelper/JNIHelp.h>
#include "jni.h"
#include "android_runtime/AndroidRuntime.h"
#include "utils/misc.h"
#include "utils/Log.h"
#include "stdio.h"

namespace android{
    // 实现加法功能函数,后续被自定义服务调用
    static jint myCustom_AddVal(JNIEnv *env, jobject clazz, jint a, jint b) {
        return a+b;
    }

    // 必须有的,注册函数到jni
    static JNINativeMethod sMethods[] = {
            {"addValue", "(II)I", (void *) myCustom_AddVal},
    };

    // 必须有的
    int register_android_server_MyCustomService(JNIEnv *env) {
        int res = jniRegisterNativeMethods(env, "com/android/server/MyCustomService", sMethods,
                                           NELEM(sMethods));
        if (res != 0) {
            return -1;
        }
        return 0;
    }

}

2. 在Android.bp文件中添加上面编写的JNI代码

在frameworks/base/services/core/jni/Android.bp中添加上面新增上面编写的jni代码文件,照葫芦画瓢即可。

cc_library_static {
    name: "libservices.core",
    defaults: ["libservices.core-libs"],

    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-unused-parameter",
        "-Wthread-safety",

        "-DEGL_EGLEXT_PROTOTYPES",
        "-DGL_GLEXT_PROTOTYPES",
    ],

    srcs: [
        "BroadcastRadio/JavaRef.cpp",
        "BroadcastRadio/NativeCallbackThread.cpp",
        "BroadcastRadio/BroadcastRadioService.cpp",
        "BroadcastRadio/Tuner.cpp",
        "BroadcastRadio/TunerCallback.cpp",
        "BroadcastRadio/convert.cpp",
        "BroadcastRadio/regions.cpp",
        "com_android_server_AlarmManagerService.cpp",
        "com_android_server_am_BatteryStatsService.cpp",
        "com_android_server_connectivity_Vpn.cpp",
        "com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
        "com_android_server_ConsumerIrService.cpp",
        "com_android_server_devicepolicy_CryptoTestHelper.cpp",
        "com_android_server_HardwarePropertiesManagerService.cpp",
        "com_android_server_hdmi_HdmiCecController.cpp",
        "com_android_server_input_InputManagerService.cpp",
        "com_android_server_lights_LightsService.cpp",
        "com_android_server_location_GnssLocationProvider.cpp",
        "com_android_server_locksettings_SyntheticPasswordManager.cpp",
        "com_android_server_net_NetworkStatsService.cpp",
        "com_android_server_power_PowerManagerService.cpp",
        "com_android_server_security_VerityUtils.cpp",
        "com_android_server_SerialService.cpp",
        "com_android_server_storage_AppFuseBridge.cpp",
        "com_android_server_SystemServer.cpp",
        "com_android_server_TestNetworkService.cpp",
        "com_android_server_tv_TvUinputBridge.cpp",
        "com_android_server_tv_TvInputHal.cpp",
        "com_android_server_vr_VrManagerService.cpp",
        "com_android_server_UsbAlsaJackDetector.cpp",
        "com_android_server_UsbDeviceManager.cpp",
        "com_android_server_UsbDescriptorParser.cpp",
        "com_android_server_UsbMidiDevice.cpp",
        "com_android_server_UsbHostManager.cpp",
        "com_android_server_VibratorService.cpp",
        "com_android_server_PersistentDataBlockService.cpp",
        "com_android_server_GraphicsStatsService.cpp",
        "com_android_server_am_AppCompactor.cpp",
        "com_android_server_am_LowMemDetector.cpp",
        "com_android_server_MyCustomService.cpp", // 添加我们编写的JNI代码文件
        "onload.cpp",
        ":lib_networkStatsFactory_native",
    ],

    ......

3. 注册jni

在frameworks/base/services/core/jni/onload.cpp中注册jni,比葫芦画瓢,随便找个JNI代码实现参照着写即可。

/*
 * Copyright (C) 2009 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 <nativehelper/JNIHelp.h>
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"

#include "BroadcastRadio/BroadcastRadioService.h"
#include "BroadcastRadio/Tuner.h"

namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryStatsService(JNIEnv* env);
int register_android_server_ConsumerIrService(JNIEnv *env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_storage_AppFuse(JNIEnv* env);
int register_android_server_SerialService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_UsbAlsaJackDetector(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbMidiDevice(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_vr_VrManagerService(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_location_GnssLocationProvider(JNIEnv* env);
int register_android_server_connectivity_Vpn(JNIEnv* env);
int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv*);
int register_android_server_TestNetworkService(JNIEnv* env);
int register_android_server_devicepolicy_CryptoTestHelper(JNIEnv*);
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_tv_TvUinputBridge(JNIEnv* env);
int register_android_server_tv_TvInputHal(JNIEnv* env);
int register_android_server_PersistentDataBlockService(JNIEnv* env);
int register_android_server_Watchdog(JNIEnv* env);
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_server_GraphicsStatsService(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
int register_android_server_net_NetworkStatsService(JNIEnv* env);
int register_android_server_security_VerityUtils(JNIEnv* env);
int register_android_server_am_AppCompactor(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
// 定义我们的注册函数,重点在这儿,重点在这儿,重点在这儿,重点在这儿
int register_android_server_MyCustomService(JNIEnv* env);
};

using namespace android;

extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        ALOGE("GetEnv failed!");
        return result;
    }
    ALOG_ASSERT(env, "Could not retrieve the env!");

    register_android_server_broadcastradio_BroadcastRadioService(env);
    register_android_server_broadcastradio_Tuner(vm, env);
    register_android_server_PowerManagerService(env);
    register_android_server_SerialService(env);
    register_android_server_InputManager(env);
    register_android_server_LightsService(env);
    register_android_server_AlarmManagerService(env);
    register_android_server_UsbDeviceManager(env);
    register_android_server_UsbMidiDevice(env);
    register_android_server_UsbAlsaJackDetector(env);
    register_android_server_UsbHostManager(env);
    register_android_server_vr_VrManagerService(env);
    register_android_server_VibratorService(env);
    register_android_server_SystemServer(env);
    register_android_server_location_GnssLocationProvider(env);
    register_android_server_connectivity_Vpn(env);
    register_android_server_connectivity_tethering_OffloadHardwareInterface(env);
    register_android_server_TestNetworkService(env);
    register_android_server_devicepolicy_CryptoTestHelper(env);
    register_android_server_ConsumerIrService(env);
    register_android_server_BatteryStatsService(env);
    register_android_server_hdmi_HdmiCecController(env);
    register_android_server_tv_TvUinputBridge(env);
    register_android_server_tv_TvInputHal(env);
    register_android_server_PersistentDataBlockService(env);
    register_android_server_HardwarePropertiesManagerService(env);
    register_android_server_storage_AppFuse(env);
    register_android_server_SyntheticPasswordManager(env);
    register_android_server_GraphicsStatsService(env);
    register_android_hardware_display_DisplayViewport(env);
    register_android_server_net_NetworkStatsFactory(env);
    register_android_server_net_NetworkStatsService(env);
    register_android_server_security_VerityUtils(env);
    register_android_server_am_AppCompactor(env);
    register_android_server_am_LowMemDetector(env);
    // 调用我们的注册函数,重点在这儿,重点在这儿,重点在这儿,重点在这儿
    register_android_server_MyCustomService(env);
    return JNI_VERSION_1_4;
}

4. 编译JNI

 mmm -j20 frameworks/base/services/core/jni/

在这里插入图片描述

二、在服务中调用jni

在上一篇博客Android源码中添加AIDL服务实战,添加的自定义服务的基础上,进行修改。

1. 添加服务函数

修改/frameworks/base/core/java/android/os/IMyCustomService.aidl,添加myAdd服务函数。

package android.os;

/** @hide */
interface IMyCustomService{
    // 带参数的示例
    String processData(String input);
    int myAdd(int a,int b);
}

2. 服务函数调用jni

修改frameworks/base/services/core/java/com/android/server/MyCustomService.java,实现myAdd服务函数。

package com.android.server;

import android.os.IMyCustomService;
import android.os.RemoteException;
import android.util.Slog;

import java.lang.Override;
import java.lang.String;

public class MyCustomService extends IMyCustomService.Stub {
    private static final String TAG = "MyCustomService";

    public MyCustomService() {
        Slog.d(TAG, "Initialized");
    }

    @Override
    public String processData(String input) throws RemoteException {
        Slog.d(TAG, "Processing data: " + input);
        return "Processed: " + input;
    }
    
    @Override
    public int myAdd(int a,int b){
        // 调用我们上面用C++编写的JNI函数
        int ans = addValue(a, b);
        Slog.d(TAG, "myAdd a + b = " + a + " + " + b + " = " + ans);
        return ans;
    }
    // 添加native函数,具体实现就是我们上面JNI的实现
    private static native int addValue(int a, int b);
}

3. 编译服务

mmm frameworks/base/ -j8

三、在相机代码中使用服务

1. 修改相机代码

修改packages/apps/Camera2/src/com/android/camera/CameraActivity.java,在mSessionListener的回调函数onSessionQueued中调用自定义服务my_custom_service中的myAdd函数。
在这里插入图片描述

try {
//                        IBinder binder = ServiceManager.getService("my_custom_service");
//                        IMyCustomService service = IMyCustomService.Stub.asInterface(binder);
    Class<?> smClass = Class.forName("android.os.ServiceManager");
    Method getService = smClass.getMethod("getService", String.class);
    IBinder binder = (IBinder) getService.invoke(null, "my_custom_service");
    IMyCustomService service = IMyCustomService.Stub.asInterface(binder);
    String ans = service.processData("mekeater");
    Log.v(TAG, "IMyCustomService processData: " + ans);
    // 调用服务中的myAdd函数,而该函数具体实现是调用JNI函数
    int add = service.myAdd(6,6);
    Log.v(TAG, "IMyCustomService myAdd: " + add);
} catch (Exception e) {
    Log.e(TAG, "call my_custom_service fail: ", e);
}

2. 编译相机

mmm packages/apps/Camera2/ -j8

四、打开虚拟机验证功能

emulator -writable-system

1. 桌面启动不了问题解决

  1. 相机权限缺失问题
    因为相机缺少android.permission.BIND_WALLPAPER权限,导致桌面起不来
07-03 10:08:03.854  2044  2044 E AndroidRuntime: java.lang.IllegalStateException: Signature|privileged permissions not in privapp-permissions whitelist: {com.android.camera2: android.permission.BIND_WALLPAPER}
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.server.pm.permission.PermissionManagerService.systemReady(PermissionManagerService.java:2970)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.server.pm.permission.PermissionManagerService.access$100(PermissionManagerService.java:122)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.server.pm.permission.PermissionManagerService$PermissionManagerServiceInternalImpl.systemReady(PermissionManagerService.java:3031)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.server.pm.PackageManagerService.systemReady(PackageManagerService.java:21785)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.server.SystemServer.startOtherServices(SystemServer.java:2001)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.server.SystemServer.run(SystemServer.java:514)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.server.SystemServer.main(SystemServer.java:351)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
07-03 10:08:03.854  2044  2044 E AndroidRuntime: 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:908)

在/frameworks/base/data/etc/privapp-permissions-platform.xml中添加该权限,重新编译framework/base
在这里插入图片描述

 <permissions>
        <privapp-permissions package="com.android.camera2">
            <permission name="android.permission.BIND_WALLPAPER"/>
        </privapp-permissions>
 </permissions>
  1. 解决完上面的问题后,你会发现systemui又开始缺各种权限了,我懒得修改了,反正我们是验证,直接把抛异常的代码给干掉,让桌面跑起来,(但桌面可能有点异常,有黑屏卡顿现象,迁就着能用)

修改
frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.java,把systemReady函数中权限检查的异常给注释掉,重新编译framework/base
在这里插入图片描述
3. 再启动虚拟机,执行以下命令

adb root
adb remount
adb sync
adb reboot

2. 将上面新编译的camera2.apk push到系统目录

adb root
adb remout
adb push out/target/product/generic_x86_64/system/product/priv-app/Camera2/Camera2.apk /system/product/app/Camera2/Camera2.apk
adb reboot

3. 关闭SELinux权限

adb root
adb remount
adb shell setenforce 0

4. 相机打不开问题解决

打开相机,这时候会报权限问题,相机打不开,从打开setting中打开相机,将相机位置权限打开,就可以正常打开相机了
在这里插入图片描述

5. 点击拍照,查看log,确认功能是否正常

在这里插入图片描述
可以看到myAdd函数功能正常,说明我们添加的JNI函数正常生效了。

后记

如何添加JNI,其实只有本博文中第一章的三个步骤,还是比较简单,比葫芦画瓢即可。但是在验证的过程中,会遇到各种问题,我们要学会解决修改android源码后,遇到的各种问题,可以通过查看log,找到问题原因,然后去问AI(如DeepSeek),相信在解决问题的过程中,就有自己越来越灵活的解决思路了。学习android源码的路上,我们一起加油。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mekeater

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值