/// <summary> /// APP服务启动 /// 目前APP支持两种启动方式,一种为服务命令,一种为直接启动组件 /// 服务启动方式:优点是用户不需要做任何操作,缺点是启动服务不稳定,针对部分手机启动受权限限制</summary> /// 组件启动方式:优点是启动服务稳定,缺点是启动组件时屏幕会闪动一下,用户体验比较差<param name="device">设备对象</param> /// 综合上述两种启动方式,在APP中提供两种启动方式,有限已服务启动,启动失败后再通过组件方式进行启动<param name="port">映射端口(第一个:“12580”是由你们自己定义的pc端口,第二个你们不用管,它是手机端的服务端口)</param> /// <param name="tcp">默认密码</param> /// <returns>是否成功,true成功,反之亦然</returns> private bool AppServiceStart(Device device, int port, int tcp = 12345) { try { DefaultReceiver dreceiver = new DefaultReceiver(); string sc1 = "am startservice -n mwh.com.spfsocket/mwh.com.spfsocket.MainService"; this.ExecuteRemoteCommand(sc1, device, dreceiver); if (device.IsRoot) { sc1 = "su -c \"am startservice -n mwh.com.spfsocket/mwh.com.spfsocket.MainService\""; this.ExecuteRemoteCommand(sc1, device, dreceiver); } string sc2 = String.Format("host-serial:{0}:forward:tcp:{1};tcp:{2}", device.SerialNumber, port, tcp); this.ExecuteAdbSocketCommand(null, sc2); // 服务启动失败则使用组件启动 if (dreceiver.Lines.Contains("Error: Not found; no service started.")) { return(false); } return(true); } catch (Exception ex) { // 组件启动 if (AppAmStart(device, port, 12345)) { return(true); } return(false); } }
/// <summary> /// 拷贝本地文件到设备临时目录(/data/local/tmp/) /// </summary> public string PushSingleFile(Device device, string localFile, string remotePath, int bufferSize = 64 * 1024, int maxtimeout = 9000) { if (!System.IO.File.Exists(localFile)) { return(string.Empty); } //验证目标目录 if (!this.RemoutFileIsExist(device, remotePath)) { DefaultReceiver reciver = new DefaultReceiver(); this.ExecuteRemoteAutoCommand(string.Format("mkdir -p {0}", remotePath), device, reciver); } this.UpgradeFilePermission(device, remotePath); var remoutFile = ConnectLinuxPath(remotePath, Path.GetFileName(localFile)); try { if (this.DoPushFile(device, localFile, remoutFile, bufferSize, maxtimeout)) { return(remoutFile); } } catch (Exception ex) { } return(string.Empty); }
/// <summary> /// 卸载指定包名的App,如com.mwh.spfsocket /// </summary> /// <param name="packageName"></param> public void UninstallPackage(string packageName, Device device) { try { var cmd = string.Format("pm uninstall {0}", packageName); DefaultReceiver receiver = new DefaultReceiver(); this.ExecuteRemoteCommand(cmd, device, receiver); } catch (Exception ex) { } }
/// <summary> /// 验证一个目录或文件是已存在 /// </summary> /// <param name="device"></param> /// <param name="remouteFile"></param> internal bool RemoutFileIsExist(Device device, string remouteFile) { try { DefaultReceiver receiver = new DefaultReceiver(); //若文件名中包含空格 var path = remouteFile.Replace(" ", "\\ "); this.ExecuteRemoteAutoCommand(string.Format("ls -l {0}", path), device, receiver); return(true); } catch (FileNotFoundException ex) { return(false); } }
/// <summary> /// 安装本地制定apk文件到手机,强制安装。 /// 默认安装到data/local/tmp/ /// </summary> public bool InstallPackage(string localFile, Device device, string remoutePath = "/data/local/tmp/") { string remouteFile = string.Empty; var flag = false; try { remouteFile = this.PushSingleFile(device, localFile, remoutePath); if (String.IsNullOrWhiteSpace(remouteFile)) { return(false); } //1.1 要提升文件权限,这要命,找了半天才知道是这个问题 this.UpgradeFilePermission(device, remouteFile); //2.install var cmd = string.Format("pm install -r {0}", remouteFile); DefaultReceiver receiver = new DefaultReceiver(); this.ExecuteRemoteCommand("pm setInstallLocation 0", device, receiver); this.ExecuteRemoteCommand(cmd, device, receiver, 16 * 1024, 20000); if (Contains(receiver.Data, "success", StringComparison.OrdinalIgnoreCase)) { flag = true; } else { throw new ApplicationException(receiver.Data); } } catch (Exception ex) { flag = false; //如果默认路径安装失败,则尝试拷贝到SDCard安装 if (remoutePath.Equals("/data/local/tmp/")) { flag = this.InstallPackage(localFile, device, remoutePath = this.TryParseSDCardByDict(device)); } } finally { try { var receiver = new DefaultReceiver(); this.ExecuteRemoteCommand(string.Format("rm {0}", remouteFile), device, receiver); } catch { } } return(flag); }
/// <summary> /// 通过路径字典尝试获取SDCard路径 /// </summary> private string TryParseSDCardByDict(Device device) { foreach (var p in SDCardPathDictionary) { DefaultReceiver receiver = new DefaultReceiver(); try { this.ExecuteRemoteCommand(string.Format("ls -l {0}", p), device, receiver); } catch { continue; } if (receiver.Lines != null) { return(p); } } return(string.Empty); }
/// <summary> /// APP组件启动 /// </summary> /// <param name="device">设备对象</param> /// <param name="port">映射端口(第一个:“12580”是由你们自己定义的pc端口,第二个你们不用管,它是手机端的服务端口)</param> /// <param name="tcp">默认密码</param> /// <returns>是否成功,true成功,反之亦然</returns> private bool AppAmStart(Device device, int port, int tcp = 12345) { try { DefaultReceiver dreceiver = new DefaultReceiver(); string sc1 = "am start mwh.com.spfsocket/.MainActivity"; string sc2 = "am broadcast -a NotifyServiceStop"; var sc3 = String.Format("host-serial:{0}:forward:tcp:{1};tcp:{2}", device.SerialNumber, port, tcp); var sc4 = "am broadcast -a NotifyServiceStart"; this.ExecuteRemoteCommand(sc1, device, dreceiver); this.ExecuteRemoteCommand(sc2, device, dreceiver); this.ExecuteAdbSocketCommand(null, sc3); this.ExecuteRemoteCommand(sc4, device, dreceiver); return(true); } catch (Exception ex) { return(false); } }
/// <summary> /// 提升文件操作权限 /// recursion=true 支持递归所有子文件、文件夹,但有些手机(三星)不支持该参数 /// recursion=false 只操作当前指定的文件/文件夹路径 /// </summary> /// <param name="path">文件路径</param> public bool UpgradeFilePermission(Device device, string path, bool recursion = false) { try { //若文件名中包含空格 path = path.Replace(" ", "\\ "); DefaultReceiver receiver = new DefaultReceiver(); string cmd = recursion ? "chmod -R 777 {0}" : "chmod 777 {0}"; var result = this.ExecuteRemoteAutoCommandNoException(string.Format(cmd, path), device, receiver); if (!result.Success && null != result.Ex) { return(false); } return(true); } catch (Exception ex) { return(false); } }
/// <summary> /// 判断文件或者路径是否存在 /// </summary> /// <param name="device"></param> /// <param name="path"></param> /// <returns></returns> private bool IsExistsFileOrPath(Device device, string path) { int buffersize = 16 * 1024; int maxtimeout = 9000; DefaultReceiver receiver = new DefaultReceiver(); string command = string.Format("ls -l {0}", path.TrimEnd(new char[] { '#', 'F' })); if (device.IsRoot) { command = string.Format("su -c \"{0}\" ", command); } Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { socket.Connect(this.SocketAddress); socket.ReceiveTimeout = maxtimeout; socket.SendTimeout = maxtimeout; socket.ReceiveBufferSize = buffersize; //设置当前使用设备 String msg = "host:transport:" + device.SerialNumber; byte[] device_query = AdbSocketHelper.FormAdbRequest(msg); AdbSocketHelper.Write(socket, device_query); var res = AdbSocketHelper.ReadResponse(socket); if (!res.IsOkay) { return(false); } //reqest var request = AdbSocketHelper.FormAdbRequest("shell:" + command); AdbSocketHelper.Write(socket, request); //response res = AdbSocketHelper.ReadResponse(socket); if (!res.IsOkay) { return(false); } //read AdbSocketHelper.Read(socket, receiver, buffersize); //验证输出是否合法 //determines weahter the output is valid string[] cmd = command.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var tdata = receiver.Data.ToString().Trim(); if (tdata.Contains(String.Format("{0}: not found", cmd[0]))) { return(false); } if (tdata.Contains("No such file or directory")) { return(false); } if (tdata.Contains("no closing quote")) { return(false); } } finally { this.DisposeSocket(socket); } return(true); }
/// <summary> /// 执行文件拷贝到本地操作 /// </summary> private void DoPullFile(Device device, string sourceFile, string local, PullFileReceiver receiver = null, int bufferSize = 64 * 1024, int maxtimeout = 9000, bool trycat = true) { if (device == null || String.IsNullOrWhiteSpace(sourceFile) || String.IsNullOrWhiteSpace(local)) { return; } #region /data/data/文件处理 //只处理/data/data/文件夹下文件 bool IsNeedMoveFile = sourceFile.Replace('\\', '/').StartsWith("/data/data/") && sourceFile.EndsWith(".db"); var dr = new DefaultReceiver(); string rmCmd = ""; if (IsNeedMoveFile) { string tempSrc = string.Format("/data/local/tmp/{0}", FileHelper.GetFileName(local)); string cpCmd = string.Format("cp {0} /data/local/tmp", sourceFile); this.ExecuteRemoteAutoCommandNoException(cpCmd, device, dr); //拷贝/data/data/ 到/data/local/tmp if (this.IsExistsFileOrPath(device, tempSrc)) //判断是否拷贝成功 { this.UpgradeFilePermission(device, tempSrc); //提权 sourceFile = tempSrc; rmCmd = string.Format("rm {0}", tempSrc); } } #endregion receiver = receiver ?? new PullFileReceiver(local); Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { socket.Connect(this.SocketAddress); socket.ReceiveTimeout = maxtimeout; socket.SendTimeout = maxtimeout; socket.Blocking = true; socket.ReceiveBufferSize = bufferSize; this.SetDevice(socket, device); this.SendAsyncReqest(socket); var request = AdbSocketHelper.FormPullFileRequest(sourceFile); AdbSocketHelper.Write(socket, request); receiver.DoReceive(socket); } catch (ADBConnectionException cex) { var mes = string.Format("pull file[{0}] failed:{1}", sourceFile, cex.Message); if (trycat) { var rec = new CatToFileReceiver(local); rec.OnReceiveData = receiver.OnReceiveData; var rt = this.CatToFile(device, sourceFile, local, rec); if (rt != string.Empty) { throw new Exception(rt); } } else { throw new ApplicationException(mes, cex); } } catch (Exception ex) { throw new ApplicationException(string.Format("pull file[{0}] failed", sourceFile), ex); } finally { if (IsNeedMoveFile && !String.IsNullOrWhiteSpace(rmCmd)) {//删除temp文件 try { this.ExecuteRemoteAutoCommandNoException(rmCmd, device, dr); } catch { } } this.DisposeSocket(socket); } }