sheldon_blogs

Android : App通过LocalSocket 与 HAL间通信

  LocalSocket其通信方式与Socket差不多,只是LocalSocket没有跨越网络边界。对于*nix系统来说,“一切皆为文件”,Socket也不例外,Socket按照收发双方的媒介来说有三种类型:

  1,通过网络端口: 即通过本地回环接口(即LoopBack)127.0.0.1来收发数据;

  2,通过文件系统: 通过文件作为收发数据的中转站;

  3,通过内存映射文件:在内存中开辟一块区域作为收发数据的中转站,此区域仍然使用文件读写API进行访问;

  LocalSocket支持方式2和方式3。

 

以下通过HAL层(c)作为server,App端(java)作为client,进行LocalSocket通信演示(核心部分代码):

C代码:

    J_U8* sName="nano_server_socket";
    J_Int server_socketfd, client_socketfd;  
    J_Int server_len, client_len;
    J_Int reuse = 1;
    J_Int err;
   struct sockaddr_un server_address; /*声明一个UNIX域套接字结构*/  
    struct sockaddr_un client_address;  

    unlink (sName); /*删除原有server_socket对象*/

    /*创建 socket, 通信协议为AF_LOCAL, SOCK_STREAM 数据方式*/    
    server_socketfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    if(server_socketfd < 0){
        ALOGE("server_socketfd error : %s\n",strerror(errno));
        return;
    }

    /*配置服务器信息(socket对象路径)*/
    // Check with length +1 for the *initial* '\0'.
    if ((strlen(sName) + 1) > sizeof(server_address.sun_path)) {
        ALOGE("name too long\n");
        goto EXIT;
    }

    /*
     * Note: The path in this case is *not* supposed to be
     * '\0'-terminated. ("man 7 unix" for the gory details.)
     */    
     server_address.sun_path[0] = 0;
     memcpy(server_address.sun_path + 1, sName, strlen(sName));

     /*配置服务器信息(通信协议)*/  
     server_address.sun_family = AF_LOCAL;

     /*默认设置resue FLAG*/
     if (setsockopt(server_socketfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
         ALOGE("reuse error\n");
         goto EXIT;
     }

    /*配置服务器信息(服务器地址长度)*/  
    client_len = server_len = strlen(sName) + offsetof(struct sockaddr_un, sun_path) + 1;

    /*绑定 socket 对象*/  
    bind (server_socketfd, (struct sockaddr *)&server_address, server_len);  
      
    /*监听网络,队列数为1*/  
    if(listen(server_socketfd,1)<0){
        ALOGE("listen error : %s\n",strerror(errno));
        goto EXIT;
    }
    /*接受客户端请求; 第2个参数用来存储客户端地址; 第3个参数用来存储客户端地址的大小*/    
    /*建立(返回)一个到客户端的文件描述符,用以对客户端的读写操作*/  
    client_socketfd = accept (server_socketfd, (struct sockaddr *)&client_address, (socklen_t *)&client_len);
   if (client_socketfd == -1) { ALOGE("accept error : %s\n",strerror(errno)); goto EXIT; } else if (client_socketfd> FD_SETSIZE){ ALOGE("accept reach max\n"); close(client_socketfd); goto EXIT; }
   /*start read loop*/ 
   while(1) {

      char buf[1024]={0};
      ssize_t res=read(client_socketfd,buf,sizeof(buf));

      ALOGD("server get data : %s", buf);

      if(res <=0){
        if(errno == EAGAIN)
          continue;
        ALOGE("errno %s\n",strerror(errno));
        close(client_socketfd);
        return;
       }

    }

EXIT:
    close(server_socketfd);

HAL层可具体参考Android源码的 system\bt\osi\src\socket_utils\ 目录下的 socket_local_client.cc 和 socket_local_server.cc 代码(Android 8.0),

直接调用如下封装好的接口即可:

/***********/
/***服务端***/
/**********/
/*(1)创建server socket接收client端数据*/    
server_socketfd = osi_socket_local_server("server_socket",ANDROID_SOCKET_NAMESPACE_ABSTRACT,SOCK_STREAM); 
/*(2)等待client端连接*/
client_socketfd = accept (server_socketfd, NULL, NULL);
/*(3)数据读取线程*/
while(1){
    ssize_t res=read(client_socketfd ,buf,sizeof(buf));
}  

/***********/
/***客户端***/
/**********/
/*(1)创建client socket */    
client_socketfd = osi_socket_local_client("client_socket",ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
/*(2)发送数据到server端 */    
write(client_socketfd, "hello", strlen("hello"));

 

 

JAVA代码:

package com.example.administrator.localsocket;

import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

import java.io.IOException;

import static android.widget.Toast.LENGTH_SHORT;

public class MainActivity extends AppCompatActivity {

    private static final String SOCKET_ADDRESS = "nano_server_socket"; //和HAL层统一地址名称

    LocalSocket client_socket = null;
    LocalSocketAddress addr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        client_socket = new LocalSocket(); //创建socket对象
        addr = new LocalSocketAddress(SOCKET_ADDRESS,LocalSocketAddress.Namespace.ABSTRACT); //使用虚空间地址
        try {
            client_socket.connect(addr);  //请求连接
        } catch (IOException e) {
            e.printStackTrace();
        }

        findViewById(R.id.sendMsg).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    //发送数据
                    String data="hello";
                    client_socket.getOutputStream().write(data.getBytes());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
   }
@Override
protected void onStop() { super.onStop(); try { client_socket.close(); } catch (IOException e) { e.printStackTrace(); } } }

:Android8.0版本验证app端要用LocalSocket,需要两个条件:

   1.apk需要签名成platform_app; (方法可参考: https://www.cnblogs.com/blogs-of-lxl/p/9233285.html )

   2.需要给platform_app增加selinux权限,修改xxx\sepolicy\platform_app.te,添加如下两条规则:

    typeattribute platform_app mlstrustedsubject;
    allow platform_app audioserver:unix_stream_socket connectto;

 

测试结果:

 

posted on 2018-06-27 21:34  sheldon_blogs  阅读(2324)  评论(0编辑  收藏  举报

导航