Android 通过JNI获取MAC地址(适配Android6.0及以上)

最近项目中遇到需要在C++层进行加密,然后编译成so。我们知道,MAC地址能够辨别设备的唯一性。所以有个需求就是需要在C++层获取MAC地址,这里我们就需要用到JNI编程了,话不多说,开始看看如何获取吧。

在这之前,不要忘记添加权限

android:name="android.permission.INTERNET" />
android:name="android.permission.READ_PHONE_STATE" />
android:name="android.permission.ACCESS_WIFI_STATE" />

首先,我们来看看java中是怎么获取的。Android6.0之前的方法在6.0之后已经无法获取成功。所以我们直接使用最新的获取方法。代码很简单,通过静态方法getByName获取NetworkInterface实例,然后通过getHardwareAddress()获取byte[]数组,注意这里获取到的是16进制的数组,需要转换。

/**
     * 获取设备MAC地址
     * */
    public static String getMacAddress() {
 /*获取mac地址有一点需要注意的就是android 6.0版本后,以下注释方法不再适用,不管任何手机都会返回"02:00:00:00:00:00"这个默认的mac地址,这是googel官方为了加强权限管理而禁用了getSYstemService(Context.WIFI_SERVICE)方法来获得mac地址。*/
        //        String macAddress= "";
//        WifiManager wifiManager = (WifiManager) MyApp.getContext().getSystemService(Context.WIFI_SERVICE);
//        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
//        macAddress = wifiInfo.getMacAddress();
//        return macAddress;

        String macAddress = null;
        StringBuffer buf = new StringBuffer();
        NetworkInterface networkInterface = null;
        try {
            networkInterface = NetworkInterface.getByName("eth1");
            if (networkInterface == null) {
                networkInterface = NetworkInterface.getByName("wlan0");
            }
            if (networkInterface == null) {
                return "02:00:00:00:00:02";
            }
            byte[] addr = networkInterface.getHardwareAddress();
            for (byte b : addr) {
                buf.append(String.format("%02X:", b));
            }
            if (buf.length() > 0) {
                buf.deleteCharAt(buf.length() - 1);
            }
            macAddress = buf.toString();
        } catch (SocketException e) {
            e.printStackTrace();
            return "02:00:00:00:00:02";
        }
        return macAddress;
    }
然后我们根据此方法用JNI来实现

//通过JNI找到java中的NetworkInterface类
jclass cls_networkInterface = env->FindClass("java/net/NetworkInterface");
if (cls_networkInterface == 0) {
    return env->NewStringUTF("");
}
//找到getByName方法
jmethodID jmethodID1 = env->GetStaticMethodID(cls_networkInterface, "getByName", "(Ljava/lang/String;)Ljava/net/NetworkInterface;");
if (jmethodID1 == 0)
    return env->NewStringUTF("");
std::string ss = "wlan0";
jstring jss2 = env->NewStringUTF(ss.c_str());
//调用getByname方法返回NetworkInterface的实例
jobject jobject1 = env->CallStaticObjectMethod(cls_networkInterface, jmethodID1, jss2);
//找到getHardAddress方法
jmethodID getHardwareAddress = env->GetMethodID(cls_networkInterface, "getHardwareAddress", "()[B");
if (getHardwareAddress == 0)
    return env->NewStringUTF("");
//调用getHardAddress方法获取MAC地址的byte[]数组
jbyteArray jbyte1 = (jbyteArray)env->CallObjectMethod(jobject1, getHardwareAddress);
//下面一些列流程就是讲byte[]数组转换成char类型字符在转换成字符串
jbyte * olddata = (jbyte*)env->GetByteArrayElements(jbyte1, 0);
jsize  oldsize = env->GetArrayLength(jbyte1);
// BYTE定义为  #define BYTE unsigned char
BYTE* bytearr = (BYTE*)olddata;
int len = (int)oldsize;

char* data = (char*)env->GetByteArrayElements(jbyte1, 0);
char *temp = new char[len*2 + 1];
memset(temp,0,len*2 +1);
for (int i = 0; i < len; i++) {
    char * buffer = new char[2];
    memset(buffer,2,0);
    sprintf(buffer, "%02X", data[i]);
    memcpy(temp+i*2, buffer, 2);
    delete[] (buffer);
}
jstring jMac = charTojstring(env, temp);
delete[] temp;
return jMac;

其中BYTE为宏定义 在cpp文件中加上

#define BYTE unsigned char
charTojstring方法如下:

jstring charTojstring(JNIEnv* env, const char* pat) {
    //定义java String strClass
    jclass strClass = (env)->FindClass("java/lang/String");
    //获取String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
    jmethodID ctorID = (env)->GetMethodID(strClass, "", "([BLjava/lang/String;)V");
    //建立byte数组
    jbyteArray bytes = (env)->NewByteArray(strlen(pat));
    //char* 转换为byte数组
    (env)->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*) pat);
    // 设置String, 保存语言类型,用于byte数组转换至String时的参数
    jstring encoding = (env)->NewStringUTF("GB2312");
    //byte数组转换为java String,并输出
    return (jstring) (env)->NewObject(strClass, ctorID, bytes, encoding);
}

主要的难点就是如何进行数据的转换。JNI通过一系列类似java反射方法来完成java所做的事情,来进行对java的调用。具体参数转换网上很多,这里就不列出。这样我们就获取到了设备的MAC地址,当然一般情况下我们不需要再返回java层(java能获取,为什么还这么麻烦在c层获取,再返回呢,除非我有病。)二十直接在JNI层对MAC地址进行操作,比如加密。

文章就到这了,大家有兴趣还可以对java的很多方法进行JNI调用,除了系统的类,我们也可以自定义类,如果需要对代码加密,不想让别人轻易的反编译,也可以使用这招哦,毕竟so还是很安全的。


你可能感兴趣的:(移动应用,JNI)