def start(self):
        try:
            if options.list:
                for device in Adb.devices().out.split('\n')[1:]:
                    print(device)
                sys.exit(0)

            level = logging.DEBUG if options.verbose else logging.INFO
            coloredlogs.install(level=level)

            # set log
            os.makedirs(LOG_DIR, mode=0o700, exist_ok=True)
            log_file = open(os.path.join(LOG_DIR, LOG_FILENAME),
                            'a',
                            encoding='utf-8')
            coloredlogs.install(level=level, stream=log_file)

            # set handling interrupt exceptions
            signal.signal(signal.SIGTERM, self.shutdown)
            signal.signal(signal.SIGINT, self.shutdown)

            Adb.start_server()

            watch_thread = WatchThread()

            try:
                watch_thread.start()
                while True:
                    time.sleep(1)
            except MainExit:
                while True:
                    try:
                        self.log.info(
                            'shutdown command received, wait for clean up please...'
                        )
                        watch_thread.terminate()
                        while watch_thread.is_alive():
                            time.sleep(1)
                        break
                    except MainExit:
                        pass
        except (KeyboardInterrupt, InterruptedError):
            pass

        self.log.info('thank you for using, bye!')
    def run(self) -> None:
        self.log.debug('{} start'.format(self.__class__.__name__))

        while True:
            if self._terminate:
                break

            devices = frida.enumerate_devices()

            # usb devices from frida api
            usb_devices = [
                device for device in devices if device.type == 'usb'
            ]
            usb_devices_ids = [device.id for device in usb_devices]

            # devices strings from "adb devices"
            adb_devices_strings = Adb.devices().out.split('\n')[1:]
            adb_devices_strings = [
                _.split('\t')[0] for _ in adb_devices_strings
            ]

            # we need to access these devices remotely
            remote_devices_strings = set(adb_devices_strings) - set(
                usb_devices_ids)
            remote_devices = []

            for _ in remote_devices_strings:
                new_device = FakeDevice()
                new_device.id = _
                remote_devices.append(new_device)

            for device in usb_devices + remote_devices:
                # only hook specified devices
                if options.devices and device.id not in options.devices:
                    continue

                duplicated = False

                for t in self.frida_threads:
                    if t.device.id == device.id:
                        if not t.is_alive():
                            self.frida_threads.remove(t)
                            break

                        duplicated = True
                        break

                if duplicated:
                    continue

                try:
                    frida_thread = FridaThread(device)
                except RuntimeError as e:
                    self.log.error(
                        'error occurred when init frida thread: {}'.format(e))
                else:
                    frida_thread.start()
                    self.frida_threads.append(frida_thread)

            time.sleep(0.1)

        self.shutdown()
        self.log.debug('watch thread exit')