Porting of cURL to Android OS using NDK

 
In continuing my journey into Android territory, I decided it would be useful to understand the NDK development kit.  Given I want to transfer some files and possibly do a couple of other projects requiring low level work, I selected the cURL kit to port.  I've used cURL for a number of projects and it is my hope it runs well under Android.  The project, curltest, was run under Android 2.1 but it should work for other versions as well.  Given that most Android phones will be getting upgrades rolled out over the next couple of months to version 2.1, seems like a reasonable target.  I will be added HTTPS capability as soon as I port openssl, for now, this is strictly unsecured transfers.

I did see some examples of partial porting and even in the curl library source there is an Android.mk which describes how to set it up although not in enough detail.  Both work helpful but didn't actually get me to the holy land of a usable test applications. (http://www.mail-archive.com/curl-library@cool.haxx.se/msg03433.html was helpful but didn't work for me.)

I found it worth the exercise of downloading the full android sources and building them.  Look at the build process, the bionic library, and well as whats in external.  The external directory is where you would add repositories to do things like run configure, etc

You won't actually need Linux since I have included the config file here but in case you want to follow way it was created, I've included it.

Summary of the steps to take for this port:
  - Lots of searches and view the current state of curl porting
  - Setup source repository (I used Linux, you could try windows using cygwin)
  - Create curl_config.h under the full android source tree
  - Untar curltest.tar.gz under $NDKROOT or Create NDK JNI App on Windows
  - Cross-link or add to eclipse project to build Java front-end
  - Unpack cURL into $NDKROOT/apps/curltest/jni/curl
  - Copy curltest/jni/curlbuild.h to curltest/jni/curl/include/curl
  - Copy curltest/jni/curl_config.h to curltest/jni/curl/lib
  - Build and test

Setup source repository on Linux

It's one thing to describe doing X, quite another to actually do it.  Anyway, I ended up creating my own android repository to start the process.  While I was using Windows 7 for my NDK development, I switched to Linux to create the curl_config.h file needed in the build.  What this means it that you run a couple of scripts.  First up, repo.  Created a directory and cd to it.  Download the repo script 'wget https://android.git.kernel.org/repo'.   Move repo script to somewhere in your path and issue 'repo init -u git://android.git.kernel.org/platform/manifest.git'  then after run 'repo sync'.  These two should get you all the goodies.  I had some stalls where I had to restart the sync process but after a long while it completed successfully.

Create curl_config.h under the full android source tree

Next, I created a script to call the configure command to generated the curl_config.h.  CD to  $ANDROID_ROOT/external.  Untar curl libraries, I renamed my from curl-7.20.1 to just curl.  cURL has an Android.mk inside of it.  In the file were some instructions for generating the curl_config.h.  I found them helpful as a start but ultimately I had to fiddle with a number of things to get this to actually work.  Here is the script I used:

#!/bin/sh

ANDROID_ROOT="$HOME/android_src" && \
TOOLCHAIN_VER="4.4.0"  \
PLATFORM_VER="5" \
CROSS_COMPILE=arm-eabi- \
PATH=$ANDROID_ROOT/prebuilt/linux-x86/toolchain/arm-eabi-$TOOLCHAIN_VER/bin:$PATH  && \
CPPFLAGS="-I $ANDROID_ROOT/system/core/include -I$ANDROID_ROOT/bionic/libc/include -I$ANDROID_ROOT/ndk/build/platforms/android-5/arch-arm/usr/include -I$ANDROID_ROOT/bionic/libc/kernel/arch-arm -L $ANDROID_ROOT/prebuilt/linux-x86/toolchain/arm-eabi-$TOOLCHAIN_VER/lib/gcc/arm-eabi/$TOOLCHAIN_VER/interwork -L$ANDROID_ROOT/ndk/build/platforms/android-$PLATFORM_VER/arch-arm/usr/lib  -L$ANDROID_ROOT/out/target/product/generic/system/lib " \
CFLAGS="-fno-exceptions -Wno-multichar -mthumb -mthumb-interwork -nostdlib -lc -ldl -lm "  \
./configure CC=arm-eabi-gcc --host=arm-linux --disable-tftp --disable-sspi --disable-ipv6 --disable-ldaps --disable-ldap --disable-telnet --disable-pop3 --disable-ftp --without-ssl --disable-imap --disable-smtp --disable-pop3 --disable-rtsp --disable-ares --without-ca-bundle --disable-warnings --disable-manual --without-nss --enable-shared --without-zlib --without-random

# openssl/zlib version
#./configure CC=arm-eabi-gcc --host=arm-linux --disable-tftp --disable-sspi --disable-ipv6 --disable-ldaps --disable-ldap --disable-telnet --disable-pop3 --disable-ftp --with-ssl=$ANDROID_ROOT/external/openssl --disable-imap --disable-smtp --disable-pop3 --disable-rtsp --disable-ares --without-ca-bundle --disable-warnings --disable-manual --without-nss --enable-shared --with-zlib=$ANDROID_ROOT/external/zlib --without-random

Run the script in the top curl directory (where configure resides) and it should go through many tests before ultimately creating the lib/curl_config.h file.

Create NDK JNI App on Windows

Untar curltest.tar.gz under $NDKROOT.  This has the Java side application already created.  Or you can simply create a new Application under Eclipse which will generated the src & res directories that will need to be modified.  I'm not going into App generation except that you will target $NDKROOT/apps/curltest/project as your directory tree.

Create library name under $NDKROOT/apps/, I used curltest.
CD to curltest and create the Application.mk file.
APP_PROJECT_PATH := $(call my-dir)/project
APP_MODULES := curltest libcurl

Once that is done, cd to project and create your standard Java AndroidManifest.xml file.  If you created an App from Eclipse you'll need to modify this file.  Make sure to give it INTERNET permissions.  The rest depends on what's in your Java code.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="/apk/res/android"
      android:versionCode="1"
      android:versionName="1.0" package="com.mtterra.curltest">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".curltest"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

</application>
    <uses-sdk android:minSdkVersion="5"/>
    <uses-permission android:name="android.permission.INTERNET"/>

</manifest>

Then cd to jni and create the Android.mk file.  This is where the one from cURL would go.  However, I didn't use it as anything more than a reference.

Now for the actual JNI interfacing.  There are lots of complaints here because everything needs to be specified up front and you need different packages for different hardware.  Except for some dynamic registration mechanism, I'm not sure you can get away with writing to hardware AND getting independence from it.  Actually, I am sure, you can't.

Here is my curltest.c interface:

typedef struct pageInfo_t {
char *data;
int len;
} pageInfo_t;

static size_t
HTTPData(void *buffer, size_t size, size_t nmemb, void *userData) {
int len = size * nmemb;
pageInfo_t *page = (pageInfo_t *)userData;

if (buffer &amp;&amp; page-&gt;data &amp;&amp; (page-&gt;len + len &lt; (16 * 1024)) ) { memcpy(&amp;page-&gt;data[page-&gt;len], buffer, len);
page-&gt;len += len;
}
return len;
}

//Interface function that will receive web page from Java
jstring
Java_com_mtterra_curltest_curltest_JNIGetWebpage( JNIEnv* env,
jobject entryObject,
jstring webpageJStr )
{
pageInfo_t page;
CURL *curl;
CURLcode res;
char *buffer;

const jbyte *webpage;
webpage = (*env)-&gt;GetStringUTFChars(env, webpageJStr, NULL);
if (webpage == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}

page.data = (char *)malloc(16 * 1024);
page.len = 0;
if (page.data)
memset(page.data, 32, 16 * 1024);

buffer = (char *)malloc(1024);

curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, webpage);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HTTPData);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &amp;page);

res = curl_easy_perform(curl);
/* always cleanup */
curl_easy_cleanup(curl);
(*env)-&gt;ReleaseStringUTFChars(env, webpageJStr, webpage);
if(res == 0) {
if (buffer) {
page.data[page.len &lt; 256 ? page.len : 256] = '\0'; sprintf(buffer, "pagedata(%d): %s. done.\n", page.len, page.data); return (*env)-&gt;NewStringUTF(env, buffer);
}
}
sprintf(buffer, "Result %d", res);
return (*env)-&gt;NewStringUTF(env, buffer);
} else {
return (*env)-&gt;NewStringUTF(env, "Unable to init cURL");
}
}

Finally, it's time to setup Eclipse to build.  There are some assumptions here, like having a functioning Eclipse build environment.  In order to get your environment over to Eclipse, you can use File-&gt;Import then select 'Existing Projects into Workspace' function.

Unpack cURL into $NDKROOT/apps/curltest/jni/curl.  Move the curl_config.h file created above to $NDKROOT/apps/curltest/jni/curl/lib.

Finally, we create the Android.mk file in curltest/jni and build it.  The Android.mk use is as follows:

LOCAL_PATH:= $(call my-dir)

CFLAGS := -Wpointer-arith -Wwrite-strings -Wunused -Winline \
 -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wno-long-long \
 -Wfloat-equal -Wno-multichar -Wsign-compare -Wno-format-nonliteral \
 -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement \
 -Wno-system-headers -DHAVE_CONFIG_H

include $(CLEAR_VARS)
include $(LOCAL_PATH)/curl/lib/Makefile.inc

LOCAL_SRC_FILES := $(addprefix curl/lib/,$(CSOURCES))
LOCAL_CFLAGS += $(CFLAGS)
LOCAL_C_INCLUDES += $(LOCAL_PATH)/curl/include/

LOCAL_COPY_HEADERS_TO := libcurl
LOCAL_COPY_HEADERS := $(addprefix curl/include/curl/,$(HHEADERS))

LOCAL_MODULE:= libcurl

include $(BUILD_STATIC_LIBRARY)

# Build shared library now
# curltest

include $(CLEAR_VARS)

LOCAL_MODULE := curltest
LOCAL_SRC_FILES := curltest.c
LOCAL_STATIC_LIBRARIES := libcurl
LOCAL_C_INCLUDES += $(LOCAL_PATH)/curl/include
include $(BUILD_SHARED_LIBRARY)

Going back to your $NDKROOT you should be able to build the library with 'make APP=curltest -B V=1'.  The -B means rebuilt it all and V=1 makes it verbose.

After this, you should be able to build the application.  When I get back to Eclipse I'll hit F5 to refresh the files as a habit although likely not necessary.  Another interesting thing is that some people have indicated that you need to do an 'adb push' to the emulator so the library is there.  I haven't found this is the case.  Basically, if it doesn't find your library the interface name or some other part is broken and you need to find the issue.

The finished result looks something like:

Here is the NDK project files used.  However, you'll have to add the curl sources to this.

The Software Rogue - http://thesoftwarerogue.blogspot.com

 
Posted by

Bruce Smith

at
12:50 AM

Porting of cURL to Android OS using NDK (from The Software Rogue)的更多相关文章

  1. 【android 开 发 】 - Android studio 下 NDK Jni 开发 简单例子

    Android 开发了一段时间,一方面 ,感觉不留下点什么.有点对不起自己, 另一方面,好记性不如烂笔头,为了往后可以回头来看看,就当做是笔记,便决定开始写博客.废话不多说 ! 今天想搞一搞 ndk ...

  2. Android应用---基于NDK的samples例程hello-jni学习NDK开发

    Android应用---基于NDK的samples例程hello-jni学习NDK开发 NDK下载地址:http://developer.android.com/tools/sdk/ndk/index ...

  3. 【Android Studio安装部署系列】二十五、Android studio使用NDK生成so文件和arr文件

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 Android Studio使用ndk的简单步骤. NDK环境搭建 下载NDK 下载链接:https://developer.and ...

  4. [Android Studio] Using NDK to call OpenCV

    NDK才是Android开发通向超高薪之路.(这句话,似乎四年前有云) 难点在于常用的non-free module (sift and surf) unsw@unsw-UX303UB$ pwd /h ...

  5. [ 转载 ] Android JNI(一)——NDK与JNI基础

    Android JNI(一)——NDK与JNI基础 隔壁老李头 关注  4.4 2018.05.09 17:15* 字数 5481 阅读 11468评论 8喜欢 140 本系列文章如下: Androi ...

  6. Android JNI(一)——NDK与JNI基础

    本系列文章如下: Android JNI(一)——NDK与JNI基础 Android JNI学习(二)——实战JNI之“hello world” Android JNI学习(三)——Java与Nati ...

  7. Android Studio 之 NDK篇

    由于工作内容的关系,对于NDK的工作涉及比较广(保密性,安全性),所以本章内容讲述一下NDK的基本使用过程. 网上也有很多这样的教程或者描述,但描述的并不完全 开发工具:Android Studio ...

  8. Android JNI和NDK学习(03)--动态方式实现JNI(转)

    本文转自:http://www.cnblogs.com/skywang12345/archive/2013/05/23/3092491.html 前面总结了静态实现JNI的方法,本文介绍如何动态实现J ...

  9. Android JNI和NDK学习(02)--静态方式实现JNI(转)

    本文转自:http://www.cnblogs.com/skywang12345/archive/2013/05/23/3095013.html JNI包括两种实现方法:静态和动态.两种方法的区别如下 ...

随机推荐

  1. MySQL用户授权

    一.授权语法格式  grant 权限列表 on 数据库名.表名 to '用户名'@'客户端主机' [identified by '密码']; 单词: privileges [ˈprivilidʒz] ...

  2. Linux内核分析第一周学习博客 --- 通过反汇编方式学习计算机工作过程

    Linux内核分析第一周学习博客 通过反汇编方式学习计算机工作过程 总结: 通过这次对一个简单C程序的反汇编学习,我了解到计算机在实际工作工程中要涉及大量的跳转指针操作.计算机通常是顺序执行一条一条的 ...

  3. acid(数据库事务正确执行的四个基本要素的缩写)

    ACID,指数据库事务正确执行的四个基本要素的缩写.包含:原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持久性(Durability).一个支持事务(T ...

  4. git push要输入密码问题

    git push突然每次都要输入密码了,这个问题困扰了两天,要无密码push,要保证两点. 1.  git clone的url一定得是git开头的,不能是https开头的,这个容易被忽略,github ...

  5. HTTP ------ connection 为 close 和 keep-alive 的区别

    keep-alive和close这个要从TCP握手讲起 HTTP请求是基于TCP连接的,TCP的请求会包含(三次握手,中间请求,四次挥手)在HTTP/1.0时代,一个HTTP请求就要三次握手和四次挥手 ...

  6. 使用cron命令配置定时任务(cron jobs)

    原文 http://www.cnblogs.com/end/archive/2012/02/21/2361741.html 开机就启动cron进程的设置命令:chkconfig --add crond ...

  7. JS中encodeURI、encodeURIComponent、decodeURI、decodeURIComponent

    js 对文字进行编码涉及2个函数:encodeURI,encodeURIComponent,相应2个解码函数:decodeURI,decodeURIComponent 1.用来编码和解码URI的 统一 ...

  8. UVA 1390 Interconnect

    https://vjudge.net/problem/UVA-1390 题意: 给出n个点m条边的无向图, 每次随机加一条非自环的边,(加完后可出现重边), 添加每条边的概率是相等的 求使图连通的期望 ...

  9. web项目中的 log4net的配置

    最近用log4net,网上查了很多资料,照着网上的配置大多都不管用,可能我还是有什么地方配置的不对.看出来的朋友平指出.下面是我自己亲测的,可以用! 1.web项目中的web.config 配置log ...

  10. 【BZOJ】3143: [Hnoi2013]游走 期望+高斯消元

    [题意]给定n个点m条边的无向连通图,每条路径的代价是其编号大小,每个点等概率往周围走,要求给所有边编号,使得从1到n的期望总分最小(求该总分).n<=500. [算法]期望+高斯消元 [题解] ...