提供 NvramService 给 AS 直接调用(基于android10)

简介: 提供 NvramService 给 AS 直接调用(基于android10)

最近对 Nvram 研究上瘾了,要读写 nvram 需要在安卓源码中编译才能调用相关 API,这样局限性太大。


本文的目的就是给系统增加一个 NvramService 让普通 APP 可直接读写 nvram,类似这样的调用。


WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);


Display defaultDisplay = windowManager.getDefaultDisplay();


以下是本文最终实现的 API


NvramManager mNvramManager = (NvramManager)getSystemService(“NvramService”);


String buff2 = mNvramManager.readFileByName(PRODUCT_INFO_FILENAME, 10);


源码最终编译成功后,需要将


out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 拷贝

到 AS 中,这样 apk 运行时就可调用 NvramManager


好了,不说废话了,来看如何实现吧!


1、新增com_android_server_nvram_NvramService.cpp

frameworks\base\services\core\jni\com_android_server_nvram_NvramService.cpp


参考系统原有的 LightsService 服务,找到 frameworks\base\services\core\jni\ 路径,新增 com_android_server_nvram_NvramService.cpp

/*
 * 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. com_android_server_nvram_NvramService.cpp
 */
#define LOG_TAG "NvramService"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include "android_runtime/AndroidRuntime.h"
#include <vendor/mediatek/hardware/nvram/1.0/INvram.h>
#include <android-base/chrono_utils.h>
#include <utils/misc.h>
#include <utils/Log.h>
#include <map>
#include <stdio.h>
// #include <vector>
using android::sp;
using android::hardware::hidl_string;
using vendor::mediatek::hardware::nvram::V1_0::INvram;
// using readFileByName_cb = std::function<void(const ::android::hardware::hidl_string& data)>;
namespace android {
sp<INvram> hw_device;
// hidl_string result;
jstring charTojstring(JNIEnv* env, const char* pat)
{
 jclass strClass = (env)->FindClass("java/lang/String");
 jmethodID ctorID = (env)->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
 jbyteArray bytes = (env)->NewByteArray((jsize)strlen(pat));
 (env)->SetByteArrayRegion(bytes, 0, (jsize)strlen(pat), (jbyte*)pat);
 jstring encoding = (env)->NewStringUTF("GB2312"); 
 return (jstring)(env)->NewObject(strClass, ctorID, bytes, encoding);
}
char* jstringToChar(JNIEnv* env, jstring jstr)
{
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("GB2312");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char*) malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}
static jstring nativeReadFileByName(JNIEnv* env, jobject /* clazz */,jstring filename, jint size) {
      ALOGE("android_server_app_NvramService_nativeReadFileByName.....");
      hw_device = INvram::getService();
      if (hw_device == nullptr) {
              ALOGW("failed to get INvram service");
              return filename;
     }
     ALOGE("success to get INvram service");
      //std::string str = "abc";
      //char* chardata = "abc".c_str();
      //jstring jstr = charTojstring(env, chardata);
      jstring jstr = env->NewStringUTF("abc");
      char* chardata = jstringToChar(env, filename);
      ALOGE("jstr=%s,name=%s\n", jstringToChar(env, jstr), chardata);
      //hidl_string &operator=(const filename);
      //hw_device->readFileByName("/vendor/nvdata/APCFG/APRDEB/PRODUCT_INFO", size, result);
      hw_device->readFileByName(chardata, size, [&](hidl_string result) {
                //printf("%s\n", result.c_str());
            ALOGE("INvram service hidl_string==%s\n",result.c_str());
            //jstr = result.c_str();
            //jstr = charTojstring(env, result.c_str());
            jstr = env->NewStringUTF(result.c_str());
        });
      // return charTojstring(env, chardata);
      return jstr;
}
static jint nativeWriteFileByNamevec(JNIEnv*  env, jobject /* clazz */,
                jstring filename, jint size, jstring data) {
      ALOGE("android_server_app_NvramService_nativeWriteFileByNamevec.....");
      hw_device = INvram::getService();
        if (hw_device == nullptr) {
              ALOGW("failed to get INvram service");
              return -1;
     }
     ALOGE("success to get INvram service");
     char* cFileName = jstringToChar(env, filename);
     char* chardata = jstringToChar(env, data);
     ALOGE("filename=%s,chardata=%s\n", cFileName, chardata);
     std::string strData = chardata;
     std::vector<uint8_t> vecData; 
     vecData.assign(strData.begin(), strData.end());
     jint rt = hw_device->writeFileByNamevec(cFileName, size, vecData);
     return rt;
}
static const JNINativeMethod method_table[] = {
    { "nativeReadFileByName", "(Ljava/lang/String;I)Ljava/lang/String;", (void*)nativeReadFileByName },
    { "nativeWriteFileByNamevec", "(Ljava/lang/String;ILjava/lang/String;)I", (void*)nativeWriteFileByNamevec },
};
int register_android_server_NvramService(JNIEnv *env) {
    return jniRegisterNativeMethods(env, "com/android/server/nvram/NvramService",
            method_table, NELEM(method_table));
}
};


为啥要叫 com_android_server_nvram_NvramService.cpp 这么长的名字呢?和系统其它保持一致这样看起来比较易懂,这样 cpp 文件对应的


NvramService.java 文件所处路径就为 frameworks/base/services/core/java/com/android/server/nvram/NvramService.java


char* 转化为 jstring,固定写法 jni 中经常用到


jstring charTojstring(JNIEnv* env, const char* pat)


jstring 转化为 char* ,固定写法 jni 中经常用到


char* jstringToChar(JNIEnv* env, jstring jstr)


提供给 NvramService 调用读取指定 nvram 底层实现


static jstring nativeReadFileByName(JNIEnv* env, jobject /* clazz */,jstring filename, jint size)


提供给 NvramService 调用写入指定 nvram 底层实现


static jint nativeWriteFileByNamevec(JNIEnv* env, jobject /* clazz */,jstring filename, jint size, jstring data)


固定写法,提供要注册到 NvramService 中方法表


static const JNINativeMethod method_table[]


这里需要注意的是 “(Ljava/lang/String;ILjava/lang/String;)I” ,这个代表 nativeWriteFileByNamevec 方法传递三个参数,返回 int


{ “nativeWriteFileByNamevec”, “(Ljava/lang/String;ILjava/lang/String;)I”, (void*)nativeWriteFileByNamevec },


Ljava/lang/String; jstring filename


I jint size


ILjava/lang/String; jstring data


I jint nativeWriteFileByNamevec


这块不太好理解,我在这里耗费了很多时间才搞明白,可以参考这篇 Android JNI 使用的数据结构JNINativeMethod详解


固定写法,将上面 method_table 中方法注册到 NvramService 中


int register_android_server_NvramService(JNIEnv *env)


对应两个方法具体实现逻辑不难,其中数据类型转换需要注意 hidl_string 转 jstring


有关 hidl_string 相关介绍可参考官方介绍 HIDL(C++)数据类型


2、开机注册 com_android_server_nvram_NvramService.cpp


frameworks/base/services/core/jni/onload.cpp


 int register_android_server_InputManager(JNIEnv* env);
 int register_android_server_LightsService(JNIEnv* env);
+int register_android_server_NvramService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
 int register_android_server_storage_AppFuse(JNIEnv* env);
 int register_android_server_SerialService(JNIEnv* env);
@@ -78,6 +79,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
     register_android_server_SerialService(env);
     register_android_server_InputManager(env);
     register_android_server_LightsService(env);
+    register_android_server_NvramService(env);
     register_android_server_AlarmManagerService(env);
     register_android_server_UsbDeviceManager(env);
     register_android_server_UsbMidiDevice(env);

3、Android.bp 添加编译 com_android_server_nvram_NvramService.cpp

frameworks/base/services/core/jni/Android.bp

         "com_android_server_hdmi_HdmiCecController.cpp",
         "com_android_server_input_InputManagerService.cpp",
         "com_android_server_lights_LightsService.cpp",
+        "com_android_server_nvram_NvramService.cpp",
         "com_android_server_location_GnssLocationProvider.cpp",
         "com_android_server_locksettings_SyntheticPasswordManager.cpp",
         "com_android_server_net_NetworkStatsService.cpp",
@@ -118,6 +119,7 @@ cc_defaults {
         "android.hardware.input.classifier@1.0",
         "android.hardware.ir@1.0",
         "android.hardware.light@2.0",
+        "vendor.mediatek.hardware.nvram@1.0",
         "android.hardware.power@1.0",
         "android.hardware.power@1.1",
         "android.hardware.power.stats@1.0",


上面三步完成后,jni 部分就已搞定,接下来再增加 framework 中 NvramService

4、新增 INvramService.aidl

frameworks\base\core\java\android\app\INvramService.aidl


package android.app;
interface INvramService {
    String readFileByName(in String filename, int size);
    int writeFileByNamevec(in String filename, int size, in String data);
}


注意别加 @hide 注释,不然在 NvramManager 中传递时会报如下错误


alps/frameworks/base/core/java/android/app/NvramManager.java:17: error: Class android.os.INvramService is hidden but was referenced (as parameter type) from public parameter service in android.app.NvramManager(android.content.Context ctx, android.os.INvramService service) [ReferencesHidden]

17:09:20 ninja failed with: exit status 1


5、Android.bp 添加编译 INvramService.aidl

frameworks\base\Android.bp


         "core/java/android/app/usage/ICacheQuotaService.aidl",
         "core/java/android/app/usage/IStorageStatsManager.aidl",
         "core/java/android/app/usage/IUsageStatsManager.aidl",
+        "core/java/android/app/INvramService.aidl",
         ":libbluetooth-binder-aidl",
         "core/java/android/content/IClipboard.aidl",
         "core/java/android/content/IContentService.aidl",


6、新增 NvramService.java


frameworks\base\services\core\java\com\android\server\nvram\NvramService.java

注意 native 方法名称要和 jni 中对应才能成功注册服务

/* * Copyright (C) 2008 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.
 */
package com.android.server.nvram;
import com.android.server.SystemService;
import android.app.ActivityManager;
import android.app.INvramService;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.os.Trace;
import android.provider.Settings;
import android.util.Slog;
public class NvramService extends INvramService.Stub {
    static final String TAG = "NvramService";
    static final boolean DEBUG = false;
    public NvramService(){
      android.util.Log.d(TAG,"Start NvramService...");
    }
    @Override 
    public String readFileByName(String filename,int size){
      android.util.Log.d(TAG,"NvramService readFileByName()...filename="+ filename);
      return nativeReadFileByName(filename, size);
    }
    @Override 
    public int writeFileByNamevec(String filename,int size, String data){
      android.util.Log.d(TAG,"NvramService writeFileByNamevec()...filename="+filename);
      return nativeWriteFileByNamevec(filename, size, data);
    }
    public static native String nativeReadFileByName(String filename,int size);
    public static native int nativeWriteFileByNamevec(String filename,int size, String data);
}

7、在 SystemServer 中注册 NvramService

frameworks\base\services\java\com\android\server\SystemServer.java

           traceBeginAndSlog("PinnerService");
           mSystemServiceManager.startService(PinnerService.class);
           traceEnd();
           traceBeginAndSlog("SignedConfigService");
           SignedConfigService.registerUpdateReceiver(mSystemContext);
           traceEnd();
+           
+            ServiceManager.addService("NvramService", new com.android.server.nvram.NvramService());
+        
         } catch (RuntimeException e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service", e);

8、提供 NvramManager 并代理 INvramService


frameworks\base\core\java\android\app\SystemServiceRegistry.java


代码中的"NvramService",标准写法在 Context 中新增常量,这里就不写了

                 return new AlarmManager(service, ctx);
             }});
+        registerService("NvramService", NvramManager.class,
+                new CachedServiceFetcher<NvramManager>() {
+            @Override
+            public NvramManager createService(ContextImpl ctx) {
+                try{
+                    IBinder b = ServiceManager.getServiceOrThrow("NvramService");
+                    INvramService service = INvramService.Stub.asInterface(b);
+                    return new NvramManager(ctx, service);
+                }catch(ServiceNotFoundException e){
+                    return new NvramManager(ctx, null);
+                }
+            }});
+
         registerService(Context.AUDIO_SERVICE, AudioManager.class,
                 new CachedServiceFetcher<AudioManager>() {


frameworks\base\core\java\android\app\NvramManager.java

package android.app;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.util.Log;
public class NvramManager {
    public String TAG = "NvramManager"; 
    INvramService mService;
    public NvramManager(Context ctx, INvramService service){
        mService = service;
    }
    public String readFileByName(String filename, int size){
    Log.d(TAG,"NvramManager readFileByName()...filename="+filename);
        try{
            return mService.readFileByName(filename, size);
        }catch(Exception e){
            Log.e(TAG,e.toString());
            e.printStackTrace();
        }
        return "111";
    }
    public int writeFileByNamevec(String filename, int size, String data){
      Log.d(TAG,"NvramManager writeFileByNamevec()...filename="+filename);
      try{
            return mService.writeFileByNamevec(filename, size, data);
      }catch(Exception e){
          Log.e(TAG,e.toString());
          e.printStackTrace();
      }
      return -1;
    }
}


到这里 NvramService 服务就成功注册了,先执行 make api-stubs-docs-update-current-api 命令更新系统 api

然后在重新 make 项目


9、一些坑集锦


烧写开机后 NvramService 无法启动

2021-01-29 14:22:52.915 373-373/? E/SELinux: avc: denied { add } for service=NvramService pid=1080 uid=1000 scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=0

2021-01-29 14:22:52.915 373-373/? E/ServiceManager: add_service(‘NvramService’,52) uid=1000 - PERMISSION DENIED


增加对应 selinux 权限


device\mediatek\sepolicy\basic\non_plat\system_server.te


allow system_server default_android_service:service_manager add;


再次重新 make,编译报错


libsepol.report_failure: neverallow on line 509 of system/sepolicy/public/domain.te (or line 11816 of policy.conf) violated by allow system_server default_android_service:service_manager { add };

libsepol.check_assertions: 1 neverallow failures occurred

Error while expanding policy


根据提示去除509行相应权限检测


system\sepolicy\public\domain.te


system\sepolicy\prebuilts\api\29.0\public\domain.te

#neverallow * default_android_service:service_manager add;
neverallow * default_android_vndservice:service_manager { add find };
neverallow * default_android_hwservice:hwservice_manager { add find };


服务成功启动,导入 classes.jar 成功,调用 readFileByName 或者 writeFileByNamevec 时


又出现权限问题


SELinux: avc: denied { find } for interface=vendor.mediatek.hardware.nvram::INvram sid=u:r:system_server:s0 pid=1075 scontext=u:r:system_server:s0 tcontext=u:object_r:nvram_agent_binder_hwservice:s0 tclass=hwservice_manager permissive=0


Binder:1060_8: type=1400 audit(0.0:2715): avc: denied { call } for scontext=u:r:system_server:s0 tcontext=u:r:nvram_agent_binder:s0 tclass=binder permissive=0


最终 system_server.te 修改如下


device\mediatek\sepolicy\basic\non_plat\system_server.te

allow system_server default_android_service:service_manager add;
allow system_server nvram_agent_binder_hwservice:hwservice_manager find;
allow system_server nvram_agent_binder:binder call;


如果无法成功写入数据能正常读取数据,还需要关闭写保护,注释 set_write_protect()


vendor\mediatek\proprietary\bootable\bootloader\lk\platform\mt6765\write_protect.c


if (!bypass_wp) {

// set_write_protect();

pal_log_err(“write protect Done! \n”);

} else

pal_log_err(“Bypass write protect! \n”);



AS 中两种调用 NvramService 方式

    NvramManager mNvramManager = (NvramManager)getSystemService("NvramService");
        String buff2 = mNvramManager.readFileByName(PRODUCT_INFO_FILENAME, 10);
        Log.e(TAG, " buff2="+buff2);
        INvramService nvramService = INvramService.Stub.asInterface(getService("NvramService"));
        String buff = "1";
        try {
            buff = nvramService.readFileByName(PRODUCT_INFO_FILENAME, 10);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        Log.d(TAG, " buff="+buff);


Android驱动学习-app调用内核驱动过程(驱动框架回顾)

AndroidQ 打通应用层到HAL层—(JNI服务和AIDL服务实现)

目录
相关文章
|
Java Android开发
Android的BaseFragment封装
Android的BaseFragment封装
179 0
|
Android开发
Android几行代码解决键盘遮挡问题
1.scrollTo、scrollBy实现键盘不遮挡; 2.列表中的Edittext,利用假键盘实现键盘不遮挡
2306 0
|
Android开发 定位技术 存储
|
XML Java Android开发