def _start_mnt(self): """ fork a process to start minicap on android """ # adb shell LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P 540x960@340x720/0 size = self._get_size().split('x') real_x = int(size[0]) real_y = int(size[1]) virtual_x = real_x virtual_y = real_y # if real_x >= 1080 and real_y >= 1920: # virtual_x = int(real_x * config.VIRTUAL_X) # virtual_y = int(real_y * config.VIRTUAL_Y) command_list = [ _ADB, "-s", self.device_id, "shell", "LD_LIBRARY_PATH=/data/local/tmp", "/data/local/tmp/minicap", "-P", "{}x{}@{}x{}/0".format(real_x, real_y, virtual_x, virtual_y), ] logger.info("start minicap: {}".format(" ".join(command_list))) self.mncap_process = subprocess.Popen(command_list, stdout=subprocess.DEVNULL) logger.info("start minicap: {}".format(self.mncap_process))
def __init__(self, device_id): self.device_id = device_id self.abi = self.get_abi() self.sdk = self.get_sdk() if self.is_mnc_existed(): logger.info("minicap already existed in {}".format(device_id)) else: self.download_target_mnc() if self.is_mnc_so_existed(): logger.info("minicap.so already existed in {}".format(device_id)) else: self.download_target_mnc_so()
def is_device_connected(device_id): """ return True if device connected, else return False """ _ADB = config.ADB_EXECUTOR try: device_name = subprocess.check_output( [_ADB, "-s", device_id, "shell", "getprop", "ro.product.model"] ) device_name = ( device_name.decode(config.DEFAULT_CHARSET) .replace("\n", "") .replace("\r", "") ) logger.info("device {} online".format(device_name)) except subprocess.CalledProcessError: return False return True
def download_target_mnc(self): # minicap文件 abi = self.get_abi() target_url = "{}/{}/bin/minicap".format(config.MNT_PREBUILT_URL, abi) logger.info("target minicap url: " + target_url) curPath = os.path.abspath(os.path.dirname(__file__)) mnc_path = curPath + os.sep + 'prebuilt' + os.sep + abi + os.sep + 'bin' + os.sep + 'minicap' # push and grant subprocess.check_call( [_ADB, "-s", self.device_id, "push", mnc_path, config.MNC_HOME]) subprocess.check_call([ _ADB, "-s", self.device_id, "shell", "chmod", "777", config.MNC_HOME ]) logger.info("minicap already installed in {}".format(config.MNC_HOME))
def download_target_mnc_so(self): # minicap.so文件 abi = self.get_abi() sdk = self.get_sdk() so_target_url = "{}/{}/lib/android-{}/minicap.so".format( config.MNT_PREBUILT_URL, abi, sdk) logger.info("target minicap.so url: " + so_target_url) curPath = os.path.abspath(os.path.dirname(__file__)) so_path = curPath + os.sep + 'prebuilt' + os.sep + abi + os.sep + 'lib' + os.sep + 'android-{}'.format( sdk) + os.sep + 'minicap.so' # push and grant subprocess.check_call( [_ADB, "-s", self.device_id, "push", so_path, config.SO_HOME]) subprocess.check_call([ _ADB, "-s", self.device_id, "shell", "chmod", "777", config.SO_HOME ]) logger.info("minicap.so already installed in {}".format( config.MNC_HOME))
def __init__(self, device_id): assert is_device_connected(device_id) self.device_id = device_id logger.info("searching a usable port ...") self.port = self._get_port() logger.info("device {} bind to port {}".format(device_id, self.port)) # check minicap self.installer = MNTInstaller(device_id) # keep minicap alive self._forward_port() self.mncap_process = None self._start_mnt() # make sure it's up time.sleep(1) assert ( self.heartbeat() ), "minicap did not work. see https://github.com/williamfzc/pyminicap/issues/11"
def get_sdk(self): sdk = subprocess.getoutput( "{} -s {} shell getprop ro.build.version.sdk".format( _ADB, self.device_id)) logger.info("device_sdk {} is {}".format(self.device_id, sdk)) return sdk.strip()
def get_abi(self): abi = subprocess.getoutput( "{} -s {} shell getprop ro.product.cpu.abi".format( _ADB, self.device_id)) logger.info("device_abi {} is {}".format(self.device_id, abi)) return abi.strip()
def send(self, content): """ send message and get its response """ byte_content = str2byte(content) self.client.sendall(byte_content) logger.info("touch: {}".format(byte_content)) return self.client.recv(self._DEFAULT_BUFFER_SIZE)
def disconnect(self): self.client and self.client.close() self.client = None logger.info("minicap disconnected")
def ReadImageStream(self): # get minicap server info readBannerBytes = 0 bannerLength = 2 readFrameBytes = 0 frameBodylength = 0 dataBody = b"" while self.client: try: reallen = self.client.recv(4096) length = len(reallen) if not length: continue cursor = 0 while cursor < length: # 获取图片头部信息 if readBannerBytes < bannerLength: if readBannerBytes == 0: self.banner.Version = reallen[cursor] elif readBannerBytes == 1: bannerLength = reallen[cursor] self.banner.Length = bannerLength elif readBannerBytes in [2, 3, 4, 5]: self.banner.Pid += (reallen[cursor] << ( (readBannerBytes - 2) * 8)) >> 0 elif readBannerBytes in [6, 7, 8, 9]: self.banner.RealWidth += (reallen[cursor] << ( (readBannerBytes - 6) * 8)) >> 0 elif readBannerBytes in [10, 11, 12, 13]: self.banner.RealHeight += (reallen[cursor] << ( (readBannerBytes - 10) * 8)) >> 0 elif readBannerBytes in [14, 15, 16, 17]: self.banner.VirtualWidth += (reallen[cursor] << ( (readBannerBytes - 14) * 8)) >> 0 elif readBannerBytes in [18, 19, 20, 21]: self.banner.VirtualHeight += (reallen[cursor] << ( (readBannerBytes - 18) * 8)) >> 0 elif readBannerBytes == 22: self.banner.Orientation = reallen[cursor] * 90 elif readBannerBytes == 23: self.banner.Quirks = reallen[cursor] cursor += 1 readBannerBytes += 1 if readBannerBytes == bannerLength: logger.info(self.banner.toString()) elif readFrameBytes < 4: # 第一个过来的图片信息的前4个字符不是图片的二进制信息,而是携带着图片大小的信息 frameBodylength = frameBodylength + ( (reallen[cursor] << (readFrameBytes * 8)) >> 0) cursor += 1 readFrameBytes += 1 # print('{} - {} '.format(cursor,frameBodylength)) else: # 真正获取图片信息,比如我们接受到的信息长度为n,4~n部分是图片的信息,需要保存下来。 # print('{} - {} - {} '.format(length,cursor, frameBodylength)) if length - cursor >= frameBodylength: dataBody = dataBody + (reallen[cursor:( cursor + frameBodylength)]) if dataBody[0] != 0xFF or dataBody[1] != 0xD8: return self.picture.put(dataBody) # self.save_file('d:/pic.png', dataBody) cursor += frameBodylength frameBodylength = 0 readFrameBytes = 0 dataBody = b"" else: dataBody = dataBody + reallen[cursor:length] frameBodylength -= length - cursor readFrameBytes += length - cursor cursor = length except: logger.info('退出线程') break
def _get_size(self): out_put = subprocess.check_output( [_ADB, "-s", self.device_id, "shell", "wm", "size"]) logger.info("当前设备分辨率为: {}".format(out_put.decode('utf-8'))) size = out_put.decode('utf-8').split(': ')[1].strip() return size
def stop(self): self.mncap_process and self.mncap_process.kill() self._PORT_SET.add(self.port) logger.info("device {} unbind to {}".format(self.device_id, self.port))