zoukankan      html  css  js  c++  java
  • Android与.Net交互模拟用户屏幕操作添加APN和网络4G/3G切换

      前几天接到一个需求,我们的客户需要对手机网络接入点进行可用性测试,简单点说就是需要实现Android上的APN配置的添加,APN切换网络模式4G/3G/2G切换,我要调研下写个demo。

      因为是要实现自动化测试,而且得合并到现有的拨测系统(C#项目)成为其中的一个模块,就需要用C#来驱动Android测试。交互方式上首先想到的是撸个代码放Android上,定时从服务端获取任务命令然后执行,嗯,OWIN实现个webapi进行数据交互分分钟的事情,貌似可行。 不过又想到,我们测试万一网络切换坏了,就不能联网了那就完了。这样的话,就不能进行任何手机天线端的网络操作了。接着就想到USB交互 然后找到了这个命令:adb forward tcp:PCPort tcp:Androidport 作用是将当前环境的某个端口与Android的某个端口绑定。这样Android 内部请求Androidport端口号就和请求PC上的PCPort端口一样,反之亦然,手机需要打开USB调试。准备写的时候我又想到,我们做的是无人值守的主动测试,Android一会儿跑过来问问有没有执行命令,一会儿跑过来问问 感觉有点不大好,麻烦别人还得别人惦记着不是我的性格。。。 balabala一番思想斗争后决定用socket交互,Android端做服务端,要做啥 过来说下~~

     

      Android的Server端通讯简要讯码:

      SCServer :接收连接过来的客户端,并且保存到ClientManager中

    public class SCServer implements Runnable {
    
        static Boolean Startd = false;
        static Integer Port;
        static ServerSocket serverSocket = null;
        ClientManager clientManager = new ClientManager();
    
        public SCServer(int port) {
            Port = port;
        }
    
        @Override
        public void run() {
            if (!Startd) {
                try {
                    serverSocket = new ServerSocket(Port);
                    Startd = true;
                    System.out.println("Startd :" + Port);
                } catch (IOException e) {
                    e.printStackTrace();
                }
    
                try {
                    while (Startd) {
                        Socket socket = serverSocket.accept();
                        clientManager.AddClient(socket);
                    }
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    
        public void RegistCallBack(String comm, CallBack callBack) {
            CommManager.Add(comm, callBack);
        }
    
        public void UnRegistCallBack(String comm) {
            CommManager.Remove(comm);
        }
    
        public void Send(Integer clientID, String comm, Map<String, String> msgDatas) {
            clientManager.SendMsg(clientID, comm, msgDatas);
        }
    
    }
    View Code 

      ClientManager:保存所有客户端,分配唯一编号,线程运行客户端监听消息,根据编号找到客户端Client 发送消息。

    public class ClientManager {
        static Integer ClientID=0;
        static Map<Integer, Client> Clients = new HashMap<>();
    
        public void AddClient(Socket socket) {
            Integer clientID= ClientID++;
            Client clinet = new Client(socket,clientID);
            new Thread(clinet).start();
             Clients.put(clientID, clinet);
        }
    
        public void SendMsg(Integer clientID, String comm,
                Map<String, String> msgDatas) {
            if (Clients.containsKey(clientID)) {
                Client client = Clients.get(clientID);
                client.SendMsg(comm, msgDatas);
            }
        }
    }
    View Code 

       Client:数据收发,命令解析。消息的载体是json格式FastJson处理。数据类容转换为Map<String,String>对应的为C#的Dictionary<string, string>

    public class Client implements Runnable {
        private Socket socket;
        private DataOutputStream dos = null;
        private BufferedReader brIs = null;
        private boolean bConnected = false;
        public Integer ClientID = -1;
    
        public Client(Socket socket, int id) {
            this.socket = socket;
            this.ClientID = id;
        }
    
        @Override
        public void run() {
            try {
                brIs = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
                dos = new DataOutputStream(socket.getOutputStream());
                System.out.println(this.ClientID + " Start");
                bConnected = true;
                while (bConnected) {
                    String str = brIs.readLine();
                    if(str!=null){
                    System.out.println("-------->" + str);
                    JSONObject jb = JSON.parseObject(str);
                    String msgComm = jb.getString("MsgComm");
                    CallBack cb = CommManager.Get(msgComm);
                    if (cb != null) {
                        String msgCBComm = jb.getString("MsgCBComm");
                        Map<String, String> msgDatas = (Map<String, String>) JSON.parse(jb.getString("MsgDatas"));
                        cb.execute(ClientID, msgCBComm, msgDatas);
                    } else {
                        System.out.println("--->MsgComm:[" + msgComm+ "] Can't Find!");
                    }}
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public void SendMsg(String comm, String callBackComm,
                Map<String, String> msgDatas) {
            Message msg = new Message();
            msg.MsgCBComm = callBackComm;
            msg.MsgComm = comm;
            msg.MsgDatas = msgDatas;
            String StrJson = JSON.toJSONString(msg);
            System.out.println("<--------"+StrJson);
            try {
                this.dos.writeUTF(StrJson);
                this.dos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        public void SendMsg(String comm, Map<String, String> msgDatas) {
            SendMsg(comm,"",msgDatas);
        }
    }
    View Code

      CommManager:消息命令管理,保存命令关键字与回调的处理方法。

    public class CommManager {
    
        static Map<String, CallBack> Comms = new HashMap<String, CallBack>();
    
        public static void Add(String comm, CallBack callBack) {
            Comms.put(comm, callBack);
        }
    
        public static CallBack Get(String comm) {
            if (Comms.containsKey(comm)) {
                CallBack callBack = Comms.get(comm);
                return callBack;
            } else {
                return null;
            }
        }
        
        public static void Remove(String comm) {
            Comms.remove(comm);
        }
    }
    View Code

      CallBack:回调接口,返回客户端ID,消息返回命令,接收的消息

    public interface CallBack {
         public void execute(Integer clientID, String callBackComm,
                    Map<String, String> msgDatas);  
    }
    View Code

      Message:交互的消息

    public class Message {
        public String MsgComm;  //传过来的命令
        public String MsgCBComm;//回应的命令
        public Map<String,String> MsgDatas=new HashMap<String, String>();//数据
    }
    View Code

    调用方式:

     1 final SCServer sc = new SCServer(57641);
     2 
     3         sc.RegistCallBack("DoSth", new CallBack() {
     4             @Override
     5             public void execute(Integer clientID, String callBackComm,Map<String, String> msgDatas) {
     6                     // 执行代码
     7                 msgDatas.clear();
     8                 msgDatas.put("Result", "OK");
     9                 sc.Send(clientID, callBackComm, msgDatas);
    10             }
    11         });

    C#的Client端通讯简要代码

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net;
    using System.Net.Sockets;
    using System.Text;
    using System.Threading;
    
    namespace LiteSocket
    {
    
    
        public class SocketClient
        {
            public bool IsConnected = false;
            private static byte[] result = new byte[2048];
            string IP;
            int Port;
            Thread t_Server;
            Socket clientSocket;
            
            Dictionary<string, Action<string, Dictionary<string, string>>> Comms = new Dictionary<string, Action<string, Dictionary<string, string>>>();
    
    
            public SocketClient(string ip, int port)
            {
                IP = ip;
                Port = port;
            }
            public void Close()
            {
                clientSocket.Close();
                t_Server.Abort();
            }
    
            public bool Connect()
            {
                try
                {
                    IPAddress ip = IPAddress.Parse(IP);
                    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    clientSocket.Connect(new IPEndPoint(ip, Port)); //配置服务器IP与端口  
                    t_Server = new Thread(() =>
                      {
                          while (clientSocket.Connected)
                          {
                              try
                              {
                                  int receiveLength = clientSocket.Receive(result);
                                  if (receiveLength > 0)
                                  {
                                      //接收数据处理
                                      string msgStr = Encoding.UTF8.GetString(result, 2, receiveLength - 2);
                                      Console.WriteLine(msgStr);
                                      Message msg = JsonConvert.DeserializeObject<Message>(msgStr);
                                      Action<string, Dictionary<string, string>> action = null;
                                      if (!Comms.TryGetValue(msg.MsgComm, out action))
                                      {
                                          Console.WriteLine("MsgComm :" + msg.MsgComm + " 不存在");
                                      }
                                      else
                                      {
                                          action(msg.MsgCBComm, msg.MsgDatas); //回调
                                      }
                                  }
                              }
                              catch (Exception ex)
                              {
                                  
                              }
                          }
                      });
                    t_Server.IsBackground = false;
                    t_Server.Start();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                IsConnected = clientSocket.Connected;
                return IsConnected;
            }
    
            /// <summary>
            /// 注册回调方法
            /// </summary>
            /// <param name="Comm">消息命令</param>
            /// <param name="CallBack">回调方法</param>
            public void RegistComm(string Comm, Action<string/*返回消息命令*/, Dictionary<string, string>> CallBack)
            {
                if (!Comms.ContainsKey(Comm))
                {
                    Comms.Add(Comm, CallBack);
                }
                else
                {
                    Comms[Comm] = CallBack;
                }
            }
    
            public void UnRegistComm(string Comm)
            {
                if (Comms.ContainsKey(Comm))
                {
                    Comms.Remove(Comm);
                }
            }
            /// <summary>
            /// 发送数据给服务端,需要返回,回调响应
            /// </summary>
            /// <param name="comm">命令消息</param>
            /// <param name="callBackComm">返回消息</param>
            /// <param name="msgDatas">消息内容</param>
            public void PostData(string comm, string callBackComm, Dictionary<string, string> msgDatas)
            {
                Message m = new Message();
                m.MsgComm = comm;
                m.MsgCBComm = callBackComm;
                m.MsgDatas = msgDatas;
                string json = JsonConvert.SerializeObject(m);
    
                Console.WriteLine(json);
                if (clientSocket.Connected)
                {
                    clientSocket.Send(Encoding.UTF8.GetBytes(json + "
    "));
                }
                else
                {
                    Console.WriteLine("Connected Is Broken");
                }
            }
            /// <summary>
            /// 发送命令给服务端,不需要返回数据
            /// </summary>
            /// <param name="comm"></param>
            /// <param name="msgDatas"></param>
            public void PostData(string comm, Dictionary<string, string> msgDatas)
            {
                PostData(comm, "", msgDatas);
            }
    
            /// <summary>
            /// 发送命令给服务端,并等待返回的消息。
            /// </summary>
            /// <param name="comm"></param>
            /// <param name="waitSeconds">命令执行超时时间 默认60s</param>
            /// <returns></returns>
            public Dictionary<string, string> SendData(string comm, int waitSeconds = 60)
            {
                return SendData(comm, new Dictionary<string, string>(), waitSeconds);
            }
            /// <summary>
            /// 发送命令和数据给服务端,并等待返回的消息。
            /// </summary>
            /// <param name="comm"></param>
            /// <param name="msgDatas"></param>
            /// <param name="waitSeconds">命令执行超时时间 默认60s</param>
            /// <returns></returns>
            public Dictionary<string, string> SendData(string comm, Dictionary<string, string> msgDatas, int waitSeconds = 60)
            {
                DateTime waitTime = DateTime.Now.AddSeconds(waitSeconds);
                Dictionary<string, string> returnMsgDatas = null;
                string RdComm = RandomStr(8); //随机生成返回消息命令
                RegistComm(RdComm, (cbkey, data) =>
                {
                    returnMsgDatas = data;
                });
                Message m = new Message();
                m.MsgComm = comm;
                m.MsgCBComm = RdComm;
                m.MsgDatas = msgDatas;
                string json = JsonConvert.SerializeObject(m);
                if (clientSocket.Connected)
                {
                    clientSocket.Send(Encoding.UTF8.GetBytes(json + "
    "));
                }
                else
                {
                    Console.WriteLine("Connect Is Broken");
    
                }
                //等待返回数据
                double wait = 0.00;
                while (returnMsgDatas == null && wait<=0)
                {
                    Thread.Sleep(500);
                    wait = (DateTime.Now - waitTime).TotalSeconds;
                }
                UnRegistComm(RdComm); //注销命令
                return returnMsgDatas;
            }
    
    
            public static string CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            /// <summary>
            /// 真·随机字符串
            /// </summary>
            /// <param name="lenght">长度</param>
            /// <returns></returns>
            public string RandomStr(int lenght)
            {
    
                StringBuilder sb = new StringBuilder();
                Random r = new Random(Guid.NewGuid().GetHashCode());
                for (int i = 0; i < lenght; i++)
                {
                    sb.Append(CHAR[r.Next(25)]);
                }
                return sb.ToString();
            }
        }
    }

    Message:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace LiteSocket
    {
       public class Message
        {
           public string MsgComm { set; get; }
           public string MsgCBComm { set; get; }
           private Dictionary<string, string> _MsgDatas = new Dictionary<string, string>();
           public Dictionary<string, string> MsgDatas
           {
               get { return _MsgDatas; }
               set { _MsgDatas = value; }
           }
    
        }
    }

    调用方法:

            SocketClient SC = new SocketClient(ip, port);
                Dictionary<string, string> Dic_doSth = new Dictionary<string, string>();
                Dic_doSth.Add("somethingKey", "somethingValue");
                var result = SC.SendData("DoSth", Dic_doSth);//发送并接收返回数据
                //OR
                SC.RegistComm("SthOver", (rekey, value) => { 
                    //处理返回数据
                });
                SC.PostData("DoSth", "SthOver", Dic_doSth); //发送 异步处理返回数据

    以上的交互完成了,后面就是业务代码了。APN添加切换 网络模式切换

    网上搜了下,得到一个例子:Android开发之APN网络切换 心中暗喜:有前辈给出了解决方案,还有代码实例,这实现起来还不简单么。照猫画虎。。。后发现出了个错:

     No permission to write APN settings:

    查询了一翻发现android 4.0以上对这一权限进行回收了。我们的测试机为小米4,按照网上说的方法进行了 重新系统签名,系统权限设置均无效,依然会有权限错误,中间为了得到android4.4.4的platform.pk8文件还下载了8G的android 4.4.4源码。可能是MIUI的与android原生的系统签名不一样 总是就是要不没权限 要不安装不上。 网上还有一种方法是 MM编译,得在Linux环境下;Eclipse+NDK配置又是很多的配置,看着教程实在感受不到爱了。。。 索性就放弃了这方案 曲线救国的方式来实现需求-----模拟用户屏幕操作。 adb有个Input命令,可以模拟键盘输入,屏幕点击,屏幕滑动。

    adb shell input keyevent “value”
    usage: input ...
           input text <string>
           input keyevent <key code number or name>
           input tap <x> <y>
           input swipe <x1> <y1> <x2> <y2>

    常用键:

    input keyevent 3    // Home
    input keyevent 4    // Back
    input keyevent 19  //Up
    input keyevent 20  //Down
    input keyevent 21  //Left
    input keyevent 22  //Right
    input keyevent 23  //Select/Ok
    input keyevent 24  //Volume+
    input keyevent 25  // Volume-
    input keyevent 82  // Menu 菜单

    抄个这段代码,Android上执行终端命令,Root权限?小米4:—_—

        public static void execShellCmd(String cmd) {
    
            try {
                // 申请获取root权限
                Process process = Runtime.getRuntime().exec("su");
                OutputStream outputStream = process.getOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(
                        outputStream);
                dataOutputStream.writeBytes(cmd);
                dataOutputStream.flush();
                dataOutputStream.close();
                outputStream.close();
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }

     那么,当我需要添加一个APN的时候:

    Android:

            final SCServer sc = new SCServer(57641);
            sc.RegistCallBack("AddApn", new CallBack() {
                @Override
                public void execute(Integer clientID, String callBackComm,
                        Map<String, String> msgDatas) {
                    Intent intent = new Intent(Settings.ACTION_APN_SETTINGS);
                    startActivity(intent);
             SystemClock.Sleep(1000);
    for (int i = 0; i < msgDatas.values().size(); i++) { String strDo = msgDatas.get(i + ""); FoolHand.execShellCmd(strDo); Log.d("strDo", strDo); SystemClock.sleep(1000); } msgDatas.clear(); msgDatas.put("Result", "OK"); sc.Send(clientID, callBackComm, msgDatas); } });

     C#:

      public bool AddApn(string Name, string APN)
            {
                Dictionary<string, string> doSth = new Dictionary<string, string>();
                int i = 0;
                doSth.Add((i++).ToString(), "input tap 463 1810");//点击新建
                doSth.Add((i++).ToString(), "input tap 650 290"); //点击名称
                doSth.Add((i++).ToString(), "input text " + Name); //输入名称
                doSth.Add((i++).ToString(), "input tap 846 1040");  //点击确定
                doSth.Add((i++).ToString(), "input tap 650 470");  //点击APN
                doSth.Add((i++).ToString(), "input text " + APN); //输入APN
                doSth.Add((i++).ToString(), "input tap 846 1040");  //点击确定
                doSth.Add((i++).ToString(), "input keyevent 4"); //退出 (弹出保存确认框)
                doSth.Add((i++).ToString(), "input tap 730 1780");  // 确认保存
                var result = SC.SendData("AddApn", doSth);
                if (result["Result"] == "OK")
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }

     效果:

    效果

    sc.RegistCallBack("SetNetMode", new CallBack() {
                @Override
                public void execute(Integer clientID, String callBackComm,
                        Map<String, String> msgDatas) {
                    Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
                    startActivity(intent);
    
                    for (int i = 0; i < msgDatas.values().size(); i++) {
                        String strDo = msgDatas.get(i + "");
                        FoolHand.execShellCmd(strDo);
                        Log.d("strDo", strDo);
                        SystemClock.sleep(1000);
                    }
                    msgDatas.clear();
                    msgDatas.put("Result", "OK");
                    sc.Send(clientID, callBackComm, msgDatas);
                }
            });
    切换网络模式Java
    public bool ChangeNetMode(string NetMode)
            {
                Dictionary<string, string> doSth = new Dictionary<string, string>();
                int i = 0;
                doSth.Add((i++).ToString(), "input swipe 640 550 640 1440");  //滑到最顶端
                doSth.Add((i++).ToString(), "input tap 640 430");
                doSth.Add((i++).ToString(), "input tap 640 1040");
                switch (NetMode)
                {
                    case "4G":
                        doSth.Add((i++).ToString(), "input tap 640 260");//选择4G
                        break;
                    case "3G":
                        doSth.Add((i++).ToString(), "input tap 640 430");//选择3G
                        break;
                    case "2G":
                        doSth.Add((i++).ToString(), "input tap 640 600");//点击2G
                        break;
                    default:
                        break;
                }
                doSth.Add((i++).ToString(), "input keyevent 4");
                doSth.Add((i++).ToString(), "input keyevent 4");
                // 640   260 430 
                var result = SC.SendData("SetNetMode", doSth);
                if (result["Result"] == "OK")
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
    切换网络模式C#

    这玩意模拟键盘输入,所以得记住屏幕位置。

    这玩意模拟键盘输入,所以不能录入中文。

    源码:  AndroidAPNSettings

  • 相关阅读:
    使用mybatis如果类属性名和数据库中的属性名不一样取值就会为null
    学习mybatis时出现了java.io.IOException: Could not find resource EmployeeMapper.xml
    配置mybatis-config.xml出现过很诡异的现象
    mybatis学习(一)
    报错cannot be cast to javassist.util.proxy.Proxy
    列车调度
    三角形
    土豪聪要请客(stol)
    Jams倒酒(pour)
    Data
  • 原文地址:https://www.cnblogs.com/yesicoo/p/4459828.html
Copyright © 2011-2022 走看看