定位SystemUi控件的普适方法

21k words

Hook SystemUi

0x01: 前言

在完善BetterBar的时候,想要快速定位某个控件的位置,搞清楚每个控件的作用。由于不知道系统应用应该如何动态调试,所以之前都是根据类名猜个大概,然后去对应View的onxxx函数里去创建view或者修改该view的背景为红色,这样比较醒目,如果打开手机对应位置看到了红色,说明定位到了。

但这种方式太低效了,首先Systemui里有很多类是没用的,而且有许多命名相似的类,所以前期开发苦不堪言。好在之前运气不错,记录了许多类对应的功能。但是今天突然想要修改xml里的某个控件的属性:

1
2
3
4
5
6
7
8
9
10
11
12
override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
resparam.res.hookLayout("com.android.systemui", "layout", "control_center", object : XC_LayoutInflated() {
override fun handleLayoutInflated(liparam: LayoutInflatedParam) {
// 获取根视图 ControlCenterPanelView
val rootView = liparam.view as ViewGroup

val childView = rootView.getChildAt(0)
childView.visibility = View.GONE

}
})
}

这段代码很简单,就是在隐藏control_cente.xml中根布局下的第一个控件。写完之后对应位置的view显著的消失了,到这里我突发奇想,那如果我直接把所有子视图都隐藏了,那是不是就能快速定位到这个xml的作用了呢,如果xml功能符合我们的预期,那我们就能进而分析子控件对应的功能了!

0x02: 开干

说干就干,代码也只要稍作修改即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

override fun handleInitPackageResources(resparam: XC_InitPackageResources.InitPackageResourcesParam) {
resparam.res.hookLayout("com.android.systemui", "layout", "control_center", object : XC_LayoutInflated() {
override fun handleLayoutInflated(liparam: LayoutInflatedParam) {
// 获取根视图 ControlCenterPanelView
val rootView = liparam.view as ViewGroup

// 遍历所有子视图并隐藏
for (i in 0 until rootView.childCount) {
val childView = rootView.getChildAt(i)
childView.visibility = View.GONE
}
}
})

    }

这样之后,一大片布局都消失了,确实符合预期。但是SystemUi里这么多xml,该如何确定想要的具体是哪个xml呢?

那当然是 穷举 + 二分,我们可以借助伟大的ai!,直接把apk/res/layout解压出来,比如解压到Download/layout下了,那么我们直接:

1
2
cd Download/layout
ls

我们把得到的所有xml名都复制丢给ai,并且让他按照命名分类(以SystemUi举例,里面有很多以status, notification…开头的xml,按照名字前缀分类即可),然后分别取隐藏每个xml的子视图,这样我们hook之后,重启SystemUi会发现一堆东西都缺失了。那么接下来我们该怎么定位呢?

0x03: 伟大的二分法

如题所言,我们可以使用二分法。比如我的代码是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
val statusXmls = listOf(
"status_bar",
"status_bar_expanded",
"status_bar_expanded_plugin_frame",
"status_bar_hot_zone_guide",
"status_bar_mobile_signal_group",
"status_bar_mobile_signal_group_inner",
"status_bar_mobile_signal_group_new",
"status_bar_no_notifications",
"status_bar_notification_footer",
"status_bar_notification_row",
"status_bar_notification_section_header",
"status_bar_notification_shelf",
"status_bar_ticker",
"status_bar_ticker_ext",
"status_bar_user_chip_container",
"status_bar_wifi_group",
"status_bar_wifi_group_inner"
)

// 遍历列表,对每个 xml 文件进行 Hook
for (xmlName in statusXmls) {
resparam.res.hookLayout("com.android.systemui", "layout", xmlName, object : XC_LayoutInflated() {
override fun handleLayoutInflated(liparam: LayoutInflatedParam) {
// 获取根视图
val rootView = liparam.view as ViewGroup
// 遍历所有子视图并隐藏
for (i in 0 until rootView.childCount) {
val childView = rootView.getChildAt(i)
childView.visibility = View.GONE
}
}
})
}

我们可以先注释掉statusXmls的下半部分,接着继续查看对应控件是否被隐藏,如果依旧被隐藏,说明我们需要的xml还在上半部分,然后我们再次注释上半部分的一半,继续查看,以此类推…这是典型的二分查找,比之前静态分析效率高很多,就是手机容易红温,我试了几次手机就发烫了。工程量小可以自己写,工程量大的话还是使用ai大法吧。

不过东西多了ai就开始偷懒了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
        // 菜单相关 XML 文件列表
val menuXmls = listOf(
"abc_action_bar_title_item", "abc_action_menu_item_layout", "abc_action_menu_layout",
"abc_action_mode_bar", "abc_action_mode_close_item_material", "abc_cascading_menu_item_layout",
"abc_expanded_menu_layout", "abc_list_menu_item_checkbox", "abc_list_menu_item_icon",
"abc_list_menu_item_layout", "abc_list_menu_item_radio", "abc_popup_menu_header_item_layout",
"abc_popup_menu_item_layout", "mz_abc_action_menu_layout", "mz_abc_action_mode_bar",
"mz_action_bar_drop_down_view", "mz_action_bar_title_item", "mz_action_menu_item_layout",
"mz_action_menu_item_split_layout", "mz_action_multi_choice_mode_close_item",
"mz_action_multi_choice_mode_select_all_item", "mz_expanded_menu_layout",
"mz_list_menu_item_icon", "mz_list_menu_item_layout", "mz_popup_menu_item_layout"
)

// 对话框相关 XML 文件列表
val dialogXmls = listOf(
"abc_alert_dialog_button_bar_material", "abc_alert_dialog_material",
"abc_alert_dialog_title_material", "abc_dialog_title_material", "abc_select_dialog_material",
"grant_admin_dialog_content", "mr_cast_dialog", "mr_chooser_dialog", "mr_controller_material_dialog_b",
"mr_picker_dialog", "mtrl_alert_dialog", "mtrl_alert_dialog_actions", "mtrl_alert_dialog_title",
"mtrl_alert_select_dialog_item", "mtrl_alert_select_dialog_multichoice",
"mtrl_alert_select_dialog_singlechoice", "mz_alert_dialog_appcompat", "mz_alert_dialog_button_bar",
"alert_dialog_button_bar_systemui", "alert_dialog_systemui", "alert_dialog_title_systemui",
"scrollable_alert_dialog_systemui", "loading_alert_dialog", "dialog_with_icon",
"privacy_dialog", "privacy_dialog_item", "zen_mode_turn_on_dialog_container",
"zen_mode_duration_dialog", "controls_detail_dialog", "controls_dialog", "controls_dialog_pin",
"media_session_end_dialog", "media_output_dialog", "media_output_broadcast_update_dialog",
"font_scaling_dialog", "contrast_dialog", "long_screenshot", "full_screen_permission_circle",
"broadcast_dialog", "letterbox_education_dialog_layout", "letterbox_education_dialog_action_layout",
"letterbox_restart_dialog_layout", "log_access_user_consent_dialog_permission",
"user_creation_progress_dialog", "edit_user_info_dialog_content"
)

// 状态栏相关 XML 文件列表
val statusBarXmls = listOf(
"status_bar", "status_bar_expanded", "status_bar_expanded_plugin_frame",
"status_bar_hot_zone_guide", "status_bar_mobile_signal_group", "status_bar_mobile_signal_group_inner",
"status_bar_mobile_signal_group_new", "status_bar_no_notifications", "status_bar_notification_footer",
"status_bar_notification_row", "status_bar_notification_section_header",
"status_bar_notification_shelf", "status_bar_ticker", "status_bar_ticker_ext",
"status_bar_user_chip_container", "status_bar_wifi_group", "status_bar_wifi_group_inner",
"new_status_bar_wifi_group", "control_center_status_bar", "super_status_bar"
)

// 锁屏相关 XML 文件列表
val keyguardXmls = listOf(
"keyguard_bottom_area", "keyguard_bottom_area_overlay", "keyguard_bouncer_message_area",
"keyguard_bouncer_status_bar", "keyguard_bouncer_user_switcher",
"keyguard_bouncer_user_switcher_item", "keyguard_clock_switch", "keyguard_emergency_carrier_area",
"keyguard_media_container", "keyguard_message_area", "keyguard_num_pad_key",
"keyguard_palm_rejection_layout", "keyguard_pattern_view", "keyguard_pin_shape_hinting_view",
"keyguard_pin_shape_non_hinting_view", "keyguard_presentation", "keyguard_qs_user_switch",
"keyguard_screen_findphone_portrait", "keyguard_security_container_view",
"keyguard_settings_popup_menu", "keyguard_slice_view", "keyguard_status_bar",
"keyguard_status_view", "keyguard_user_switcher", "keyguard_user_switcher_item",
"mz_keyguard_bottom_area", "mz_keyguard_bouncer_frame", "mz_keyguard_emergency_carrier_area",
"mz_keyguard_emergency_carrier_area_pin_puk", "mz_keyguard_password_view",
"mz_keyguard_pin_view", "mz_keyguard_sim_pin_view", "mz_keyguard_sim_puk_view",
"mz_keyguard_screen_weather_and_date_widget", "mz_keyguard_screen_weather_and_date_widget_core",
"mz_keyguard_screen_weather_and_date_widget_core_from_editor"
)

// 通知相关 XML 文件列表
val notificationXmls = listOf(
"notification_children_container", "notification_children_divider",
"notification_conversation_info", "notification_filter_panel", "notification_group_collapse",
"notification_gut_setting_view", "notification_guts", "notification_guts_f9",
"notification_icon_area", "notification_info", "notification_media_action",
"notification_media_cancel_action", "notification_snooze", "notification_snooze_option",
"notification_stack_scroll_layout", "notification_template_big_media",
"notification_template_big_media_custom", "notification_template_big_media_narrow",
"notification_template_big_media_narrow_custom", "notification_template_lines_media",
"notification_template_media", "notification_template_media_custom",
"notification_template_part_chronometer", "notification_template_part_time",
"super_notification_shade", "notif_half_shelf", "notif_half_shelf_row",
"ongoing_call_chip", "ongoing_privacy_chip", "flyme_ongoing_privacy_chip",
"aosp_ongoing_privacy_chip", "tv_ongoing_privacy_chip"
)

// 其他 XML 文件列表
val otherXmls = listOf(
"home", "home_handle", "hybrid_conversation_notification", "hybrid_notification",
"hybrid_overflow_number", "illustration_preference", "image_frame", "ime_switcher",
"inattentive_sleep_warning", "internet_connectivity_dialog", "internet_list_item",
"invocation_lights", "item_clock_style", "keyboard_shortcut_app_item",
"keyboard_shortcuts_category_separator", "keyboard_shortcuts_category_short_separator",
"keyboard_shortcuts_category_title", "keyboard_shortcuts_container",
"keyboard_shortcuts_key_icon_view", "keyboard_shortcuts_key_new_icon_view",
"keyboard_shortcuts_key_new_view", "keyboard_shortcuts_key_plus_view",
"keyboard_shortcuts_key_vertical_bar_view", "keyboard_shortcuts_key_view",
"keyboard_shortcuts_search_view", "keyboard_shortcuts_view", "lb_action_1_line",
"lb_action_2_lines", "lb_browse_fragment", "lb_browse_title", "lb_control_bar",
"lb_control_button_primary", "lb_control_button_secondary", "lb_details_description",
"lb_details_fragment", "lb_details_overview", "lb_divider", "lb_error_fragment",
"lb_fullwidth_details_overview", "lb_fullwidth_details_overview_logo", "lb_guidance",
"lb_guidedactions", "lb_guidedactions_datepicker_item", "lb_guidedactions_item",
"lb_guidedbuttonactions", "lb_guidedstep_background", "lb_guidedstep_fragment",
"lb_header", "lb_headers_fragment", "lb_image_card_view",
"lb_image_card_view_themed_badge_left", "lb_image_card_view_themed_badge_right",
"lb_image_card_view_themed_content", "lb_image_card_view_themed_title", "lb_list_row",
"lb_list_row_hovercard", "lb_media_item_number_view_flipper", "lb_media_list_header",
"lb_onboarding_fragment", "lb_picker", "lb_picker_column", "lb_picker_item",
"lb_picker_separator", "lb_pinpicker_item", "lb_playback_controls",
"lb_playback_controls_row", "lb_playback_fragment", "lb_playback_now_playing_bars",
"lb_playback_transport_controls_row", "lb_row_container", "lb_row_header",
"lb_row_media_item", "lb_row_media_item_action", "lb_rows_fragment", "lb_search_bar",
"lb_search_fragment", "lb_search_orb", "lb_section_header", "lb_shadow",
"lb_speech_orb", "lb_title_view", "lb_vertical_grid", "lb_vertical_grid_fragment",
"lb_video_surface", "mc_badge_view_image_item", "mc_badge_view_point_view_item",
"mc_badge_view_text_item", "mc_content_toast_layout_light", "mc_custom_date_picker",
"mc_custom_date_picker_dialog", "mc_custom_picker_24hour", "mc_date_picker",
"mc_date_picker_base", "mc_date_picker_day_time_dialog", "mc_date_picker_day_time_layout",
"mc_date_picker_dialog", "mc_date_picker_native_dialog", "mc_empty_view",
"mc_expandable_preference_layout", "mc_expandable_preference_list_item",
"mc_guide_popup_window", "mc_install_progress_bar_layout", "mc_layout_password_input",
"mc_letter_overlay", "mc_list_category_contact_partition_header", "mc_move_search_layout",
"mc_pinned_group_header", "mc_pinned_header_view", "mc_preference_widget_switch",
"mc_search_layout", "mc_selection_button", "mc_slide_notice_content",
"mc_stretch_search_layout", "mc_stretch_search_layout_ext", "mc_time_picker_column_12",
"mc_time_picker_column_24", "mc_time_picker_dialog", "mc_toast_layout",
"mc_weekday_picker_item", "media_carousel", "media_carousel_settings_button",
"media_controller_switch_view", "media_controller_switch_view_area",
"media_long_press_menu", "media_output_broadcast_area", "media_projection_app_selector",
"media_projection_recent_tasks", "media_projection_task_item", "media_recommendation_view",
"media_recommendations", "media_session_view", "media_smartspace_recommendations",
"media_ttt_chip_receiver", "menu_ime", "misc_tile_layout", "nav_key_space",
"navigation_bar", "navigation_bar_window", "navigation_layout", "navigation_layout_vertical",
"new_status_bar_wifi_group", "operator_name", "overlay_action_chip",
"panel_hot_zone_guide", "partial_conversation_info", "partner_customization_layout",
"people_space_activity", "people_space_activity_list_divider",
"people_space_activity_no_conversations", "people_space_activity_with_conversations",
"people_space_placeholder_layout", "people_space_tile_view", "people_status_scrim_layout",
"people_tile_emoji_background_large", "people_tile_emoji_background_medium",
"people_tile_large_empty", "people_tile_large_with_content",
"people_tile_large_with_notification_content", "people_tile_large_with_status_content",
"people_tile_medium_empty", "people_tile_medium_with_content",
"people_tile_punctuation_background_large", "people_tile_punctuation_background_medium",
"people_tile_small", "people_tile_small_horizontal", "people_tile_suppressed_layout",
"people_tile_with_suppression_detail_content_horizontal",
"people_tile_with_suppression_detail_content_vertical",
"people_tile_work_profile_quiet_layout", "photo_preview_overlay", "pip_menu",
"pip_menu_action", "power_notification_controls_settings", "preference",
"preference_category", "preference_category_material", "preference_dialog_edittext",
"preference_dropdown", "preference_dropdown_material", "preference_list_fragment",
"preference_material", "preference_recyclerview", "preference_usage_progress_bar",
"preference_widget_checkbox", "preference_widget_primary_switch",
"preference_widget_radiobutton", "preference_widget_seekbar",
"preference_widget_seekbar_material", "preference_widget_switch",
"preference_widget_switch_compat", "quick_access_wallet", "quick_settings_brightness_dialog",
"quick_settings_footer_dialog", "quick_settings_footer_dialog_parental_controls",
"quick_status_bar_expanded_header", "reachability_ui_layout", "recent_apps",
"remote_input", "restricted_switch_widget", "rotate_suggestion",
"screen_decor_hwc_layer", "screen_pinning_request", "screen_pinning_request_buttons",
"screen_pinning_request_buttons_land", "screen_pinning_request_buttons_sea",
"screen_pinning_request_land_phone", "screen_pinning_request_sea_phone",
"screen_pinning_request_text_area", "screen_record_dialog",
"screen_record_dialog_audio_source", "screen_record_dialog_audio_source_selected",
"screen_record_options", "screen_share_dialog", "screen_share_dialog_spinner_item_text",
"screen_share_dialog_spinner_text", "screenshot", "screenshot_detection_notice",
"screenshot_notification_share_and_delete", "screenshot_static",
"screenshot_work_profile_first_run", "settings_bar_chart", "settings_bar_view",
"settings_spinner_preference", "settingslib_button_layout", "settingslib_main_switch_layout",
"shade_carrier", "shade_carrier_group", "sidefps_view", "slice_permission_request",
"smart_action_button", "smart_reply_button", "smart_reply_view", "smarttouch_help",
"smarttouch_mainviewgroup", "split_decor", "split_divider", "status_bar_no_notifications",
"support_simple_spinner_dropdown_item", "switch_bar", "system_event_animation_window",
"system_icons", "tab_fragment", "text_toast", "tile_service_request_dialog",
"toast_low_power", "tuner_activity", "tuner_shortcut_item", "tuner_shortcut_list",
"tuner_widget_settings_switch", "tv_bottom_sheet", "tv_notification_item",
"tv_notification_panel", "tv_pip_menu", "tv_pip_menu_background",
"tv_privacy_chip_container", "tv_split_menu_view", "tv_window_menu_action_button",
"udfps_fpm_empty_view", "udfps_keyguard_preview", "udfps_keyguard_view_internal",
"udfps_keyguard_view_legacy", "udfps_view", "user_switcher_fullscreen",
"user_switcher_fullscreen_item", "user_switcher_fullscreen_popup_item",
"volume_dialog", "volume_dialog_row", "volume_dnd_icon", "volume_panel",
"volume_panel_dialog", "volume_panel_item", "volume_panel_slice_slider_row",
"volume_panel_sub", "volume_ringer_drawer", "volume_tool_tip_view", "wallet_card_view",
"wallet_empty_state", "wallet_fullscreen", "wallpaper_text", "window_magnification_settings_view",
"window_magnifier_view", "wiredcharge_layout", "wireless_charging_layout",
"world_clock_view", "zen_mode_condition", "zen_mode_radio_button"
)

// 隐藏指定 XML 文件子视图的函数
fun hideXmlChildren(xmlNames: List<String>) {
for (xmlName in xmlNames) {
resparam.res.hookLayout("com.android.systemui", "layout", xmlName, object : XC_LayoutInflated() {
override fun handleLayoutInflated(liparam: LayoutInflatedParam) {
val rootView = liparam.view as ViewGroup
for (i in 0 until rootView.childCount) {
val childView = rootView.getChildAt(i)
childView.visibility = View.GONE
}
}
})
}
}

// 隐藏菜单相关 XML 文件的子视图
//hideXmlChildren(menuXmls)
// 隐藏对话框相关 XML 文件的子视图
//hideXmlChildren(dialogXmls)
// 隐藏状态栏相关 XML 文件的子视图
//hideXmlChildren(statusBarXmls)
// 隐藏锁屏相关 XML 文件的子视图
//hideXmlChildren(keyguardXmls)
// 隐藏通知相关 XML 文件的子视图
//hideXmlChildren(notificationXmls)
// 隐藏其他 XML 文件的子视图
//hideXmlChildren(otherXmls)

可以看到下面他已经懒得分类了哈哈哈哈哈哈哈

0x04: 小结

虽然是笨方法,但起码是找到了各个场景通用的解决方案:)

周末快乐!