def generate_event_based_on_utg(self): """ generate an event based on current device state note: ensure these fields are properly maintained in each transaction: last_event_flag, last_touched_view, last_state, exploited_views, state_transitions @return: InputEvent """ self.save_state_transition(self.last_event_str, self.last_state, self.current_state) if self.device.is_foreground(self.app): # the app is in foreground, clear last_event_flag self.last_event_flag = EVENT_FLAG_STARTED else: number_of_starts = self.last_event_flag.count(EVENT_FLAG_START_APP) # If we have tried too many times but the app is still not started, stop DroidBot if number_of_starts > MAX_NUM_RESTARTS: raise InputInterruptedException("The app cannot be started.") # if app is not started, try start it if self.last_event_flag.endswith(EVENT_FLAG_START_APP): # It seems the app stuck at some state, and cannot be started # just pass to let viewclient deal with this case self.logger.info("The app had been restarted %d times.", number_of_starts) self.logger.info("Trying to restart app...") pass else: start_app_intent = self.app.get_start_intent() self.last_event_flag += EVENT_FLAG_START_APP self.last_event_str = EVENT_FLAG_START_APP return IntentEvent(start_app_intent) # select a view to click view_to_touch = self.select_a_view(self.current_state) # if no view can be selected, restart the app if view_to_touch is None: stop_app_intent = self.app.get_stop_intent() self.last_event_flag += EVENT_FLAG_STOP_APP self.last_event_str = EVENT_FLAG_STOP_APP return IntentEvent(stop_app_intent) view_to_touch_str = view_to_touch['view_str'] if view_to_touch_str.startswith('BACK'): result = KeyEvent('BACK') else: result = TouchEvent(view=view_to_touch) self.last_event_flag += EVENT_FLAG_TOUCH self.last_event_str = view_to_touch_str self.save_explored_view(self.current_state, self.last_event_str) return result
def start(self, input_manager): """ start producing events :param input_manager: instance of InputManager """ count = 0 while input_manager.enabled and count < input_manager.event_count: try: # make sure the first event is go to HOME screen # the second event is to start the app if count == 0: event = KeyEvent(name="HOME") elif count == 1: event = IntentEvent(self.app.get_start_intent()) else: event = self.generate_event() input_manager.add_event(event) except KeyboardInterrupt: break except InputInterruptedException as e: self.logger.warning("stop sending events: %s" % e) break # except RuntimeError as e: # self.logger.warning(e.message) # break except Exception as e: self.logger.warning("exception during sending events: %s" % e) import traceback traceback.print_exc() continue count += 1
def generate_event_based_on_utg(self): """ generate an event based on current UTG @return: InputEvent """ if self.__first_event: self.__first_event = False self.logger.info("Trying to start the app...") start_app_intent = self.app.get_start_intent() return IntentEvent(intent=start_app_intent) else: return ManualEvent()
def generate_event_based_on_utg(self): """ generate an event based on current UTG @return: InputEvent """ current_state = self.current_state if current_state.state_str in self.__missed_states: self.__missed_states.remove(current_state.state_str) if current_state.get_app_activity_depth(self.app) < 0: # If the app is not in the activity stack start_app_intent = self.app.get_start_intent() if self.__event_trace.endswith(EVENT_FLAG_START_APP + EVENT_FLAG_STOP_APP) \ or self.__event_trace.endswith(EVENT_FLAG_START_APP): self.__num_restarts += 1 else: self.__num_restarts = 0 if self.__num_restarts > MAX_NUM_RESTARTS: # If the app had been restarted too many times, abort msg = "The app had been restarted too many times." self.logger.info(msg) raise InputInterruptedException(msg) else: # Start the app self.__event_trace += EVENT_FLAG_START_APP self.logger.info("Trying to start the app...") return IntentEvent(intent=start_app_intent) elif current_state.get_app_activity_depth(self.app) > 0: # If the app is in activity stack but is not in foreground self.__num_steps_outside += 1 if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE: # If the app has not been in foreground for too long, try to go back go_back_event = KeyEvent(name="BACK") self.__event_trace += EVENT_FLAG_NAVIGATE self.logger.info("Going back to the app...") return go_back_event else: # If the app is in foreground self.__num_steps_outside = 0 # Get all possible input events possible_events = current_state.get_possible_input() if self.random_input: random.shuffle(possible_events) # If there is an unexplored event, try the event first for input_event in possible_events: if not self.utg.is_event_explored(event=input_event, state=current_state): self.logger.info("Trying a unexplored event.") self.__event_trace += EVENT_FLAG_EXPLORE return input_event target_state = self.__get_nav_target(current_state) if target_state: event_path = self.utg.get_event_path(current_state=current_state, target_state=target_state) if event_path and len(event_path) > 0: self.logger.info("Navigating to %s, %d steps left." % (target_state.state_str, len(event_path))) self.__event_trace += EVENT_FLAG_NAVIGATE return event_path[0] # If couldn't find a exploration target, stop the app stop_app_intent = self.app.get_stop_intent() self.logger.info( "Cannot find an exploration target. Trying to restart app...") self.__event_trace += EVENT_FLAG_STOP_APP return IntentEvent(intent=stop_app_intent)
def generate_event_based_on_utg(self): """ generate an event based on current UTG @return: InputEvent """ #self.logger.info("Generating events in gym state: ") current_state = self.device.get_current_state() #self.current_state self.current_state = current_state self.logger.info("Current state: %s" % current_state.state_str) if current_state.state_str in self.__missed_states: self.__missed_states.remove(current_state.state_str) if current_state.get_app_activity_depth(self.app) < 0: # If the app is not in the activity stack start_app_intent = self.app.get_start_intent() # It seems the app stucks at some state, has been # 1) force stopped (START, STOP) # just start the app again by increasing self.__num_restarts # 2) started at least once and cannot be started (START) # pass to let viewclient deal with this case # 3) nothing # a normal start. clear self.__num_restarts. if self.__event_trace.endswith(EVENT_FLAG_START_APP + EVENT_FLAG_STOP_APP) \ or self.__event_trace.endswith(EVENT_FLAG_START_APP): self.__num_restarts += 1 self.logger.info("The app had been restarted %d times.", self.__num_restarts) else: self.__num_restarts = 0 # pass (START) through if not self.__event_trace.endswith(EVENT_FLAG_START_APP): if self.__num_restarts > MAX_NUM_RESTARTS: # If the app had been restarted too many times, enter random mode msg = "The app had been restarted too many times. Entering random mode." self.logger.info(msg) self.__random_explore = True else: # Start the app self.__event_trace += EVENT_FLAG_START_APP self.logger.info("Trying to start the app...") return IntentEvent(intent=start_app_intent) elif current_state.get_app_activity_depth(self.app) > 0: # If the app is in activity stack but is not in foreground self.__num_steps_outside += 1 if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE: # If the app has not been in foreground for too long, try to go back if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE_KILL: stop_app_intent = self.app.get_stop_intent() go_back_event = IntentEvent(stop_app_intent) else: go_back_event = KeyEvent(name="BACK") self.__event_trace += EVENT_FLAG_NAVIGATE self.logger.info("Going back to the app...") return go_back_event else: # If the app is in foreground self.__num_steps_outside = 0 # Get all possible input events possible_events = current_state.get_possible_input() if self.random_input: random.shuffle(possible_events) if self.search_method == POLICY_GREEDY_DFS: possible_events.append(KeyEvent(name="BACK")) elif self.search_method == POLICY_GREEDY_BFS: possible_events.insert(0, KeyEvent(name="BACK")) # get humanoid result, use the result to sort possible events # including back events if self.device.humanoid is not None: possible_events = self.__sort_inputs_by_humanoid(possible_events) # If there is an unexplored event, try the event first for input_event in possible_events: if not self.utg.is_event_explored(event=input_event, state=current_state): self.logger.info("Trying an unexplored event.") self.__event_trace += EVENT_FLAG_EXPLORE return input_event target_state = self.__get_nav_target(current_state) if target_state: event_path = self.utg.get_event_path(current_state=current_state, target_state=target_state) if event_path and len(event_path) > 0: self.logger.info("Navigating to %s, %d steps left." % (target_state.state_str, len(event_path))) self.__event_trace += EVENT_FLAG_NAVIGATE return event_path[0] if self.__random_explore: self.logger.info("Trying random event.") random.shuffle(possible_events) return possible_events[0] # If couldn't find a exploration target, stop the app stop_app_intent = self.app.get_stop_intent() self.logger.info("Cannot find an exploration target. Trying to restart app...") self.__event_trace += EVENT_FLAG_STOP_APP return IntentEvent(intent=stop_app_intent)
def reset_start(self): start_app_intent = self.app.get_start_intent() #self.logger.info("Entering reset_start. Trying to restart app...") self.__event_trace += EVENT_FLAG_START_APP #self.logger.info(start_app_intent) return IntentEvent(intent=start_app_intent)
def reset_stop(self): stop_app_intent = self.app.get_stop_intent() #self.logger.info("Entering reset_stop. Trying to restart app...") self.__event_trace += EVENT_FLAG_STOP_APP return IntentEvent(intent=stop_app_intent)
def check_gym_event(self, event, event_trace_str = "+NA"): #self.logger.info("checking gym event from env.step") current_state = self.device.get_current_state() self.current_state = current_state #self.logger.info("Current state: %s" % current_state.state_str) if current_state.state_str in self.__missed_states: self.__missed_states.remove(current_state.state_str) if current_state.get_app_activity_depth(self.app) < 0: # If the app is not in the activity stack start_app_intent = self.app.get_start_intent() # It seems the app stucks at some state, has been # 1) force stopped (START, STOP) # just start the app again by increasing self.__num_restarts # 2) started at least once and cannot be started (START) # pass to let viewclient deal with this case # 3) nothing # a normal start. clear self.__num_restarts. if self.__event_trace.endswith(EVENT_FLAG_START_APP + EVENT_FLAG_STOP_APP) \ or self.__event_trace.endswith(EVENT_FLAG_START_APP): self.__num_restarts += 1 self.logger.info("The app had been restarted %d times.", self.__num_restarts) else: self.__num_restarts = 0 # pass (START) through if not self.__event_trace.endswith(EVENT_FLAG_START_APP): if self.__num_restarts > MAX_NUM_RESTARTS: # If the app had been restarted too many times, enter random mode msg = "The app had been restarted too many times. Enabling random mode." self.logger.info(msg) self.__random_explore = True else: # Start the app self.__event_trace += EVENT_FLAG_START_APP self.logger.info("Trying to start the app...") return IntentEvent(intent=start_app_intent) elif current_state.get_app_activity_depth(self.app) > 0: # If the app is in activity stack but is not in foreground self.__num_steps_outside += 1 if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE: # If the app has not been in foreground for too long, try to go back if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE_KILL: stop_app_intent = self.app.get_stop_intent() go_back_event = IntentEvent(stop_app_intent) else: go_back_event = KeyEvent(name="BACK") self.__event_trace += EVENT_FLAG_NAVIGATE self.logger.info("Going back to the app...") return go_back_event else: # If the app is in foreground self.__num_steps_outside = 0 #self.logger.info("Passed initial steps, returning the event for input manager") self.__event_trace += event_trace_str #update the utg #self.current_state = self.device.get_current_state() #updated up above self.__update_utg() self.last_state = self.current_state self.last_event = event return event
def generate_event_based_on_utg(self): """ generate an event based on current UTG @return: InputEvent """ current_state = self.current_state if current_state.state_str in self.__missed_states: self.__missed_states.remove(current_state.state_str) if current_state.get_app_activity_depth(self.app) < 0: # If the app is not in the activity stack start_app_intent = self.app.get_start_intent() if self.__event_trace.endswith(EVENT_FLAG_START_APP + EVENT_FLAG_STOP_APP) \ or self.__event_trace.endswith(EVENT_FLAG_START_APP): self.__num_restarts += 1 else: self.__num_restarts = 0 if self.__num_restarts > MAX_NUM_RESTARTS: # If the app had been restarted too many times, abort msg = "The app had been restarted too many times." self.logger.info(msg) raise InputInterruptedException(msg) else: # Start the app self.__event_trace += EVENT_FLAG_START_APP self.logger.info("Trying to start the app...") return IntentEvent(intent=start_app_intent) elif current_state.get_app_activity_depth(self.app) > 0: # If the app is in activity stack but is not in foreground self.__num_steps_outside += 1 if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE: # If the app has not been in foreground for too long, try to go back go_back_event = KeyEvent(name="BACK") self.__event_trace += EVENT_FLAG_NAVIGATE self.logger.info("Going back to the app...") return go_back_event else: # If the app is in foreground self.__num_steps_outside = 0 curr_screenshot = current_state.device.take_screenshot() new_screenshot = str(curr_screenshot).replace("/temp/", "/all_screenshots/") directory = os.path.dirname(new_screenshot) if not os.path.exists(directory): os.mkdir(directory) move(curr_screenshot, new_screenshot) # Get all possible input events all_views = current_state.views actionable_views = filter(lambda x : x["enabled"] and x["visible"] and (x["clickable"] or x["scrollable"] or x["checkable"] or x["long_clickable"]), all_views) items = [] for view in actionable_views: data = {"class": view["class"]} if view["parent"] >= 0: data["parent"] = all_views[view["parent"]]["class"] else: data["parent"] = "none" if len(view["children"]) > 1: data["children2"] = all_views[view["children"][1]]["class"] else: data["children2"] = "none" if len(view["children"]) > 0: data["children1"] = all_views[view["children"][0]]["class"] else: data["children1"] = "none" items.append(data) predictions = ModelBased.classify(items) selected_idx = ModelBased.select(predictions) possible_events = current_state.get_possible_input() if len(possible_events) > 0: # If it was not possible to classify fall back to purely random strategy if selected_idx >= 0: selected_widget = actionable_views[selected_idx] chance = random.random() tmp_events = [x for x in possible_events if ((not isinstance(x, UIEvent)) and (chance < 0.2)) or (isinstance(x, UIEvent) and UtgRandomWidgetPolicy.are_equal(x.view, selected_widget))] if len(tmp_events) > 0: possible_events = tmp_events random.shuffle(possible_events) self.logger.info("Trying a random event.") self.__event_trace += EVENT_FLAG_EXPLORE input_event = possible_events[0] return input_event # If couldn't find a exploration target, stop the app stop_app_intent = self.app.get_stop_intent() self.logger.info("Cannot find an exploration target. Trying to restart app...") self.__event_trace += EVENT_FLAG_STOP_APP return IntentEvent(intent=stop_app_intent)
def start(self, input_manager): """ start producing events :param input_manager: instance of InputManager """ count = 0 network_monitoring = False while input_manager.enabled and count < input_manager.event_count: try: # make sure the first event is go to HOME screen # the second event is to start the app if count == 0 and self.master is None: event = KeyEvent(name="HOME") elif count == 1 and self.master is None: event = IntentEvent(self.app.get_start_intent()) else: event = self.generate_event() if network_monitoring == True and hasattr(event,"intent"): process_strace.terminate() network_monitoring = False input_manager.add_event(event) if hasattr(event,"intent"): current_processes = self.device.adb.shell("ps").split("\r\n") import re for currentProcess in current_processes: if currentProcess.find(self.app.package_name) != -1: pattern_result = re.search(r"\s\d+\s", currentProcess) pid = pattern_result.group(0) break from subprocess import Popen, PIPE network_monitoring=True process_strace = Popen(['adb' , 'shell'] , stdin=PIPE) process_strace.stdin.write('su\n') process_strace.stdin.flush() if count==1: monitor_command = "strace -f -e trace=network -s 10000 -p " + pid + " &> /mnt/user/0/primary/networkResults" else: monitor_command = "strace -f -e trace=network -s 10000 -p " + pid + " &>> /mnt/user/0/primary/networkResults" process_strace.stdin.write(monitor_command+'\n') process_strace.stdin.flush() except KeyboardInterrupt: break except InputInterruptedException as e: self.logger.warning("stop sending events: %s" % e) break # except RuntimeError as e: # self.logger.warning(e.message) # break except Exception as e: self.logger.warning("exception during sending events: %s" % e) import traceback traceback.print_exc() continue count += 1 process_strace.terminate() process_grep = Popen(['adb', 'shell'], stdout=PIPE, stdin=PIPE) process_grep.stdin.write('su\n') process_grep.stdin.flush() process_grep.stdin.write('grep -a HTTP/ /mnt/user/0/primary/networkResults' + '\n') process_grep.stdin.flush() stdout_string, stderr_string = process_grep.communicate() process_grep.terminate() import os network_file = open(os.path.join(self.device.output_dir, "HTTPLines.txt"), 'w') network_file.write(stdout_string) # python will convert \n to os.linesep network_file.close() # you can omit in most cases as the destructor will call it
def generate_event_based_on_utg(self): """ generate an event based on current UTG @return: InputEvent """ current_state = self.current_state self.logger.info("Current state: %s" % current_state.state_str) if current_state.state_str in self.__missed_states: self.__missed_states.remove(current_state.state_str) if current_state.get_app_activity_depth(self.app) < 0: # If the app is not in the activity stack start_app_intent = self.app.get_start_intent() # It seems the app stucks at some state, has been # 1) force stopped (START, STOP) # just start the app again by increasing self.__num_restarts # 2) started at least once and cannot be started (START) # pass to let viewclient deal with this case # 3) nothing # a normal start. clear self.__num_restarts. if self.__event_trace.endswith(EVENT_FLAG_START_APP + EVENT_FLAG_STOP_APP) \ or self.__event_trace.endswith(EVENT_FLAG_START_APP): self.__num_restarts += 1 self.logger.info("The app had been restarted %d times.", self.__num_restarts) else: self.__num_restarts = 0 # pass (START) through if not self.__event_trace.endswith(EVENT_FLAG_START_APP): if self.__num_restarts > MAX_NUM_RESTARTS: # If the app had been restarted too many times, enter random mode msg = "The app had been restarted too many times. Entering random mode." self.logger.info(msg) self.__random_explore = True else: # Start the app self.__event_trace += EVENT_FLAG_START_APP self.logger.info("Trying to start the app...") return IntentEvent(intent=start_app_intent) elif current_state.get_app_activity_depth(self.app) > 0: # If the app is in activity stack but is not in foreground self.__num_steps_outside += 1 if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE: # If the app has not been in foreground for too long, try to go back if self.__num_steps_outside > MAX_NUM_STEPS_OUTSIDE_KILL: stop_app_intent = self.app.get_stop_intent() go_back_event = IntentEvent(stop_app_intent) else: go_back_event = KeyEvent(name="BACK") self.__event_trace += EVENT_FLAG_NAVIGATE self.logger.info("Going back to the app...") return go_back_event else: # If the app is in foreground self.__num_steps_outside = 0 # Get all possible input events possible_events = current_state.get_possible_input() count = 0 for activity in self.edit_text_value: count = count + len(self.edit_text_value[activity]) print("Possible events: " + str(len(possible_events))) print("Editable Text Events: " + str(count)) """ for activity in self.edit_text_value: string = activity + ": " for resource_id in self.edit_text_value[activity]: string = string + resource_id + " = " + str(self.edit_text_value[activity][resource_id].text) + ", " print(string) """ variation = False activity_variation = list() event_added = list() for event in possible_events: if isinstance(event, SetTextEvent): activity = event.view["activity"] resource_id = event.view["resource_id"] if activity not in self.edit_text_value or resource_id not in self.edit_text_value[ activity]: if activity not in self.edit_text_value: self.edit_text_value[activity] = dict() self.edit_text_value[activity][resource_id] = event event_added.append(event) variation = True if activity not in activity_variation: activity_variation.append(activity) if variation: for activity in activity_variation: event_list = list(self.edit_text_value[activity].values()) view_text_association = ViewTextAssociation(event_list) complete_event_list = view_text_association.GetSetTextEvents() for new_event in event_added: for event in complete_event_list: activity = event.view["activity"] resource_id = event.view["resource_id"] if new_event.view["resource_id"] == event.view[ "resource_id"]: self.edit_text_value[activity][resource_id] = event if self.random_input: random.shuffle(possible_events) if self.search_method == POLICY_GREEDY_DFS: possible_events.append(KeyEvent(name="BACK")) elif self.search_method == POLICY_GREEDY_BFS: possible_events.insert(0, KeyEvent(name="BACK")) # get humanoid result, use the result to sort possible events # including back events if self.device.humanoid is not None: possible_events = self.__sort_inputs_by_humanoid(possible_events) new_input_event = None if self.last_event is not None and self.last_event.event_type == 'touch' and \ self.last_event.view['class'] == 'android.widget.EditText': resource_id = self.last_event.view['resource_id'] for input_event in possible_events: if isinstance(input_event, SetTextEvent) and \ input_event.view['resource_id'] == resource_id: new_input_event = input_event break last_event_edit = False if self.last_event is not None and self.last_event.event_type == 'set_text' and \ self.last_event.view['class'] == 'android.widget.EditText': resource_id = self.last_event.view['resource_id'] last_event_edit = True # If there is an unexplored event, try the event first for input_event in possible_events: if new_input_event is not None: input_event = new_input_event """ elif last_event_edit: if input_event.view['resource_id'] == resource_id: continue """ if not self.utg.is_event_explored(event=input_event, state=current_state): self.logger.info("Trying an unexplored event.") self.__event_trace += EVENT_FLAG_EXPLORE if isinstance(input_event, SetTextEvent): resource_id = input_event.view["resource_id"] activity = input_event.view["activity"] for event_resource_id in self.edit_text_value[activity]: if resource_id == event_resource_id: input_event.text = self.edit_text_value[activity][ event_resource_id].text print(input_event) return input_event target_state = self.__get_nav_target(current_state) if target_state: event_path = self.utg.get_event_path(current_state=current_state, target_state=target_state) if event_path and len(event_path) > 0: self.logger.info("Navigating to %s, %d steps left." % (target_state.state_str, len(event_path))) self.__event_trace += EVENT_FLAG_NAVIGATE return event_path[0] if self.__random_explore: self.logger.info("Trying random event.") random.shuffle(possible_events) return possible_events[0] # If couldn't find a exploration target, stop the app stop_app_intent = self.app.get_stop_intent() self.logger.info( "Cannot find an exploration target. Trying to restart app...") self.__event_trace += EVENT_FLAG_STOP_APP return IntentEvent(intent=stop_app_intent)