def search_popup_window_group(log_info, logs, curr_idx): content = log_info['content'] popup_window = content['popupWindow'] msg_type = content['msgType'] popup_window_width = int(content['width']) popup_window_heigth = int(content['height']) if msg_type.lower() == 'action' and content['action'].lower() == 'show': # if msg_type.lower() == 'action' and content['action'].lower() == 'show' and (popup_window_width != -2 or popup_window_heigth != -2): idx = curr_idx + 1 while idx < len(logs): _log_info = extract_info(logs[idx]) _content = _log_info['content'] idx += 1 if _log_info['tag'] == POPUPWINDOW_TAG and _content[ 'popupWindow'] == popup_window and _content[ 'action'].lower() == 'hide': # search view v_idx = curr_idx + 1 view_groups = list() while v_idx < idx - 1: v_log_info = extract_info(logs[v_idx]) if v_log_info['tag'] == DIALOG_TAG: group, next_v_idx = search_dialog_group( v_log_info, logs, v_idx) if group is None: v_idx += 1 else: v_idx += 1 view_groups.append(group) elif v_log_info['tag'] == VIEW_TAG: group, next_v_idx = search_view_group( v_log_info, logs, v_idx) if group is None: v_idx += 1 else: v_idx = next_v_idx view_groups.append(group) else: v_idx += 1 continue return (curr_idx, idx - 1, POPUPWINDOW_TAG, popup_window, view_groups, msg_type), idx return None, curr_idx + 1
def preprocess(file): """ 1. Eliminate redundant log 2. Eliminate irrelevant logs (Only reserve [Activity], [Dialog], [PopupWindow], [ViewOnTouchEvent], [EditableInputConnection], [SpannerStringBuilder], [TextViewKeyboard]) """ valid_logs = list() with open(file, 'r') as f: prev_log = None for log in f: log = log.strip() if not find_match(log): continue if VIEW_PATTERN.match(log): log_info = extract_info(log) if 'handle' in log_info['content']: continue if prev_log is None: prev_log = log valid_logs.append(log) else: if LOCATION_LISTENER_PATTERN.match( log) or SENSOR_LISTENER_PATTERN.match(log): valid_logs.append(log) prev_log = log else: if compare_logs(prev_log, log): continue valid_logs.append(log) prev_log = log save_path = save_file(file, 'preprocess', '\n'.join(valid_logs)) return save_path
def clear_logs(logs, target_tags): valid_logs = list() for log in logs: lf = extract_info(log) if lf['tag'] in target_tags: valid_logs.append(log) return valid_logs
def compare_logs(log_1, log_2): log_1_info, log_2_info = extract_info(log_1), extract_info(log_2) keys = ['tag', 'plid', 'package'] for k in keys: if log_1_info[k] != log_2_info[k]: return False if len(log_1_info['content']) != len(log_2_info['content']): return False keys = list(log_1_info['content'].keys()) for k in keys: if k == 'target' or k == 'webview': continue if log_1_info['content'][k] != log_2_info['content'][k]: return False return True
def search_activity_group(log_info, logs, curr_idx): """ <down_idx, up_idx> TouchEvent and KeyEvent may be missed in activity """ content = log_info['content'] plid = log_info['plid'] package = log_info['package'] activity = content['activity'] msg_type = content['msgType'] search_target = None search_msg_type = None search_action_name = None search_action = None if msg_type.lower() == 'touchevent' and content['actionId'] == 0: # Touch down search_msg_type = msg_type search_action_name = 'actionId' search_action = 1 search_target = activity down_time = content['downTime'] if msg_type.lower() == 'keyevent' and content['actionCode'] == 0: # Key down search_msg_type = msg_type search_action_name = 'actionCode' search_action = 1 search_target = activity down_time = content['downTime'] if search_target is None: return None, curr_idx + 1 # Search idx = curr_idx + 1 return_idx = -1 while idx < len(logs): _log_info = extract_info(logs[idx]) _content = _log_info['content'] idx += 1 if _log_info['tag'] == ACTIVITY_TAG and _content['msgType'] == search_msg_type \ and _content['activity'] == search_target and _content[search_action_name] == search_action and _content['downTime'] == down_time: if return_idx == -1: return_idx = idx return (curr_idx, idx - 1, ACTIVITY_TAG, activity, search_msg_type), return_idx if _log_info['tag'] == POPUPWINDOW_TAG: return_idx = idx - 1 return (curr_idx, curr_idx, ACTIVITY_TAG, activity, search_msg_type), curr_idx + 1
def group_logs(file): """ 1. Activity: a. touch: DOWN -> UP b. key: DOWN -> UP 2. Dialog: a. touch: DOWN -> UP b. key: DOWN -> UP 3. PopupWindow: a. action: show -> hide 4. View: a. touch: DOWN -> UP 5. SpannerStringBuilder a. text 6. EditableInputConnection: a. show: first-appear, last-appear 7. WebViewConsole 8. WebViewClient """ logs = list() with open(file, 'r') as f: logs = f.readlines() func_dict = { ACTIVITY_TAG: search_activity_group, DIALOG_TAG: search_dialog_group, POPUPWINDOW_TAG: search_popup_window_group, EDITABLE_INPUT_CONNECTION_TAG: search_editable_input_connection_group, TEXT_VIEW_KEY_TAG: search_textview_keyboard_group, WEBVIEW_CONSOLE_TAG: search_webview_console_group, WEBVIEW_CLIENT_TAG: search_webview_client_group, SENSOR_LISTENER_TAG: search_sensor_listener_group, LOCATION_MANAGER_TAG: search_location_manager_group, LOCATION_LISTENER_TAG: search_location_listener_group } logs_length = len(logs) idx = 0 groups = list() while idx < logs_length: log_info = extract_info(logs[idx]) tag = log_info['tag'] if tag in func_dict: group_info, next_idx = func_dict[tag](log_info, logs, idx) idx = next_idx if group_info is None: continue groups.append(group_info) else: idx += 1 pprint(groups) save_path = save_file(file, 'groups', '\n'.join([str(g) for g in groups])) return save_path, groups, logs
def search_dialog_group(log_info, logs, curr_idx): """ <down_idx, up_idx> Auto complete dialog event since the event may fail to be logged when the dialog is destroying """ content = log_info['content'] dialog = content['dialog'] msg_type = content['msgType'] search_target = None search_msg_type = None search_action_name = None search_action = None if msg_type.lower() == 'touchevent' and content['actionId'] == 0: # Touch down search_msg_type = msg_type search_action_name = 'actionId' search_action = 1 search_target = dialog if msg_type.lower() == 'keyevent' and content['actionCode'] == 0: # Key down search_msg_type = msg_type search_action_name = 'actionCode' search_action = 1 search_target = dialog if search_target is None: return None, curr_idx + 1 # Search idx = curr_idx + 1 while idx < len(logs): _log_info = extract_info(logs[idx]) _content = _log_info['content'] idx += 1 if _log_info['tag'] == DIALOG_TAG and _content[ 'msgType'] == search_msg_type and _content[ 'dialog'] == search_target and _content[ search_action_name] == search_action: return (curr_idx, idx - 1, DIALOG_TAG, dialog, search_msg_type), idx return (curr_idx, curr_idx, DIALOG_TAG, dialog, search_msg_type), curr_idx + 1
def search_view_group(log_info, logs, curr_idx): content = log_info['content'] msg_type = content['msgType'].lower() if msg_type == 'touchevent' and content['actionId'] == 0: view = content['view'] down_time = content['downTime'] idx = curr_idx + 1 while idx < len(logs): _log_info = extract_info(logs[idx]) _content = _log_info['content'] idx += 1 if _log_info['tag'] == VIEW_TAG and _content['msgType'].lower( ) == msg_type and _content['downTime'] == down_time and _content[ 'actionId'] == 1: return (curr_idx, idx - 1, VIEW_TAG, view, msg_type), idx return None, curr_idx + 1
def search_textview_keyboard_group(log_info, logs, curr_idx): content = log_info['content'] msg_type = content['msgType'].lower() textview_id = content['viewId'] if msg_type == 'keyevent' and content['actionCode'] == 0: down_time = content['downTime'] idx = curr_idx + 1 while idx < len(logs): _log_info = extract_info(logs[idx]) _content = _log_info['content'] idx += 1 if _log_info['tag'] == TEXT_VIEW_KEY_TAG and _content[ 'msgType'].lower() == msg_type and _content[ 'downTime'] == down_time and _content[ 'actionCode'] == 1: return (curr_idx, idx - 1, TEXT_VIEW_KEY_TAG, textview_id, msg_type), curr_idx + 1 return None, curr_idx + 1
def search_editable_input_connection_group(log_info, logs, curr_idx): content = log_info['content'] editable_input_connection = content['editableInputConnection'] msg_type = content['msgType'] nested_group = list() if msg_type.lower() == 'type' and content['mBatchEditNesting'] != -1: idx = curr_idx + 1 last_log_idx = curr_idx # Handle overlap EditableInputConnection first_overlap_idx = None while idx < len(logs): _log_info = extract_info(logs[idx]) _content = _log_info['content'] idx += 1 if _log_info['tag'] == EDITABLE_INPUT_CONNECTION_TAG: if _content[ 'editableInputConnection'] == editable_input_connection: last_log_idx = idx - 1 if _content['mBatchEditNesting'] == -1: if len(nested_group) > 0: nested_group = remove_invalid_groups( last_log_idx, nested_group) return (curr_idx, last_log_idx, EDITABLE_INPUT_CONNECTION_TAG, editable_input_connection, nested_group, msg_type), idx else: if first_overlap_idx is None: first_overlap_idx = idx - 1 else: first_overlap_log_info = extract_info( logs[first_overlap_idx]) if first_overlap_log_info['content'][ 'editableInputConnection'] != _content[ 'editableInputConnection']: # Second overlap, then dump if len(nested_group) > 0: nested_group = remove_invalid_groups( last_log_idx, nested_group) return_idx = min(first_overlap_idx, last_log_idx) + 1 return (curr_idx, last_log_idx, EDITABLE_INPUT_CONNECTION_TAG, editable_input_connection, nested_group, msg_type), return_idx # Support nested interaction with activity and dialog if _log_info['tag'] == ACTIVITY_TAG: group, _ = search_activity_group(_log_info, logs, idx - 1) if group is not None: nested_group.append(group) elif _log_info['tag'] == DIALOG_TAG: group, _ = search_dialog_group(_log_info, logs, idx - 1) if group is not None: nested_group.append(group) elif _log_info['tag'] == TEXT_VIEW_KEY_TAG: group, _ = search_textview_keyboard_group( _log_info, logs, idx - 1) if group is not None: nested_group.append(group) elif _log_info['tag'] == POPUPWINDOW_TAG: group, _ = search_popup_window_group(_log_info, logs, idx - 1) if group is not None: nested_group.append(group) if last_log_idx != curr_idx: if len(nested_group) > 0: nested_group = remove_invalid_groups(last_log_idx, nested_group) return_idx = last_log_idx + 1 if first_overlap_idx is None or ( last_log_idx < first_overlap_idx) else first_overlap_idx + 1 return (curr_idx, last_log_idx, EDITABLE_INPUT_CONNECTION_TAG, editable_input_connection, nested_group, msg_type), return_idx return None, curr_idx + 1