观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

  此博客讲解Android8之后的版本为准,Android8.0以前,是通过AIDL的方式去获取一个名为【NvRAMAgent】的服务。 到了Android8.0之后,NVRAM的读写方式已经变更,不再通过AIDL去获取NVRAM服务,然后进行读写。而是通过HIDL的方式去获取服务来进行读写。

  另外请注意,此博客讲解的是如何使用Android studio上编译的apk工程读写Nvram中的SN与WiFi的Mac地址。如果是系统工程apk请拉到博客最下面。

了解文件位置

在开始实现具体操作流程之前先了解下在系统工程目录里新的NVRAM是怎么生成的。HIDL的是需要依靠系统编译成so与jar形成接口来调用的,类似于AIDL与JNI的组合使用。另外这里只需要了解,并不需要修改这些文件与代码。因为我们需要得到经过系统编译后的so与jar文件,将其导入到后续我们的apk项目里。

hal文件

路径:

vendor/mediatek/proprietary/hardware/interfaces/nvram/1.0/INvram.hal

 代码:

/*
 * 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.
 */

package vendor.mediatek.hardware.nvram@1.0;


/*
 * Purpose:
 * Nvram hidl interface is used by java layer to access nvram file
 * Note:
 * Support nvram file list in nvram@1.0 was listed as below
 * Please update nvram interface and versoin if new nvram file need to be supported
 *
  /vendor/nvdata/APCFG/APRDEB/BT_Addr
  /vendor/nvdata/APCFG/APRDCL/AUXADC
  /vendor/nvdata/media/CAMERA_Para
  /vendor/nvdata/media/CAMERA_3A
  /vendor/nvdata/media/CAMERA_SHADING
  /vendor/nvdata/media/CAMERA_DEFECT
  /vendor/nvdata/media/CAMERA_SENSOR
  /vendor/nvdata/media/CAMERA_LENS
  /vendor/nvdata/APCFG/APRDCL/UART
  /vendor/nvdata/APCFG/APRDCL/FACTORY
  /vendor/nvdata/APCFG/APRDCL/BWCS
  /vendor/nvdata/APCFG/APRDCL/HWMON_ACC
  /vendor/nvdata/APCFG/APRDCL/HWMON_GYRO
  /vendor/nvdata/media/Voice_Recognize_Param
  /vendor/nvdata/media/Audio_AudEnh_Control_Opt
  /vendor/nvdata/media/Audio_VOIP_Param
  /vendor/nvdata/APCFG/APRDCL/HWMON_PS
  /vendor/nvdata/APCFG/APRDCL/MD_Type
  /vendor/nvdata/APCFG/APRDCL/EXT_MD_Type
  /vendor/nvdata/APCFG/APRDCL/SDIO
  /vendor/nvdata/media/CAMERA_VERSION
  /vendor/nvdata/media/CAMERA_FEATURE
  /vendor/nvdata/media/CAMERA_GEOMETRY
  /vendor/nvdata/APCFG/APRDCL/MD_SBP
  /vendor/nvdata/media/CAMERA_SHADING2
  /vendor/nvdata/media/CAMERA_PLINE
  /vendor/nvdata/media/CAMERA_AF
  /vendor/nvdata/media/CAMERA_FLASH_CALIBRATION
  /vendor/nvdata/media/Audio_Sph
  /vendor/nvdata/APCFG/APRDEB/GPS
  /vendor/nvdata/media/Audio_CompFlt
  /vendor/nvdata/media/Audio_Effect
  /vendor/nvdata/APCFG/APRDEB/WIFI
  /vendor/nvdata/APCFG/APRDEB/WIFI_CUSTOM
  /vendor/nvdata/media/Audio_Sph_Med
  /vendor/nvdata/media/Audio_Vol_custom
  /vendor/nvdata/media/Sph_Dual_Mic
  /vendor/nvdata/media/Audio_Wb_Sph
  /vendor/nvdata/APCFG/APRDEB/PRODUCT_INFO
  /vendor/nvdata/media/Headphone_CompFlt
  /vendor/nvdata/media/Audio_gain_table
  /vendor/nvdata/media/Audio_ver1_Vol_custom
  /vendor/nvdata/media/Audio_Hd_Record_Param
  /vendor/nvdata/media/Audio_Hd_Record_Scene_Table
  /vendor/nvdata/media/Audio_Buffer_DC_Calibration_Param
  /vendor/nvdata/media/VibSpk_CompFlt
  /vendor/nvdata/media/MusicDRC_CompFlt
  /vendor/nvdata/media/RingToneDRC_CompFlt
  /vendor/nvdata/media/Audio_MAGI_CONFERENCE
  /vendor/nvdata/media/Audio_HAC_Param
 */

interface INvram {
    readFileByName(string filename, uint32_t size)
            generates (string data);
    writeFileByNamevec(string filename, uint32_t size, vec<uint8_t> data)
            generates (int8_t retval);
};

.h文件

路径

vendor/mediatek/proprietary/external/libnvram/nvram_hidl/1.0/NvRam.h

代码:

#ifndef VENDOR_MEDIATEK_HARDWARE_NVRAM_V1_0_NVRAM_H
#define VENDOR_MEDIATEK_HARDWARE_NVRAM_V1_0_NVRAM_H

#include <vendor/mediatek/hardware/nvram/1.0/INvram.h>
#include <hidl/MQDescriptor.h>
#include <hidl/Status.h>

namespace vendor {
namespace mediatek {
namespace hardware {
namespace nvram {
namespace V1_0 {
namespace implementation {

using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::base::V1_0::IBase;
using ::vendor::mediatek::hardware::nvram::V1_0::INvram;
using ::android::hardware::hidl_array;
using ::android::hardware::hidl_memory;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;

struct Nvram : public INvram {
    // Methods from ::vendor::mediatek::hardware::nvram::V1_0::INvram follow.
    Return<void> readFileByName(const hidl_string& filename, uint32_t size, readFileByName_cb _hidl_cb) override;
    Return<int8_t> writeFileByNamevec(const hidl_string& filename, uint32_t size, const hidl_vec<uint8_t>& data) override;

    // Methods from ::android::hidl::base::V1_0::IBase follow.

};

extern "C" INvram* HIDL_FETCH_INvram(const char* name);

}  // namespace implementation
}  // namespace V1_0
}  // namespace nvram
}  // namespace hardware
}  // namespace mediatek
}  // namespace vendor

#endif  // VENDOR_MEDIATEK_HARDWARE_NVRAM_V1_0_NVRAM_H

.cpp文件

路径

vendor/mediatek/proprietary/external/libnvram/nvram_hidl/1.0/NvRam.cpp

代码:

#include "Nvram.h"
#include <android-base/logging.h>
#include <log/log.h>
#include "libnvram.h"
#include "libnvram_log.h"
using std::string;


#define NVRAM_LOG(...) \
        do { \
            ALOGD(__VA_ARGS__); \
        } while (0)


namespace vendor {
namespace mediatek {
namespace hardware {
namespace nvram {
namespace V1_0 {
namespace implementation {
    void covertVector2Array(std::vector<uint8_t> in, char* out) {
        int size = in.size();
        for(int i = 0; i < size; i++) {
            out[i] = in.at(i);
        }
    }

    void covertArray2Vector(const char* in, int len, std::vector<uint8_t>& out) {
        out.clear();
        for(int i = 0; i < len; i++) {
            out.push_back(in[i]);
        }
    }

// Methods from ::vendor::mediatek::hardware::nvram::V1_0::INvram follow.
Return<void> Nvram::readFileByName(const hidl_string& filename, uint32_t size, readFileByName_cb _hidl_cb) {
    // TODO implement
    int pRecSize=0,pRecNum=0;
    bool IsRead=1;
    char *buff=NULL;
    int file_lid = -1;
    string result;
    int i = 0;
    char *nvramstr = (char*)malloc(2*size+1);
    char *nvramptr = nvramstr;
    char *cstr=new char[filename.size()+1];
    if (nvramstr==NULL || size==0 || cstr==NULL) {
    NVRAM_LOG("nvramstr==NULL\n");
        if(nvramstr!=NULL)
            free(nvramstr);
        if(cstr!=NULL)
            delete[] cstr;
    return Void();
    }

    snprintf(cstr, filename.size()+1,"%s", filename.c_str());

    file_lid = NVM_GetLIDByName(cstr);
    if(file_lid < 0)
    {
        NVRAM_LOG("Get LID by name fail! %s\n",cstr);
        free(nvramstr);
        delete[] cstr;
        return Void();
    }

    F_ID fd=NVM_GetFileDesc(file_lid,&pRecSize,&pRecNum,IsRead);
    if (fd.iFileDesc==-1)
    {
        LOG(ERROR) << "open file Error!";
        free(nvramstr);
        delete[] cstr;
        return Void();
    }
    LOG(ERROR) << "RecNum is "<<pRecNum;
    //size=pRecSize*pRecNum;
    buff=(char *)malloc(size);
    if(buff == NULL)
    {
        NVRAM_LOG("Malloc Error!\n");
        if(!NVM_CloseFileDesc(fd))
            NVRAM_LOG("close File error!\n");
        free(nvramstr);
        delete[] cstr;
        return Void();
    }
    if((ssize_t)size == read(fd.iFileDesc,buff,(ssize_t)size))
    {
        if(NVM_CloseFileDesc(fd))
        {
            NVRAM_LOG("Read Done!Size is %d\n",size);
            //return buff;
        }
        else
        {
            NVRAM_LOG("Close file error!\n");
            free(buff);
            free(nvramstr);
            delete[] cstr;
            return Void();
        }
    }
    else
    {
        NVRAM_LOG("read File error!\n");
        if(!NVM_CloseFileDesc(fd))
            NVRAM_LOG("close File error!\n");
        free(buff);
        free(nvramstr);
        delete[] cstr;
        return Void();
    }

    NVRAM_LOG("nvramstr buff[0]%x, buff[1]%x, buff[2]%x, buff[3]%x, buff[4]%x, buff[5]%x, buff[6]%x, buff[7]%x, buff[8]%x \n",
        buff[0],buff[1],buff[2],buff[3],buff[4],buff[5],buff[6],buff[7],buff[8]);


    for(i=0; i<(int)size; i++)
    {
       nvramptr += sprintf(nvramptr, "%02X",buff[i]);
    }
    sprintf(nvramptr,"\n");
    *(nvramptr+1)='\0';


    NVRAM_LOG("nvramstr %s\n",nvramstr);
    _hidl_cb(nvramstr);
    free(buff);
    delete[] cstr;
    return Void();
}

Return<int8_t> Nvram::writeFileByNamevec(const hidl_string& filename, uint32_t size, const hidl_vec<uint8_t>& data) {
    // TODO implement
    char *cstr_filename=new char[filename.size()+1];
    char *cstr_data=new char[data.size()+1];
    if (cstr_data==NULL || size==0 || cstr_filename==NULL) {
    NVRAM_LOG("cstr_data==NULL\n");
        if(cstr_data!=NULL)
            delete[] cstr_data;
        if(cstr_filename!=NULL)
            delete[] cstr_filename;
    return int8_t {};
    }
    snprintf(cstr_filename, filename.size()+1,"%s", filename.c_str());
    covertVector2Array(data, cstr_data);

    int pRecSize=0,pRecNum=0,looptimes=0;
    bool IsRead=0;
    int file_lid = -1;

    file_lid = NVM_GetLIDByName(cstr_filename);
    if(file_lid < 0)
    {
        NVRAM_LOG("Get LID by name fail!\n");
        delete[] cstr_data;
        delete[] cstr_filename;
        return int8_t {};
    }


    F_ID fd=NVM_GetFileDesc(file_lid,&pRecSize,&pRecNum,IsRead);
    if (fd.iFileDesc==-1)
    {
        NVRAM_LOG("open file Error!\n");
        delete[] cstr_data;
        delete[] cstr_filename;
        return int8_t {};
    }
    #if 0
    if(size != pRecSize)
    {
        NVRAM_LOG("Input size (%d) and RecSize (%d) not match!\n",size,pRecSize);
        if(!NVM_CloseFileDesc(fd))
            NVRAM_LOG("close File error!\n");
        //return 0;
        return int8_t {};
    }
    #endif

    // GetFileDesc should return right pos and this would cause pro_info multi lids issue.
    #if 0
    if(0 != lseek(fd.iFileDesc,0,SEEK_SET)){
        NVRAM_LOG("lseek error!\n");
        if(!NVM_CloseFileDesc(fd))
            NVRAM_LOG("close File error!\n");
        return 0;
        }
    #endif
    looptimes = pRecNum;
    NVRAM_LOG("RecNum is :%d\n",pRecNum);
    while(looptimes--)
    {
        if((ssize_t)size != write(fd.iFileDesc,cstr_data,(ssize_t)size))
        {
            NVRAM_LOG("write file error!\n");
            if(!NVM_CloseFileDesc(fd))
                NVRAM_LOG("close File error!\n");
            delete[] cstr_data;
            delete[] cstr_filename;
            return int8_t {};
        }
    }
    if(NVM_CloseFileDesc(fd))
    {
        NVRAM_LOG("Write file Done!\n");
        delete[] cstr_data;
        delete[] cstr_filename;
        return int8_t {};
    }
    else
    {
        NVRAM_LOG("close File error!\n");
        delete[] cstr_data;
        delete[] cstr_filename;
        return int8_t {};
    }
    //return int8_t {};
}

// Methods from ::android::hidl::base::V1_0::IBase follow.

INvram* HIDL_FETCH_INvram(const char* /* name */) {
    return new Nvram();
}

}  // namespace implementation
}  // namespace V1_0
}  // namespace nvram
}  // namespace hardware
}  // namespace mediatek
}  // namespace vendor

经过系统编译后输出的so文件路径与jar文件路径

so路径

out\target\product\A8385_JS04\obj\SHARED_LIBRARIES\vendor.mediatek.hardware.nvram@1.0.vendor_intermediates

jar路径

out\target\common\obj\JAVA_LIBRARIES\vendor.mediatek.hardware.nvram-V1.0-java_intermediates

其中classes.jar就包含了INvram这个类,这里可以用反编译工具解包后查看

 如图:

读写Nvram中SN号的操作流程

这里是Android studio上编译的工程

第一步 导入系统编译Nvram的so与jar

 在build里添加

android {
    compileSdk 28

    defaultConfig {
        applicationId "com.xxx.xxxx"
        minSdk 26
        targetSdk 28
        versionCode 11
        versionName "1.10"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    //添加
    sourceSets {
        main {
            jniLibs.srcDirs = ["libs"]
        }
    }
  //略...
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])//添加
    implementation files('libs\\classes.jar')//添加
}

第二步 项目必须是系统权限与系统签名

怎么实现请参考博客:https://www.cnblogs.com/guanxinjing/p/11410915.html

第三步 项目必须架包framework

怎么实现请参考博客:https://www.cnblogs.com/guanxinjing/p/16613716.html

这里说一下为什么需要架framework,因为Android studio在编译的时候会检查到代码里没有IHwInterface这个类并且抛出编译异常,而这个类在framework中。

android.os.IHwInterface;

这里在啰嗦一下为什么会检查这个类。因为在调用INvram.getService()方法的时候,INvram的基类是IBase

 

而IBase继承了IHwInterface

 

 

第四步 修改系统工程里的代码,删除Nv写入保护的代码

系统工程修改文件路径

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

修改代码位置

 如果不删除写入保护set_write_protect,会出现在写入NV的时候如下报错

第五步 使用下面工具类读写SN号,或者Nv里的其他数据 

import android.util.Log;
import com.android.internal.util.HexDump;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import vendor.mediatek.hardware.nvram.V1_0.INvram;

/**
 *
 *   /vendor/nvdata/APCFG/APRDEB/BT_Addr
 *   /vendor/nvdata/APCFG/APRDCL/AUXADC
 *   /vendor/nvdata/media/CAMERA_Para
 *   /vendor/nvdata/media/CAMERA_3A
 *   /vendor/nvdata/media/CAMERA_SHADING
 *   /vendor/nvdata/media/CAMERA_DEFECT
 *   /vendor/nvdata/media/CAMERA_SENSOR
 *   /vendor/nvdata/media/CAMERA_LENS
 *   /vendor/nvdata/APCFG/APRDCL/UART
 *   /vendor/nvdata/APCFG/APRDCL/FACTORY
 *   /vendor/nvdata/APCFG/APRDCL/BWCS
 *   /vendor/nvdata/APCFG/APRDCL/HWMON_ACC
 *   /vendor/nvdata/APCFG/APRDCL/HWMON_GYRO
 *   /vendor/nvdata/media/Voice_Recognize_Param
 *   /vendor/nvdata/media/Audio_AudEnh_Control_Opt
 *   /vendor/nvdata/media/Audio_VOIP_Param
 *   /vendor/nvdata/APCFG/APRDCL/HWMON_PS
 *   /vendor/nvdata/APCFG/APRDCL/MD_Type
 *   /vendor/nvdata/APCFG/APRDCL/EXT_MD_Type
 *   /vendor/nvdata/APCFG/APRDCL/SDIO
 *   /vendor/nvdata/media/CAMERA_VERSION
 *   /vendor/nvdata/media/CAMERA_FEATURE
 *   /vendor/nvdata/media/CAMERA_GEOMETRY
 *   /vendor/nvdata/APCFG/APRDCL/MD_SBP
 *   /vendor/nvdata/media/CAMERA_SHADING2
 *   /vendor/nvdata/media/CAMERA_PLINE
 *   /vendor/nvdata/media/CAMERA_AF
 *   /vendor/nvdata/media/CAMERA_FLASH_CALIBRATION
 *   /vendor/nvdata/media/Audio_Sph
 *   /vendor/nvdata/APCFG/APRDEB/GPS
 *   /vendor/nvdata/media/Audio_CompFlt
 *   /vendor/nvdata/media/Audio_Effect
 *   /vendor/nvdata/APCFG/APRDEB/WIFI
 *   /vendor/nvdata/APCFG/APRDEB/WIFI_CUSTOM
 *   /vendor/nvdata/media/Audio_Sph_Med
 *   /vendor/nvdata/media/Audio_Vol_custom
 *   /vendor/nvdata/media/Sph_Dual_Mic
 *   /vendor/nvdata/media/Audio_Wb_Sph
 *   /vendor/nvdata/APCFG/APRDEB/PRODUCT_INFO
 *   /vendor/nvdata/media/Headphone_CompFlt
 *   /vendor/nvdata/media/Audio_gain_table
 *   /vendor/nvdata/media/Audio_ver1_Vol_custom
 *   /vendor/nvdata/media/Audio_Hd_Record_Param
 *   /vendor/nvdata/media/Audio_Hd_Record_Scene_Table
 *   /vendor/nvdata/media/Audio_Buffer_DC_Calibration_Param
 *   /vendor/nvdata/media/VibSpk_CompFlt
 *   /vendor/nvdata/media/MusicDRC_CompFlt
 *   /vendor/nvdata/media/RingToneDRC_CompFlt
 *   /vendor/nvdata/media/Audio_MAGI_CONFERENCE
 *   /vendor/nvdata/media/Audio_HAC_Param
 */
public class SNParamUtils {
    public static final String PRODUCT_INFO_FILENAME = "/vendor/nvdata/APCFG/APRDEB/PRODUCT_INFO";
    private static final int SN_LENGTH = 32;

    public static void writeSn(String sn) {
        char sn_chars[] = sn.toCharArray();
        byte sn_bytes[] = getBytes(sn_chars);
        try {
            INvram agent = INvram.getService();
            if (agent != null) {
                ArrayList<Byte> dataArray = new ArrayList<>();
                for (byte b : sn_bytes) {
                    dataArray.add(new Byte(b));
                }
                int ret_1 = agent.writeFileByNamevec(PRODUCT_INFO_FILENAME, SN_LENGTH, dataArray);
                if (ret_1 == 0) {
                    Log.e("zh", "writeSn success " + ret_1);
                } else {
                    Log.e("zh", "writeSn failed" + ret_1);
                }
            } else {
                Log.e("zh", "writeSn: agent null");
            }
        } catch (Exception e) {
            Log.e("zh", "writeSn exception:" + e.getLocalizedMessage());
            e.printStackTrace();
        }
    }


    public static String readSn() {
        int targets = 0;
        try {
            String buff = null;
            INvram agent = INvram.getService();

            if (agent != null) {
                buff = agent.readFileByName(PRODUCT_INFO_FILENAME, SN_LENGTH);
                Log.e("zh", "buff: " + buff );
            }
            byte[] buffArr = HexDump.hexStringToByteArray(buff.substring(0, buff.length() - 1));
            targets = (buffArr[0] & 0xff) | ((buffArr[1] << 8) & 0xff00) | ((buffArr[2] << 24) >>> 8) | (buffArr[3] << 24);
            Log.e("zh",  "readSn: chars=>" + Arrays.toString(getChars(buffArr)) + ", targets == " + targets);
            Log.e("zh",  "readSn: bytes=>" + Arrays.toString((buffArr)) + ", targets == " + targets);
            return String.valueOf(getChars(buffArr));
        } catch (Exception e) {
            Log.e("zh",  "readSn exception:" + e.getLocalizedMessage());
            e.printStackTrace();
        }
        return "123456789";
    }

    private static char[] getChars(byte[] bytes) {
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes);
        bb.flip();
        CharBuffer cb = cs.decode(bb);
        return cb.array();
    }

    private static byte[] getBytes(char[] chars) {
        Charset cs = Charset.forName("UTF-8");
        CharBuffer cb = CharBuffer.allocate(chars.length);
        cb.put(chars);
        cb.flip();
        ByteBuffer bb = cs.encode(cb);
        return bb.array();
    }
}

假如你是系统工程编译apk

可以在apk工程的android.mk里直接导入,不需要以上的第一步到第三步,但是依然需要第四步与第五步

LOCAL_STATIC_JAVA_LIBRARIES := vendor.mediatek.hardware.nvram-V1.0-java
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_CERTIFICATE := platform

# Add static library for nvram
# LOCAL_STATIC_JAVA_LIBRARIES := vendor.mediatek.hardware.nvram-V1.1-java-static
LOCAL_STATIC_JAVA_LIBRARIES := vendor.mediatek.hardware.nvram-V1.0-java
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := JNvRAM_V11
LOCAL_PRIVATE_PLATFORM_APIS := true
include $(BUILD_PACKAGE)

MTK平台如何用Build.getSerial()方法读取写入的SN号

这里直接复制MTK论坛那边的开发回复(下面提是MTK的写号工具SN Write,但是我们这个写NV的方法同样适用):

在/vendor/mediatek/proprietary/bootable/bootloader/lk/app/mt_boot/mt_boot.c
中,将 #define SERIAL_NUM_FROM_BARCODE  宏定义打开
这时SN Write工具写Barcode即可,其他无需修改。
这是最简单的方式,相当于serial no取Barcode空间的值。

在NV中读写WiFi的Mac地址

请注意!在Android10版本以后,WiFi已经默认使用随机mac地址,意思是说,哪怕你写到了NV中,在WifiManager中也不会读取到,这需要你手动设置或者修改代码将,下面的选项从随机Mac地址修改成固定设备的Mac地址。如下图位置:

代码部分:

其他流程与上面一样,这里直接提供读写代码。 

import android.app.Application;
import android.content.Context;
import android.net.wifi.WifiManager;
import android.util.Log;
import com.android.internal.util.HexDump;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.StringTokenizer;
import vendor.mediatek.hardware.nvram.V1_0.INvram;

public class MacParamUtils {
    public static final String MAC_ADDRESS_FILENAME = "/vendor/nvdata/APCFG/APRDEB/WIFI";
    private static final int MAC_ADDRESS_OFFSET = 4;
    private static final int MAC_ADDRESS_DIGITS = 6;

    /**
     * 通过WifiManager获取WiFi Mac地址
     */
    public static String getMACAddress(Application application) {
        try {
            WifiManager wifiManager = (WifiManager)application.getSystemService(Context.WIFI_SERVICE);
            Method getFactoryMacAddresses = wifiManager.getClass().getMethod("getFactoryMacAddresses");
            String[] macAddresses  = (String[]) getFactoryMacAddresses.invoke(wifiManager);
            String macAddress = "";
            if (macAddresses.length > 0) {
                macAddress = macAddresses[0];
            }
            return macAddress;
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 直接从NV中读取Mac地址
     * @return
     */
    public static String getWifiMacFromNvram() {
        StringBuffer nvramBuf = new StringBuffer();
        try {
            int i = 0;
            String buff = null;
            INvram agent = INvram.getService();
            if (agent == null) {
                Log.e("zh", "NvRAMAgent is null");
                return "";            }
            try {
                buff = agent.readFileByName(MAC_ADDRESS_FILENAME, MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS);
            } catch (Exception e) {
                e.printStackTrace();
                return "";
            }
            Log.e("zh", "Raw data:" + buff);
            if (buff.length() < 2 * (MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS)) {
                return "";
            }
            // Remove the \0 special character.
            int macLen = buff.length() - 1;
            for (i = MAC_ADDRESS_OFFSET * 2; i < macLen; i += 2) {
                if ((i + 2) < macLen) {
                    nvramBuf.append(buff.substring(i, i + 2));
                    nvramBuf.append(":");
                } else {
                    nvramBuf.append(buff.substring(i));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        return nvramBuf.toString();
    }

    /**
     * 更新WiFi Mac地址到NV中
     * @param mac
     * @return
     */
    public static int updateWifiMacToNvram(String mac){
        try {
            int i = 0;
            INvram agent = INvram.getService();
            byte[] macAddr = new byte[MAC_ADDRESS_DIGITS];
            if (agent == null) {
                Log.e("zh", "NvRAMAgent is null");
                return 0;
            }
            //parse mac address firstly
            StringTokenizer txtBuffer = new StringTokenizer(mac, ":");
            while (txtBuffer.hasMoreTokens()) {
                macAddr[i] = (byte) Integer.parseInt(txtBuffer.nextToken(), 16);
                i++;
            }
            if(i != MAC_ADDRESS_DIGITS){
                Log.e("zh", "Wrong length of macAddr:" + i);
                return 0;
            }
            String buff = null;
            try {
                buff = agent.readFileByName(MAC_ADDRESS_FILENAME, MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS);
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
            byte[] buffArr = HexDump.hexStringToByteArray(buff.substring(0, buff.length() - 1));
            for (i = 0; i < MAC_ADDRESS_DIGITS; i ++) {
                buffArr[i + 4] = macAddr[i];
            }
            ArrayList<Byte> dataArray = new ArrayList<Byte>(MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS);
            for (i = 0; i < MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS; i++) {
                dataArray.add(i, new Byte(buffArr[i]));
            }
            int flag = 0;
            try {
                flag = agent.writeFileByNamevec(MAC_ADDRESS_FILENAME,MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS,dataArray);
                return flag;
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    private static char[] getChars(byte[] bytes) {
        Charset cs = Charset.forName("UTF-8");
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes);
        bb.flip();
        CharBuffer cb = cs.decode(bb);
        return cb.array();
    }

    private static byte[] getBytes(char[] chars) {
        Charset cs = Charset.forName("UTF-8");
        CharBuffer cb = CharBuffer.allocate(chars.length);
        cb.put(chars);
        cb.flip();
        ByteBuffer bb = cs.encode(cb);
        return bb.array();
    }
}

 

 

End

posted on 2022-11-08 14:20  观心静  阅读(1190)  评论(0编辑  收藏  举报