在Android应用程序中,我们可以通过ConnectivityManager.setProcessDefaultNetwork()来限制当前进程可访问的网络。对于native app,我们有没有办法对native进程进行限制呢?通过什么方法进行限制?
- 环境
Android系统版本:android-5.1.1_r15 @Nexus 4, 在项目中编译。
- 代码
一段很简单的代码,发起http请求:
int try_to_get_http_url(const char *hostname, const char *url) { int len; char request[256], response[4096]; int fd = socket_network_client(hostname, 80, SOCK_STREAM); if (fd < 0) { printf("Failed to connect: %s, errno=%d(%s)\n", hostname, errno, strerror(errno)); return fd; } len = sprintf(request, "GET %s HTTP/1.1\r\nHost: %s\r\n\r\n", url, hostname); len = write(fd, request, len); if (len <= 0) { printf("Failed to send request: %s, errno=%d(%s)\n", request, errno, strerror(errno)); close(fd); return len; } len = read(fd, response, sizeof(response) - 1); if (len <= 0) { printf("Failed to get response, errno=%d(%s)\n", errno, strerror(errno)); close(fd); return len; } response[len] = '\0'; printf(response); return close(fd); } int main(int argc, const char *argv[]) { return try_to_get_http_url("brobwind.com", "/"); }
执行之后,可以从log中得到如下信息:
03-06 15:33:18.649 14917 14917 E hello-native: HTTP/1.1 301 Moved Permanently 03-06 15:33:18.649 14917 14917 E hello-native: Date: Sun, 06 Mar 2016 15:33:20 GMT 03-06 15:33:18.649 14917 14917 E hello-native: Server: Apache/2.4.7 (Ubuntu) 03-06 15:33:18.649 14917 14917 E hello-native: X-Powered-By: PHP/5.5.9-1ubuntu4.14 03-06 15:33:18.649 14917 14917 E hello-native: Location: http://www.brobwind.com/ 03-06 15:33:18.649 14917 14917 E hello-native: Content-Length: 0 03-06 15:33:18.649 14917 14917 E hello-native: Content-Type: text/html; charset=UTF-8
试试这种方法,在JNI代码中去回调JAVA代码:
1. JNI相关代码:
#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ LOG_FATAL_IF(! var, "Unable to find method" methodName); static struct { jobject thiz; jmethodID nativeSetProcessNetwork; } gNativeApplication; extern "C" void Java_com_brobwind_brontv_v1_NativeApplication_nNativeSetup(JNIEnv *env, jclass clazz, jobject thiz) { gNativeApplication.thiz = env->NewGlobalRef(thiz); GET_METHOD_ID(gNativeApplication.nativeSetProcessNetwork, clazz, "nativeSetProcessNetwork", "()V"); } extern "C" void Java_com_brobwind_brontv_v1_NativeApplication_nSayHello(JNIEnv *env, jclass, jstring binDir) { pid_t pid; char path[PATH_MAX]; ScopedUtfChars _binDir(env, binDir); sprintf(path, "%s/hello-native", _binDir.c_str()); pid = fork(); if (pid == 0) { // Child process env->CallVoidMethod(gNativeApplication.thiz, gNativeApplication.nativeSetProcessNetwork); env->DeleteGlobalRef(gNativeApplication.thiz); exit(execl(path, path, NULL)); } }
2. JAVA相关代码:
public class NativeApplication extends Application { private static final String TAG = "NativeApp"; static { System.loadLibrary("native-jni"); } @Override public void onCreate() { nNativeSetup(this); final File binDir = new File(getApplicationInfo().dataDir, "/bin"); binDir.mkdir(); binDir.setReadable(true, false); try { File binName = new File(binDir, "hello-native"); binName.delete(); if (binName.exists() != true) { AssetManager am = getAssets(); InputStream is = am.open("bin/armeabi-v7a/" + binName.getName()); FileUtils.copyToFile(is, binName); FileUtils.setPermissions(binName, FileUtils.S_IRUSR | FileUtils.S_IXUSR, -1, -1); } } catch (IOException e) { e.printStackTrace(); } nSayHello(binDir.getPath()); } public void nativeSetProcessNetwork() { ConnectivityManager cm = (ConnectivityManager)getSystemService(CONNECTIVITY_SERVICE); Network allNetworks[] = cm.getAllNetworks(); for (Network network : allNetworks) { NetworkInfo info = cm.getNetworkInfo(network); if (info.getType() == ConnectivityManager.TYPE_WIFI) { // cm.bindProcessToNetwork(network); boolean setOk = cm.setProcessDefaultNetwork(network); if (!setOk) { Log.e(TAG, " -> Failed to set process default network: " + network); } break; } } } private static native void nNativeSetup(NativeApplication thiz); private static native void nSayHello(String binDir); }
看来这种方法不可行:
03-06 16:24:24.331 17117 17117 F libc : Fatal signal 11 (SIGSEGV), code 1, fault addr 0xb3fcf038 in tid 17117 (bwind.brontv.v1) 03-06 16:24:24.433 187 187 I DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** 03-06 16:24:24.433 187 187 I DEBUG : Build fingerprint: 'Android/aosp_mako/mako:5.1.1/LMY48T/rwchen03041349:userdebug/test-keys' 03-06 16:24:24.433 187 187 I DEBUG : Revision: '11' 03-06 16:24:24.433 187 187 I DEBUG : ABI: 'arm' 03-06 16:24:24.433 558 674 W NativeCrashListener: Couldn't find ProcessRecord for pid 17117 03-06 16:24:24.433 187 187 I DEBUG : pid: 17117, tid: 17117, name: bwind.brontv.v1 >>> com.brobwind.brontv.v1 <<< 03-06 16:24:24.433 187 187 E DEBUG : AM write failure (32 / Broken pipe) 03-06 16:24:24.433 187 187 I DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xb3fcf038 03-06 16:24:24.444 187 187 I DEBUG : r0 00000000 r1 00000000 r2 00000000 r3 00000000 03-06 16:24:24.444 187 187 I DEBUG : r4 00000010 r5 00000001 r6 b7f2dd50 r7 b3fcf038 03-06 16:24:24.444 187 187 I DEBUG : r8 b3fcf028 r9 b7f2d980 sl b6e31255 fp 00007205 03-06 16:24:24.444 187 187 I DEBUG : ip b6dadd7c sp be8cfb40 lr b6da603c pc b6e3444e cpsr 800b0030 03-06 16:24:24.445 187 187 I DEBUG : 03-06 16:24:24.445 187 187 I DEBUG : backtrace: 03-06 16:24:24.445 187 187 I DEBUG : #00 pc 0002244e /system/lib/libbinder.so (android::Parcel::ipcSetDataReference(unsigned char const*, unsigned int, unsigned int const*, unsigned int, void (*)(android::Parcel*, unsigned char const*, unsigned int, unsigned int const*, unsigned int, void*), void*)+61) 03-06 16:24:24.445 187 187 I DEBUG : #01 pc 0001fa3f /system/lib/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)+206) 03-06 16:24:24.445 187 187 I DEBUG : #02 pc 0001fb3d /system/lib/libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+124) 03-06 16:24:24.445 187 187 I DEBUG : #03 pc 0001ad23 /system/lib/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+30) 03-06 16:24:24.445 187 187 I DEBUG : #04 pc 000877bf /system/lib/libandroid_runtime.so 03-06 16:24:24.445 187 187 I DEBUG : #05 pc 00c1221d /data/dalvik-cache/arm/system@framework@boot.oat
我们再回看一下ConnectivityManager.setProcessDefaultNetwork()做了些什么:->@<-
public static boolean setProcessDefaultNetwork(Network network) { int netId = (network == null) ? NETID_UNSET : network.netId; if (netId == NetworkUtils.getNetworkBoundToProcess()) { return true; } if (NetworkUtils.bindProcessToNetwork(netId)) { // Set HTTP proxy system properties to match network. // TODO: Deprecate this static method and replace it with a non-static version. Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy()); // Must flush DNS cache as new network may have different DNS resolutions. InetAddress.clearDnsCache(); // Must flush socket pool as idle sockets will be bound to previous network and may // cause subsequent fetches to be performed on old network. NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged(); return true; } else { return false; } }
1. netId: 这个好办
2. NetworkUtils.bindProcessToNetwork(): 这个方法实际上直接调用的是native的setNetworkForProcess()这个也没问题
3. Proxy.setHttpProxySystemProperty(): 这个可以先暂时不管
4. InetAddress.clearDnsCache(): 貌似也可以不管
=== 待续 ===