Android使用socket

背景

修改andorid framework,希望新增一个socket 与自定义的程序进行信息交互

实现代码

服务端
//LocalSocket graal_start_sock_raw = new LocalSocket(LocalSocket.SOCKET_SEQPACKET);
Process.ProcessStartResult my_spec_result = new Process.ProcessStartResult();

try {
LocalServerSocket graal_start_sock_server = new LocalServerSocket("_sock_graal");
LocalSocket graal_start_sock = graal_start_sock_server.accept();
    BufferedWriter cmd_out_stream =  new BufferedWriter(
                        new OutputStreamWriter(graal_start_sock.getOutputStream()),
                        Zygote.SOCKET_BUFFER_SIZE);
    DataInputStream cmd_in_stream = new DataInputStream(graal_start_sock.getInputStream());

//seee startViaZygote in ZygoteProcess.java
ArrayList args_for_graal = new ArrayList<>();
args_for_graal.add(“seq=” + app.startSeq);
String msgStr = args_for_graal.size() + “\n” + String.join(“\n”, args_for_graal) + “\n”;
cmd_out_stream.write(msgStr);

读取端
LocalSocket graal_start_sock = new LocalSocket();
long seq = -1;
try {
graal_start_sock.connect(
new LocalSocketAddress(“_sock_graal”,
LocalSocketAddress.Namespace.ABSTRACT));

    BufferedReader cmd_in_stream = new BufferedReader(
        new InputStreamReader(graal_start_sock.getInputStream()), 256);
    DataOutputStream cmd_out_stream = new DataOutputStream(graal_start_sock.getOutputStream());
    String[] args = readArgumentList(cmd_in_stream);
    seq = get_seq_from_args(args);

    cmd_out_stream.writeInt((int)android.os.Process.myPid());
    cmd_out_stream.flush();
} catch (IOException ex) {
    graal_start_sock = null;
    ex.printStackTrace();

    return -1;
}

问题

读取端总是报连接被拒绝,使用如下测试用例,尝试规避https://www.v2ex.com/t/738374 提到的addr size问题,仍然无法解决问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#define SOKET_NAME "@_sock_graal"
//#define SOKET_NAME "@_spec_sock_for_graal"

int connect_test() {
char* name = SOKET_NAME;
struct sockaddr_un addr;

// add this variant
int alen;

int sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("failed to create socket");
return -1;
}

memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOKET_NAME, sizeof(addr.sun_path)-1);
if (name[0] == '@')
addr.sun_path[0] = '\0';

// Added this line
alen = offsetof(struct sockaddr_un, sun_path) + strlen(name);
int cur_len = alen;
for (;cur_len < 32; cur_len++)
{
int ret = connect(sock, (struct sockaddr *)&addr, cur_len);
char buf[1024] = {0};
if (ret == 0)
{
printf("success with %d\n", cur_len);
read(sock, buf, 1024);
return 1;
}
else
{
perror("failed to connect");
}

}
/*
// change sizeof(addr) to alen
if (connect(sock, (struct sockaddr *)&addr, alen) < 0) {
perror("failed to connect");
close(sock);
return -1;
}
*/
return 0;
}

int main()
{
return connect_test();
}

并且使用lsof -p 查看服务端pid,可以看到确实已经成功的出现了新建的socket端口。
查看logcat,发现这里是触发了android 上的StrictMode
保护。可参考https://droidyue.com/blog/2015/09/26/android-tuning-tool-strictmode/ ,简单的说,就是在UI线程中不允许使用wait/connect这样可能会引起阻塞的操作。
由于使用C程序测试无法收到异常消息,这里只能看到连接被拒绝。

问题解决

知道问题后,可以参考frameworks/base/services/core/java/com/android/server/am/ProcessList.java的如下片段。
临时性开放权限,运行在消息循环中进行socket动作即可。

1
2
3
4
   StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
android.os.StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().permitNetwork().build());
//执行打开socket的流程。。。。
android.os.StrictMode.setThreadPolicy(oldPolicy);

参考动作

1
2
3
4
5
6
7
8
9
10
11
12
       if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
String wrapperFileName = app.info.nativeLibraryDir + "/wrap.sh";
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
try {
if (new File(wrapperFileName).exists()) {
invokeWith = "/system/bin/logwrapper " + wrapperFileName;
}
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
}