I.MX6 android BatteryService jni hacking

/****************************************************************************
 *               I.MX6 android BatteryService jni hacking
 * 声明:
 *    本文主要是为了知道Android的获取的电源管理的数据的jni是从Linux系统的
 * 什么位置获取的,获取的机制是什么。
 *
 *                                          2016-2-22 深圳 南山平山村 曾剑锋
 ***************************************************************************/

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

#define LOG_TAG "BatteryService"            

#include "JNIHelp.h"
#include "jni.h"
#include <utils/Log.h>
#include <utils/misc.h>
#include <cutils/properties.h>

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <dirent.h>
#include <linux/ioctl.h>

namespace android {

/**
 * 从下面的路径可知,POWER_SUPPLY_PATH相当于电池管理初始路径。
 *     [email protected]:/sys/class/power_supply/bq274xx-0 # ls
 *     capacity
 *     capacity_level
 *     charge_full
 *     charge_full_design
 *     charge_now
 *     current_now
 *     device
 *     power
 *     present
 *     status
 *     subsystem
 *     technology
 *     temp
 *     type
 *     uevent
 *     voltage_now
 *     [email protected]:/sys/class/power_supply/bq274xx-0 #
 */
#define POWER_SUPPLY_PATH "/sys/class/power_supply"     

/**
 * 为了在C/C++中表示属性和方法,JNI在jni.h头文件中定义了jfieldID和jmethodID类型来分别代表Java对象的属性和方法。我们在访问或是设置Java属性的时候,首先就要先在本地代码取得代表该Java属性的jfieldID,然后才能在本地代码进行Java属性操作。同样的,我们需要调用Java对象方法时,也是需要取得代表该方法的jmethodID才能进行Java方法调用。
 */
struct FieldIds {
    // members
    jfieldID mAcOnline;
    jfieldID mUsbOnline;
    jfieldID mWirelessOnline;
    jfieldID mBatteryStatus;
    jfieldID mBatteryHealth;
    jfieldID mBatteryPresent;
    jfieldID mBatteryLevel;
    jfieldID mBatteryVoltage;
    jfieldID mBatteryTemperature;
    jfieldID mBatteryTechnology;
};
static FieldIds gFieldIds;

struct BatteryManagerConstants {
    jint statusUnknown;
    jint statusCharging;
    jint statusDischarging;
    jint statusNotCharging;
    jint statusFull;
    jint healthUnknown;
    jint healthGood;
    jint healthOverheat;
    jint healthDead;
    jint healthOverVoltage;
    jint healthUnspecifiedFailure;
    jint healthCold;
};
static BatteryManagerConstants gConstants;

/**
 * 指向sysfs中电池对应的文件
 */
struct PowerSupplyPaths {
    char* acOnlinePath;
    char* usbOnlinePath;
    char* wirelessOnlinePath;
    char* batteryStatusPath;
    char* batteryHealthPath;
    char* batteryPresentPath;
    char* batteryCapacityPath;
    char* batteryVoltagePath;
    char* batteryTemperaturePath;
    char* batteryTechnologyPath;
};
static PowerSupplyPaths gPaths;

static int gVoltageDivisor = 1;

static jint getBatteryStatus(const char* status)
{
    // 通过比较首字母来判断电池状态
    switch (status[0]) {
        case ‘C‘: return gConstants.statusCharging;         // Charging
        case ‘D‘: return gConstants.statusDischarging;      // Discharging
        case ‘F‘: return gConstants.statusFull;             // Full
        case ‘N‘: return gConstants.statusNotCharging;      // Not charging
        case ‘U‘: return gConstants.statusUnknown;          // Unknown

        default: {
            ALOGW("Unknown battery status ‘%s‘", status);
            return gConstants.statusUnknown;
        }
    }
}

static jint getBatteryHealth(const char* status)
{
    // 通过比较首字母来判断电池状态
    switch (status[0]) {
        case ‘C‘: return gConstants.healthCold;         // Cold
        case ‘D‘: return gConstants.healthDead;         // Dead
        case ‘G‘: return gConstants.healthGood;         // Good
        case ‘O‘: {
            if (strcmp(status, "Overheat") == 0) {
                return gConstants.healthOverheat;
            } else if (strcmp(status, "Over voltage") == 0) {
                return gConstants.healthOverVoltage;
            }
            ALOGW("Unknown battery health[1] ‘%s‘", status);
            return gConstants.healthUnknown;
        }

        case ‘U‘: {
            if (strcmp(status, "Unspecified failure") == 0) {
                return gConstants.healthUnspecifiedFailure;
            } else if (strcmp(status, "Unknown") == 0) {
                return gConstants.healthUnknown;
            }
            // fall through
        }

        default: {
            ALOGW("Unknown battery health[2] ‘%s‘", status);
            return gConstants.healthUnknown;
        }
    }
}

// 从文件中读取内容,并剔除最后的换行符
static int readFromFile(const char* path, char* buf, size_t size)
{
    if (!path)
        return -1;
    int fd = open(path, O_RDONLY, 0);
    if (fd == -1) {
        ALOGE("Could not open ‘%s‘", path);
        return -1;
    }

    ssize_t count = read(fd, buf, size);
    if (count > 0) {
        while (count > 0 && buf[count-1] == ‘\n‘)
            count--;
        buf[count] = ‘\0‘;
    } else {
        buf[0] = ‘\0‘;
    } 

    close(fd);
    return count;
}

// 设置Java属性bool值
static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
    const int SIZE = 16;
    char buf[SIZE];

    jboolean value = false;
    if (readFromFile(path, buf, SIZE) > 0) {
        if (buf[0] != ‘0‘) {
            value = true;
        }
    }
    env->SetBooleanField(obj, fieldID, value);
}

// 设置Java属性int值
static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
    const int SIZE = 128;
    char buf[SIZE];

    jint value = 0;
    if (readFromFile(path, buf, SIZE) > 0) {
        value = atoi(buf);
    }
    env->SetIntField(obj, fieldID, value);
}

// 设置Java属性电压属性
static void setVoltageField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
{
    const int SIZE = 128;
    char buf[SIZE];

    jint value = 0;
    if (readFromFile(path, buf, SIZE) > 0) {
        value = atoi(buf);
        value /= gVoltageDivisor;
    }
    env->SetIntField(obj, fieldID, value);
}

// 更新电池状态
static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
{
    setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline);
    setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline);
    setBooleanField(env, obj, gPaths.wirelessOnlinePath, gFieldIds.mWirelessOnline);
    setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent);

    setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel);
    setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage);
    setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature);

    char prop[5];
    // always report AC plug-in and capacity 100% if emulated.battery is set to 1
    // 如果在调试的时候希望电池的状态永远为100%,电池永远被插入,那么设置属性sys.emulated.battery为1
    property_get("sys.emulated.battery", prop, "0");
    if (!strcmp(prop, "1")){
        env->SetBooleanField(obj, gFieldIds.mAcOnline, true);
        env->SetIntField(obj, gFieldIds.mBatteryLevel, 100);
    }

    const int SIZE = 128;
    char buf[SIZE];

    // 获取对应路径下的电池数据
    if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > 0)
        env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
    else
        env->SetIntField(obj, gFieldIds.mBatteryStatus,
                         gConstants.statusUnknown);

    if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > 0)
        env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));

    if (readFromFile(gPaths.batteryTechnologyPath, buf, SIZE) > 0)
        env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf));
}

// 用于注册的方法集合
static JNINativeMethod sMethods[] = {
     /* name, signature, funcPtr */
    {"native_update", "()V", (void*)android_server_BatteryService_update},
};

/**
 * [email protected]:/sys/class/power_supply/bq274xx-0 # ls
 * capacity
 * capacity_level
 * charge_full
 * charge_full_design
 * charge_now
 * current_now
 * device
 * power
 * present
 * status
 * subsystem
 * technology
 * temp
 * type
 * uevent
 * voltage_now
 * [email protected]:/sys/class/power_supply/bq274xx-0 #
 */

// 电池管理中默认被Java构造函数调用的函数
int register_android_server_BatteryService(JNIEnv* env)
{
    char    path[PATH_MAX];
    struct dirent* entry;

    // 打开电池设备在sysfs中对应的文件夹
    DIR* dir = opendir(POWER_SUPPLY_PATH);
    if (dir == NULL) {
        ALOGE("Could not open %s\n", POWER_SUPPLY_PATH);
    } else {
        // 连续读取子目录,这里可能是考虑到可能有多个电源管理芯片
        // 当然,这里也要考虑当前目录(.)和上一级目录的存在(..)
        while ((entry = readdir(dir))) {
            // 获取子文件夹的名字,这里是bq274xx-0
            const char* name = entry->d_name;   

            // ignore "." and ".."   剔除当前目录和上一级目录
            if (name[0] == ‘.‘ && (name[1] == 0 || (name[1] == ‘.‘ && name[2] == 0))) {
                continue;
            }

            char buf[20];
            // Look for "type" file in each subdirectory
            /**
             * [email protected]:/sys/class/power_supply/bq274xx-0 # cat type
             * Battery                  ---> type
             * [email protected]:/sys/class/power_supply/bq274xx-0 #
             */
            snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
            int length = readFromFile(path, buf, sizeof(buf));
            if (length > 0) {
                if (buf[length - 1] == ‘\n‘)
                    buf[length - 1] = 0;

                if (strcmp(buf, "Mains") == 0) {
                    snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.acOnlinePath = strdup(path);
                }
                else if (strcmp(buf, "USB") == 0) {
                    snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.usbOnlinePath = strdup(path);
                }
                else if (strcmp(buf, "Wireless") == 0) {
                    snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.wirelessOnlinePath = strdup(path);
                }
                /**
                 * 就是这里了,获取对应的文件路径
                 */
                else if (strcmp(buf, "Battery") == 0) {
                    snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.batteryStatusPath = strdup(path);
                    snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.batteryHealthPath = strdup(path);
                    snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.batteryPresentPath = strdup(path);
                    snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.batteryCapacityPath = strdup(path);

                    snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0) {
                        gPaths.batteryVoltagePath = strdup(path);
                        // voltage_now is in microvolts, not millivolts
                        gVoltageDivisor = 1000;
                    } else {
                        snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
                        if (access(path, R_OK) == 0)
                            gPaths.batteryVoltagePath = strdup(path);
                    }

                    snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0) {
                        gPaths.batteryTemperaturePath = strdup(path);
                    } else {
                        snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
                        if (access(path, R_OK) == 0)
                            gPaths.batteryTemperaturePath = strdup(path);
                    }

                    snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
                    if (access(path, R_OK) == 0)
                        gPaths.batteryTechnologyPath = strdup(path);
                }
            }
        }
        closedir(dir);
    }

    // 判断文件路径是否存在,并给出提示信息
    if (!gPaths.acOnlinePath)
        ALOGE("acOnlinePath not found");
    if (!gPaths.usbOnlinePath)
        ALOGE("usbOnlinePath not found");
    if (!gPaths.wirelessOnlinePath)
        ALOGE("wirelessOnlinePath not found");
    if (!gPaths.batteryStatusPath)
        ALOGE("batteryStatusPath not found");
    if (!gPaths.batteryHealthPath)
        ALOGE("batteryHealthPath not found");
    if (!gPaths.batteryPresentPath)
        ALOGE("batteryPresentPath not found");
    if (!gPaths.batteryCapacityPath)
        ALOGE("batteryCapacityPath not found");
    if (!gPaths.batteryVoltagePath)
        ALOGE("batteryVoltagePath not found");
    if (!gPaths.batteryTemperaturePath)
        ALOGE("batteryTemperaturePath not found");
    if (!gPaths.batteryTechnologyPath)
        ALOGE("batteryTechnologyPath not found");

    // 获取类指针
    jclass clazz = env->FindClass("com/android/server/BatteryService");

    if (clazz == NULL) {
        ALOGE("Can‘t find com/android/server/BatteryService");
        return -1;
    }

    // 获取属性(域)Id
    gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z");
    gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z");
    gFieldIds.mWirelessOnline = env->GetFieldID(clazz, "mWirelessOnline", "Z");
    gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I");
    gFieldIds.mBatteryHealth = env->GetFieldID(clazz, "mBatteryHealth", "I");
    gFieldIds.mBatteryPresent = env->GetFieldID(clazz, "mBatteryPresent", "Z");
    gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I");
    gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, "mBatteryTechnology", "Ljava/lang/String;");
    gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, "mBatteryVoltage", "I");
    gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, "mBatteryTemperature", "I");

    // 如果获取的属性(域)不存在,给出提示信息
    LOG_FATAL_IF(gFieldIds.mAcOnline == NULL, "Unable to find BatteryService.AC_ONLINE_PATH");
    LOG_FATAL_IF(gFieldIds.mUsbOnline == NULL, "Unable to find BatteryService.USB_ONLINE_PATH");
    LOG_FATAL_IF(gFieldIds.mWirelessOnline == NULL, "Unable to find BatteryService.WIRELESS_ONLINE_PATH");
    LOG_FATAL_IF(gFieldIds.mBatteryStatus == NULL, "Unable to find BatteryService.BATTERY_STATUS_PATH");
    LOG_FATAL_IF(gFieldIds.mBatteryHealth == NULL, "Unable to find BatteryService.BATTERY_HEALTH_PATH");
    LOG_FATAL_IF(gFieldIds.mBatteryPresent == NULL, "Unable to find BatteryService.BATTERY_PRESENT_PATH");
    LOG_FATAL_IF(gFieldIds.mBatteryLevel == NULL, "Unable to find BatteryService.BATTERY_CAPACITY_PATH");
    LOG_FATAL_IF(gFieldIds.mBatteryVoltage == NULL, "Unable to find BatteryService.BATTERY_VOLTAGE_PATH");
    LOG_FATAL_IF(gFieldIds.mBatteryTemperature == NULL, "Unable to find BatteryService.BATTERY_TEMPERATURE_PATH");
    LOG_FATAL_IF(gFieldIds.mBatteryTechnology == NULL, "Unable to find BatteryService.BATTERY_TECHNOLOGY_PATH");

    clazz = env->FindClass("android/os/BatteryManager");

    if (clazz == NULL) {
        ALOGE("Can‘t find android/os/BatteryManager");
        return -1;
    }

    /**
     * 获取一些默认值(敞亮),这些值在android/os/BatteryManager有定义
     */
    gConstants.statusUnknown = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_STATUS_UNKNOWN", "I"));

    gConstants.statusCharging = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_STATUS_CHARGING", "I"));

    gConstants.statusDischarging = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_STATUS_DISCHARGING", "I"));

    gConstants.statusNotCharging = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_STATUS_NOT_CHARGING", "I"));

    gConstants.statusFull = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_STATUS_FULL", "I"));

    gConstants.healthUnknown = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNKNOWN", "I"));

    gConstants.healthGood = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_GOOD", "I"));

    gConstants.healthOverheat = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVERHEAT", "I"));

    gConstants.healthDead = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_DEAD", "I"));

    gConstants.healthOverVoltage = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVER_VOLTAGE", "I"));

    gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I"));

    gConstants.healthCold = env->GetStaticIntField(clazz,
            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_COLD", "I"));

    /**
     * 注册更新函数
     */
    return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods));
}

} /* namespace android */
时间: 2024-08-10 23:20:29

I.MX6 android BatteryService jni hacking的相关文章

I.MX6 Linux、Jni ioctl 差异

/*********************************************************************** * I.MX6 Linux.Jni ioctl 差异 * 声明: * 在使用Jni的ioctl()的过程中,发现不能像普通的Linux函数那样使用, * 必须使用3各参数的ioctl()函数. * * 2015-12-20 深圳 南山平山村 曾剑锋 ****************************************************

【转】Android与JNI(二) -- 不错

原文网址:http://www.cnblogs.com/eddy-he/archive/2012/08/09/2629974.html 软件版本: ubuntu10.04 java version "1.6.0_30-ea" eclipse android-ndk-r5b 目录: 1. 简介 2. JNI 组件的入口函数 3. 使用 registerNativeMethods 方法 4. 测试 5. JNI 帮助方法 6. 参考资料 1. 简介 Android与JNI(一)已经简单介绍

Android通过JNI实现与C语言的串口通讯操作蓝牙硬件模块

一直想写一份技术文档,但因为自感能力有限而无从下笔,近期做了个关于Android平台下实现与C语言的通讯来操作蓝牙模块的项目,中间碰到了很多问题,也在网上查了很多资料,在完毕主要功能后.也有一些人在网上问我一些问题.这里写下这篇文档算是一个阶段性的总结. 假设反响好.兴许将会发上Android Stub与新版Android HAL的学习文档. 因为蓝牙模块是串口通讯机制.使用C语言来訪问,而Android的应用层採用Java.无法直接操作硬件.故使用JNI的技术实现主要功能.Android的JN

Android使用JNI(从java调用本地函数)

当编写一个混合有本地C代码和Java的应用程序时,需要使用Java本地接口(JNI)作为连接桥梁.JNI作为一个软件层和API,允许使用本地代码调用Java对象的方法,同时也允许在Java方法中调用本地函数. 在Java端,开发者所需要做的仅仅是在连接本地函数的方法之前加上native关键字.这样VM就会去寻找这个本地函数. 1.从Java调用本地函数 从Java调用本地函数时,需要在类中定义一个带有native关键字的特有方法,作为连接本地代码的桥梁.通过这个定义,尝试调用本地方法时JVM会找

Android之——JNI初探

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/47405683 这里,我将用一个小例子的形式来帮助大家初探JNI的用法,首先,大家要先搭建好NDK环境,请大家先阅读<Android之--NDK环境搭建>一文. 一.实现 这个小例子实现的功能就是,通过Android中的java代码来调用C代码实现java代码与C代码之间的交互. 1.布局文件 我们首先在布局文件activity_main.xml中,添加一个按钮控件,并给按钮控件

[Android] 图片JNI(C++\Java)高斯模糊 多线程

在我的博客中,曾经发布了一篇高斯模糊(堆栈模糊)的文章:在其中使用了国外的一个堆栈模糊来实现对图片的模糊处理:同时弄了一个JNI C++ 的版本. 这篇文章依然是堆栈模糊:可以说最原始的地方还是堆栈模糊部分:只不过是支持多线程的. 纳尼??感情是之前那个不支持多线程?Sorry,我说错了:两个都是支持多线程调用的.不过新讲的这个是能在内部采用多线程进行分段模糊. 原来的:[Android]-图片JNI(C++\Java)高斯模糊的实现与比较 开工吧 说明:其中代码大部分来源于网络,不过都是开源的

Android - Android调用JNI方法 及 代码

Android调用JNI方法 及 代码 本文地址: http://blog.csdn.net/caroline_wendy JNI: Java Native Interface, 实现Java和C/C++的互通. 在Android上使用JNI的方法. 时间:2014.9.3 环境: 必须使用标准Eclipse, 安装Android的环境, 才可以使用NDT插件. Eclipse Standard/SDK Version: Luna Release (4.4.0); Android: ADT-23

OpenCV4Android释疑: 透析Android以JNI调OpenCV的三种方式(让OpenCVManager永不困扰)

前文曾具体探讨了关于OpenCV的使用,原本以为天下已太平.但不断有人反应依旧配不好OpenCV4Android,不能得心应手的在Android上使用OpenCV.大量的精力都浪费在摸索配置上.尤其是OpenCVManager诞生之后.更让人无语.大家第一个反应就是怎样才干不安装OpenCVManager.由于要多安装这个东西对客户来说体验太不好了. 咱家昨夜研究至两点,今早七点起床.最终把头绪理清了. 以下咱家以之前做过的一个基于OpenCV2.3.1.android通过jni调用opencv

I.MX6 android 4.2 源码下载

/************************************************************************* * I.MX6 android 4.2 源码下载 * 说明: * NXP官网已经不提供4.2版本的源码下载了,目前这个地方还能下载. * * 2016-8-24 深圳 南山平山村 曾剑锋 ************************************************************************/ 一.参考文档: