Android: 限定app可访问的网络 – 限制native进程

在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():  貌似也可以不管

=== 待续 ===

发表评论

电子邮件地址不会被公开。 必填项已用*标注