class Partition(object): __metaclass__ = Singleton def __init__(self): self.adb = Adb() self.fastboot = Fastboot() self.device = Device() #constant variables self.RECOVERY = "recovery" self.TWRP_SWITCH = "BDO" #BSDCO def restore(self, twrp_path, backup_src, backup_dest, device_serial): try: log.info("Restoring android device partitions") RECOVERY = self.RECOVERY ORIGINAL_RECOVERY_PATH = os.path.join(backup_src, "original_recovery") log.info("Waiting for device in adb mode") self.adb.wait_for_device(device_serial) self.adb.reboot_bootloader(device_serial) self.fastboot.flash(partition=RECOVERY, image_path=twrp_path, device_serial=device_serial) self.fastboot.reboot(device_serial) self.adb.wait_for_device(device_serial) self.adb.reboot_recovery(device_serial) #self.adb.shell("mkdir -p %s"%(backup_dest)) backup_file_paths = glob.glob(os.path.join(backup_src, "*.win*")) self.adb.reboot_recovery(device_serial) #needed, if not will have bug in adb push for backup_file_path in backup_file_paths: backup_file_split = backup_file_path.split("/") backup_file_name = backup_file_split[len(backup_file_split) - 1] #self.__copy_backup(source_filepath=backup_file_path, dest_filepath=os.path.join(backup_dest, backup_file_name), direction="push", twrp=True, device_serial=device_serial, check_hash=True) backup_dest_split = backup_dest.split("/") backup_folder_name = backup_dest_split[-2] self.adb.shell("twrp restore %s %s"%(backup_folder_name, self.TWRP_SWITCH),device_serial) self.adb.shell("rm -rf %s"%(backup_dest), device_serial) self.adb.reboot(device_serial) self.adb.wait_for_device(device_serial) self.adb.reboot_bootloader(device_serial) self.fastboot.flash(RECOVERY, ORIGINAL_RECOVERY_PATH, device_serial) self.fastboot.reboot(device_serial) self.adb.wait_for_device(device_serial) log.info("Restore device successfully") return True except Exception as e: log.error("unable to restore device partitions") self.restore(self, twrp_path, backup_src, backup_dest, device_serial) def backup(self, twrp_path, backup_dest, device_serial): try: log.info("Backing up android device partitions") RECOVERY = self.RECOVERY RECOVERY_PATH = self.device.backup_path(device_serial) RECOVERY_TEMP_DEV_LOC = os.path.join(RECOVERY_PATH, RECOVERY) ORIGINAL_RECOVERY_PATH = os.path.join(backup_dest, "original_recovery") mount_point = self.__get_mount_point(device_serial=device_serial) partitions = self.__get_partitions(mount_point, device_serial=device_serial) if partitions.has_key(RECOVERY): recovery_device = partitions[RECOVERY] else: raise BackupError log.info("Waiting for device in adb mode") self.adb.wait_for_device(device_serial) self.__dd_backup_partition(partition_name=RECOVERY, device=recovery_device, dest=RECOVERY_TEMP_DEV_LOC, device_serial=device_serial) self.__copy_backup(source_filepath=RECOVERY_TEMP_DEV_LOC, dest_filepath=ORIGINAL_RECOVERY_PATH, direction="pull", device_serial=device_serial, check_hash=True) self.adb.shell("rm -f %s"%(RECOVERY_TEMP_DEV_LOC), device_serial=device_serial) self.adb.reboot_bootloader(device_serial) self.fastboot.flash(partition=RECOVERY, image_path=twrp_path, device_serial=device_serial) self.fastboot.reboot(device_serial) self.adb.wait_for_device(device_serial) self.adb.reboot_recovery(device_serial) backup_path = self.__twrp_backup_partitions(switches=self.TWRP_SWITCH, device_serial=device_serial) backup_files = self.adb.shell("ls %s"%(backup_path),device_serial=device_serial) self.adb.reboot_recovery(device_serial) #needed, if not there's a bug in adb pull for file in backup_files.std_output: source_filepath = os.path.join(backup_path, file) dest_filepath = os.path.join(backup_dest, file) #self.__copy_backup(source_filepath, dest_filepath, direction="pull", twrp=True, check_hash=False, device_serial=device_serial) time.sleep(2) #self.adb.shell("rm -f %s"%(source_filepath)) self.adb.reboot_bootloader(device_serial) self.fastboot.flash(partition=RECOVERY, image_path=ORIGINAL_RECOVERY_PATH, device_serial=device_serial) self.fastboot.reboot(device_serial) self.adb.wait_for_device(device_serial) #self.adb.shell("rm -rf %s"%(backup_path)) log.info("Backup device successfully") return backup_path except Exception as e: log.error("Unable to perform partition backup") #self.backup(twrp_path,backup_dest, device_serial) def __copy_backup(self, source_filepath, dest_filepath, check_hash, direction,tries=30, twrp=False, device_serial=""): """ Recursive function to backup file from device to host machine with limited retries :param source_filepath: source file path on device :param dest_filepath: destination file path on host machine :param tries: number of retries (default:3) :return: bool """ try: if tries == 0: log.critical("Reach max retries") raise PartitionError if direction =="pull": self.adb.pull(source=source_filepath, dest=dest_filepath, device_serial=device_serial) time.sleep(2) if check_hash: has_file_integrity = self.__compare_md5(source_filepath, dest_filepath, twrp, device_serial) elif direction == "push": self.adb.push(source=source_filepath, dest=dest_filepath, device_serial=device_serial) time.sleep(2) if check_hash: has_file_integrity = self.__compare_md5(dest_filepath, source_filepath, twrp, device_serial) else: raise BackupError if check_hash: if not has_file_integrity : log.critical("Hash of %s of copied file is different from file on device."%(source_filepath)) log.critical("Retrying after 10 seconds...") time.sleep(10) return self.__copy_backup(source_filepath, dest_filepath,check_hash,direction, tries-1, twrp, device_serial) except Exception as e: log.error("Unable to copy backup files from device to host machine") self.__copy_backup(source_filepath, dest_filepath, check_hash, direction,tries-1, twrp, device_serial) def __compare_md5(self, device_path, host_path, twrp, device_serial): """ Compare the md5 of original file on device and copied file to host machine :param device_path: file path on device :param host_path: file path on host machine :return: bool """ try: if not twrp: result = self.adb.shell("md5 " + device_path, device_serial) else: result = self.adb.shell("md5sum " + device_path, device_serial) stdout = result.std_output try: device_md5 = stdout[0].split(" ")[0] except IndexError: device_md5 = "" host_md5 = hashlib.md5(open(host_path).read()).hexdigest() log.debug("device: %s host: %s"%(device_md5, host_md5)) if device_md5 == host_md5: return True else: return False except Exception as e: log.error("Unable to compare md5") raise PartitionError def __twrp_backup_partitions(self, switches, foldername="", device_serial=""): try: command = "twrp backup %s %s"%(switches, foldername) result = self.adb.shell(command, device_serial=device_serial) backup_path = "" log.debug(result.std_output) for line in result.std_output: match = re.search(r'Backup Folder: .*', line) if match: log.debug(match.group()) match_split = match.group().split(":") backup_path = match_split[1].replace(" ", "") return backup_path except Exception as e: log.error("unable to perform twrp partition backup") self.__twrp_backup_partitions(self, switches, foldername, device_serial) def __get_mount_point(self,device_serial): """ Get the mount point of device partition :return: """ try: self.adb.wait_for_device(device_serial) FIRST_LINE = 0 parameter = "mount | grep -E /dev/block/platform/.+/by-name/system" result = self.adb.shell(root=True, command=parameter, device_serial=device_serial) if result.std_error: raise Exception #remove /system from command outline mount_point = result.std_output[FIRST_LINE].split()[FIRST_LINE][:-6] return mount_point except Exception as e: log.error("cannot get device mount points") time.sleep(5) return self.__get_mount_point(device_serial) def __get_partitions(self, device, device_serial): """ Get android device partitions :param device: mount path :return: list of dict of partition name as key and partition location as value """ try: self.adb.wait_for_device(device_serial) parameter = "ls -al " + device result = self.adb.shell(root=True, command=parameter, device_serial=device_serial) if result.std_error: raise Exception partitions = {} for line in result.std_output: match = re.search(r'\w* -> .*', line) if match: match_split = match.group().split(" -> ") partitions[match_split[0]] = match_split[1] return partitions except Exception as e: log.error("Unable to get device partitions") time.sleep(5) return self.__get_partitions(device, device_serial) def __dd_backup_partition(self, partition_name, device, dest, device_serial): """ backup given partition to desired destination :param partition_name: name of partition :param device: dev path of partition :return: """ try: command = "dd if=%s of=%s"%(device, dest) result = self.adb.shell(root=True, command=command, device_serial=device_serial) if result.std_output: log.debug(result.std_output) return True else: log.debug(result.std_error) return False except Exception as e: log.error("Unable to backup partition to destination") raise PartitionError
class adb_testcase(unittest.TestCase): def setUp(self): self.adb_c = Adb() self.adb_c.start_server() def tearDown(self): #self.adb_c.kill_server() pass #@unittest.skip("skipping test device") def test_devices(self): devices = self.adb_c.devices() #print devices result = True if len(devices) == 0: result = False self.assertTrue(result, msg="No device detected") def test_push_file(self): path = os.path.dirname(os.path.realpath(__file__)) src = os.path.join(path, TEST_FILE) dest = os.path.join(SDCARD_PATH, TEST_FILE) isSuccessful = self.adb_c.push(source=src, dest=dest) self.assertTrue(isSuccessful, msg="adb push not successful") def test_pull_file(self): path = os.path.dirname(os.path.realpath(__file__)) src = os.path.join(SDCARD_PATH, TEST_FILE) dest = os.path.join(path, "extract.txt") isSuccessful = self.adb_c.pull(source=src, dest=dest) self.assertTrue(isSuccessful, msg="adb pull not successful") os.remove(dest) def test_shell_no_root(self): parameters = "ps" result = self.adb_c.shell(command=parameters) self.assertTrue(result.isSuccess, msg="adb shell ps not successful") print result.std_output def test_shell_with_root(self): parameters = "strace -p 1" result = self.adb_c.shell(needOutput=False, root=True, command=parameters) self.assertTrue(result.isSuccess, msg="adb shell with root not successful") self.adb_c.reboot() sleep(5) while len(self.adb_c.devices()) == 0: print("waiting for device to reboot...") sleep(10) @unittest.skip("skipping reboot test") def test_reboot_commands(self): """ reboot command testcase should be the last :return: """ self.adb_c.reboot() print("device rebooting... Please wait...") sleep(3) devices = self.adb_c.devices() foundDevice = False if devices: foundDevice = True self.assertFalse(foundDevice, msg="adb reboot not successful")