diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c986629 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +indent_size = 4 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.kt] +# Compose @Composable functions use PascalCase by convention +ktlint_function_naming_ignore_when_annotated_with = Composable +# Wildcard imports are idiomatic for Compose (androidx.compose.*) +ktlint_standard_no-wildcard-imports = disabled diff --git a/kmod/.clang-format b/kmod/.clang-format new file mode 100644 index 0000000..8d01225 --- /dev/null +++ b/kmod/.clang-format @@ -0,0 +1,684 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 11. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | LC_ALL=C sort -u +ForEachMacros: + - '__ata_qc_for_each' + - '__bio_for_each_bvec' + - '__bio_for_each_segment' + - '__evlist__for_each_entry' + - '__evlist__for_each_entry_continue' + - '__evlist__for_each_entry_from' + - '__evlist__for_each_entry_reverse' + - '__evlist__for_each_entry_safe' + - '__for_each_mem_range' + - '__for_each_mem_range_rev' + - '__for_each_thread' + - '__hlist_for_each_rcu' + - '__map__for_each_symbol_by_name' + - '__perf_evlist__for_each_entry' + - '__perf_evlist__for_each_entry_reverse' + - '__perf_evlist__for_each_entry_safe' + - '__rq_for_each_bio' + - '__shost_for_each_device' + - 'apei_estatus_for_each_section' + - 'ata_for_each_dev' + - 'ata_for_each_link' + - 'ata_qc_for_each' + - 'ata_qc_for_each_raw' + - 'ata_qc_for_each_with_internal' + - 'ax25_for_each' + - 'ax25_uid_for_each' + - 'bio_for_each_bvec' + - 'bio_for_each_bvec_all' + - 'bio_for_each_folio_all' + - 'bio_for_each_integrity_vec' + - 'bio_for_each_segment' + - 'bio_for_each_segment_all' + - 'bio_list_for_each' + - 'bip_for_each_vec' + - 'bond_for_each_slave' + - 'bond_for_each_slave_rcu' + - 'bpf__perf_for_each_map' + - 'bpf__perf_for_each_map_named' + - 'bpf_for_each_spilled_reg' + - 'bpf_object__for_each_map' + - 'bpf_object__for_each_program' + - 'bpf_object__for_each_safe' + - 'bpf_perf_object__for_each' + - 'btree_for_each_safe128' + - 'btree_for_each_safe32' + - 'btree_for_each_safe64' + - 'btree_for_each_safel' + - 'card_for_each_dev' + - 'cgroup_taskset_for_each' + - 'cgroup_taskset_for_each_leader' + - 'cpufreq_for_each_efficient_entry_idx' + - 'cpufreq_for_each_entry' + - 'cpufreq_for_each_entry_idx' + - 'cpufreq_for_each_valid_entry' + - 'cpufreq_for_each_valid_entry_idx' + - 'css_for_each_child' + - 'css_for_each_descendant_post' + - 'css_for_each_descendant_pre' + - 'damon_for_each_region' + - 'damon_for_each_region_safe' + - 'damon_for_each_scheme' + - 'damon_for_each_scheme_safe' + - 'damon_for_each_target' + - 'damon_for_each_target_safe' + - 'data__for_each_file' + - 'data__for_each_file_new' + - 'data__for_each_file_start' + - 'device_for_each_child_node' + - 'displayid_iter_for_each' + - 'dma_fence_array_for_each' + - 'dma_fence_chain_for_each' + - 'dma_fence_unwrap_for_each' + - 'dma_resv_for_each_fence' + - 'dma_resv_for_each_fence_unlocked' + - 'do_for_each_ftrace_op' + - 'drm_atomic_crtc_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane_state' + - 'drm_atomic_for_each_plane_damage' + - 'drm_client_for_each_connector_iter' + - 'drm_client_for_each_modeset' + - 'drm_connector_for_each_possible_encoder' + - 'drm_for_each_bridge_in_chain' + - 'drm_for_each_connector_iter' + - 'drm_for_each_crtc' + - 'drm_for_each_crtc_reverse' + - 'drm_for_each_encoder' + - 'drm_for_each_encoder_mask' + - 'drm_for_each_fb' + - 'drm_for_each_legacy_plane' + - 'drm_for_each_plane' + - 'drm_for_each_plane_mask' + - 'drm_for_each_privobj' + - 'drm_mm_for_each_hole' + - 'drm_mm_for_each_node' + - 'drm_mm_for_each_node_in_range' + - 'drm_mm_for_each_node_safe' + - 'dsa_switch_for_each_available_port' + - 'dsa_switch_for_each_cpu_port' + - 'dsa_switch_for_each_port' + - 'dsa_switch_for_each_port_continue_reverse' + - 'dsa_switch_for_each_port_safe' + - 'dsa_switch_for_each_user_port' + - 'dsa_tree_for_each_user_port' + - 'dso__for_each_symbol' + - 'dsos__for_each_with_build_id' + - 'elf_hash_for_each_possible' + - 'elf_section__for_each_rel' + - 'elf_section__for_each_rela' + - 'elf_symtab__for_each_symbol' + - 'evlist__for_each_cpu' + - 'evlist__for_each_entry' + - 'evlist__for_each_entry_continue' + - 'evlist__for_each_entry_from' + - 'evlist__for_each_entry_reverse' + - 'evlist__for_each_entry_safe' + - 'flow_action_for_each' + - 'for_each_acpi_dev_match' + - 'for_each_active_dev_scope' + - 'for_each_active_drhd_unit' + - 'for_each_active_iommu' + - 'for_each_aggr_pgid' + - 'for_each_available_child_of_node' + - 'for_each_bench' + - 'for_each_bio' + - 'for_each_board_func_rsrc' + - 'for_each_btf_ext_rec' + - 'for_each_btf_ext_sec' + - 'for_each_bvec' + - 'for_each_card_auxs' + - 'for_each_card_auxs_safe' + - 'for_each_card_components' + - 'for_each_card_dapms' + - 'for_each_card_pre_auxs' + - 'for_each_card_prelinks' + - 'for_each_card_rtds' + - 'for_each_card_rtds_safe' + - 'for_each_card_widgets' + - 'for_each_card_widgets_safe' + - 'for_each_cgroup_storage_type' + - 'for_each_child_of_node' + - 'for_each_clear_bit' + - 'for_each_clear_bit_from' + - 'for_each_clear_bitrange' + - 'for_each_clear_bitrange_from' + - 'for_each_cmd' + - 'for_each_cmsghdr' + - 'for_each_collection' + - 'for_each_comp_order' + - 'for_each_compatible_node' + - 'for_each_component_dais' + - 'for_each_component_dais_safe' + - 'for_each_console' + - 'for_each_cpu' + - 'for_each_cpu_and' + - 'for_each_cpu_not' + - 'for_each_cpu_wrap' + - 'for_each_dapm_widgets' + - 'for_each_dedup_cand' + - 'for_each_dev_addr' + - 'for_each_dev_scope' + - 'for_each_dma_cap_mask' + - 'for_each_dpcm_be' + - 'for_each_dpcm_be_rollback' + - 'for_each_dpcm_be_safe' + - 'for_each_dpcm_fe' + - 'for_each_drhd_unit' + - 'for_each_dss_dev' + - 'for_each_efi_memory_desc' + - 'for_each_efi_memory_desc_in_map' + - 'for_each_element' + - 'for_each_element_extid' + - 'for_each_element_id' + - 'for_each_endpoint_of_node' + - 'for_each_event' + - 'for_each_event_tps' + - 'for_each_evictable_lru' + - 'for_each_fib6_node_rt_rcu' + - 'for_each_fib6_walker_rt' + - 'for_each_free_mem_pfn_range_in_zone' + - 'for_each_free_mem_pfn_range_in_zone_from' + - 'for_each_free_mem_range' + - 'for_each_free_mem_range_reverse' + - 'for_each_func_rsrc' + - 'for_each_group_evsel' + - 'for_each_group_member' + - 'for_each_hstate' + - 'for_each_if' + - 'for_each_inject_fn' + - 'for_each_insn' + - 'for_each_insn_prefix' + - 'for_each_intid' + - 'for_each_iommu' + - 'for_each_ip_tunnel_rcu' + - 'for_each_irq_nr' + - 'for_each_lang' + - 'for_each_link_codecs' + - 'for_each_link_cpus' + - 'for_each_link_platforms' + - 'for_each_lru' + - 'for_each_matching_node' + - 'for_each_matching_node_and_match' + - 'for_each_mem_pfn_range' + - 'for_each_mem_range' + - 'for_each_mem_range_rev' + - 'for_each_mem_region' + - 'for_each_member' + - 'for_each_memory' + - 'for_each_migratetype_order' + - 'for_each_missing_reg' + - 'for_each_net' + - 'for_each_net_continue_reverse' + - 'for_each_net_rcu' + - 'for_each_netdev' + - 'for_each_netdev_continue' + - 'for_each_netdev_continue_rcu' + - 'for_each_netdev_continue_reverse' + - 'for_each_netdev_feature' + - 'for_each_netdev_in_bond_rcu' + - 'for_each_netdev_rcu' + - 'for_each_netdev_reverse' + - 'for_each_netdev_safe' + - 'for_each_new_connector_in_state' + - 'for_each_new_crtc_in_state' + - 'for_each_new_mst_mgr_in_state' + - 'for_each_new_plane_in_state' + - 'for_each_new_plane_in_state_reverse' + - 'for_each_new_private_obj_in_state' + - 'for_each_new_reg' + - 'for_each_node' + - 'for_each_node_by_name' + - 'for_each_node_by_type' + - 'for_each_node_mask' + - 'for_each_node_state' + - 'for_each_node_with_cpus' + - 'for_each_node_with_property' + - 'for_each_nonreserved_multicast_dest_pgid' + - 'for_each_of_allnodes' + - 'for_each_of_allnodes_from' + - 'for_each_of_cpu_node' + - 'for_each_of_pci_range' + - 'for_each_old_connector_in_state' + - 'for_each_old_crtc_in_state' + - 'for_each_old_mst_mgr_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' + - 'for_each_oldnew_connector_in_state' + - 'for_each_oldnew_crtc_in_state' + - 'for_each_oldnew_mst_mgr_in_state' + - 'for_each_oldnew_plane_in_state' + - 'for_each_oldnew_plane_in_state_reverse' + - 'for_each_oldnew_private_obj_in_state' + - 'for_each_online_cpu' + - 'for_each_online_node' + - 'for_each_online_pgdat' + - 'for_each_path' + - 'for_each_pci_bridge' + - 'for_each_pci_dev' + - 'for_each_pcm_streams' + - 'for_each_physmem_range' + - 'for_each_populated_zone' + - 'for_each_possible_cpu' + - 'for_each_present_cpu' + - 'for_each_prime_number' + - 'for_each_prime_number_from' + - 'for_each_probe_cache_entry' + - 'for_each_process' + - 'for_each_process_thread' + - 'for_each_prop_codec_conf' + - 'for_each_prop_dai_codec' + - 'for_each_prop_dai_cpu' + - 'for_each_prop_dlc_codecs' + - 'for_each_prop_dlc_cpus' + - 'for_each_prop_dlc_platforms' + - 'for_each_property_of_node' + - 'for_each_reg' + - 'for_each_reg_filtered' + - 'for_each_registered_fb' + - 'for_each_requested_gpio' + - 'for_each_requested_gpio_in_range' + - 'for_each_reserved_mem_range' + - 'for_each_reserved_mem_region' + - 'for_each_rtd_codec_dais' + - 'for_each_rtd_components' + - 'for_each_rtd_cpu_dais' + - 'for_each_rtd_dais' + - 'for_each_script' + - 'for_each_sec' + - 'for_each_set_bit' + - 'for_each_set_bit_from' + - 'for_each_set_bitrange' + - 'for_each_set_bitrange_from' + - 'for_each_set_clump8' + - 'for_each_sg' + - 'for_each_sg_dma_page' + - 'for_each_sg_page' + - 'for_each_sgtable_dma_page' + - 'for_each_sgtable_dma_sg' + - 'for_each_sgtable_page' + - 'for_each_sgtable_sg' + - 'for_each_shell_test' + - 'for_each_sibling_event' + - 'for_each_subelement' + - 'for_each_subelement_extid' + - 'for_each_subelement_id' + - 'for_each_sublist' + - 'for_each_subsystem' + - 'for_each_supported_activate_fn' + - 'for_each_supported_inject_fn' + - 'for_each_test' + - 'for_each_thread' + - 'for_each_token' + - 'for_each_unicast_dest_pgid' + - 'for_each_vsi' + - 'for_each_wakeup_source' + - 'for_each_zone' + - 'for_each_zone_zonelist' + - 'for_each_zone_zonelist_nodemask' + - 'func_for_each_insn' + - 'fwnode_for_each_available_child_node' + - 'fwnode_for_each_child_node' + - 'fwnode_graph_for_each_endpoint' + - 'gadget_for_each_ep' + - 'genradix_for_each' + - 'genradix_for_each_from' + - 'hash_for_each' + - 'hash_for_each_possible' + - 'hash_for_each_possible_rcu' + - 'hash_for_each_possible_rcu_notrace' + - 'hash_for_each_possible_safe' + - 'hash_for_each_rcu' + - 'hash_for_each_safe' + - 'hashmap__for_each_entry' + - 'hashmap__for_each_entry_safe' + - 'hashmap__for_each_key_entry' + - 'hashmap__for_each_key_entry_safe' + - 'hctx_for_each_ctx' + - 'hists__for_each_format' + - 'hists__for_each_sort_list' + - 'hlist_bl_for_each_entry' + - 'hlist_bl_for_each_entry_rcu' + - 'hlist_bl_for_each_entry_safe' + - 'hlist_for_each' + - 'hlist_for_each_entry' + - 'hlist_for_each_entry_continue' + - 'hlist_for_each_entry_continue_rcu' + - 'hlist_for_each_entry_continue_rcu_bh' + - 'hlist_for_each_entry_from' + - 'hlist_for_each_entry_from_rcu' + - 'hlist_for_each_entry_rcu' + - 'hlist_for_each_entry_rcu_bh' + - 'hlist_for_each_entry_rcu_notrace' + - 'hlist_for_each_entry_safe' + - 'hlist_for_each_entry_srcu' + - 'hlist_for_each_safe' + - 'hlist_nulls_for_each_entry' + - 'hlist_nulls_for_each_entry_from' + - 'hlist_nulls_for_each_entry_rcu' + - 'hlist_nulls_for_each_entry_safe' + - 'i3c_bus_for_each_i2cdev' + - 'i3c_bus_for_each_i3cdev' + - 'idr_for_each_entry' + - 'idr_for_each_entry_continue' + - 'idr_for_each_entry_continue_ul' + - 'idr_for_each_entry_ul' + - 'in_dev_for_each_ifa_rcu' + - 'in_dev_for_each_ifa_rtnl' + - 'inet_bind_bucket_for_each' + - 'inet_lhash2_for_each_icsk' + - 'inet_lhash2_for_each_icsk_continue' + - 'inet_lhash2_for_each_icsk_rcu' + - 'intlist__for_each_entry' + - 'intlist__for_each_entry_safe' + - 'kcore_copy__for_each_phdr' + - 'key_for_each' + - 'key_for_each_safe' + - 'klp_for_each_func' + - 'klp_for_each_func_safe' + - 'klp_for_each_func_static' + - 'klp_for_each_object' + - 'klp_for_each_object_safe' + - 'klp_for_each_object_static' + - 'kunit_suite_for_each_test_case' + - 'kvm_for_each_memslot' + - 'kvm_for_each_memslot_in_gfn_range' + - 'kvm_for_each_vcpu' + - 'libbpf_nla_for_each_attr' + - 'list_for_each' + - 'list_for_each_codec' + - 'list_for_each_codec_safe' + - 'list_for_each_continue' + - 'list_for_each_entry' + - 'list_for_each_entry_continue' + - 'list_for_each_entry_continue_rcu' + - 'list_for_each_entry_continue_reverse' + - 'list_for_each_entry_from' + - 'list_for_each_entry_from_rcu' + - 'list_for_each_entry_from_reverse' + - 'list_for_each_entry_lockless' + - 'list_for_each_entry_rcu' + - 'list_for_each_entry_reverse' + - 'list_for_each_entry_safe' + - 'list_for_each_entry_safe_continue' + - 'list_for_each_entry_safe_from' + - 'list_for_each_entry_safe_reverse' + - 'list_for_each_entry_srcu' + - 'list_for_each_from' + - 'list_for_each_prev' + - 'list_for_each_prev_safe' + - 'list_for_each_safe' + - 'llist_for_each' + - 'llist_for_each_entry' + - 'llist_for_each_entry_safe' + - 'llist_for_each_safe' + - 'map__for_each_symbol' + - 'map__for_each_symbol_by_name' + - 'map_for_each_event' + - 'map_for_each_metric' + - 'maps__for_each_entry' + - 'maps__for_each_entry_safe' + - 'mci_for_each_dimm' + - 'media_device_for_each_entity' + - 'media_device_for_each_intf' + - 'media_device_for_each_link' + - 'media_device_for_each_pad' + - 'msi_for_each_desc' + - 'nanddev_io_for_each_page' + - 'netdev_for_each_lower_dev' + - 'netdev_for_each_lower_private' + - 'netdev_for_each_lower_private_rcu' + - 'netdev_for_each_mc_addr' + - 'netdev_for_each_uc_addr' + - 'netdev_for_each_upper_dev_rcu' + - 'netdev_hw_addr_list_for_each' + - 'nft_rule_for_each_expr' + - 'nla_for_each_attr' + - 'nla_for_each_nested' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + - 'nr_neigh_for_each' + - 'nr_neigh_for_each_safe' + - 'nr_node_for_each' + - 'nr_node_for_each_safe' + - 'of_for_each_phandle' + - 'of_property_for_each_string' + - 'of_property_for_each_u32' + - 'pci_bus_for_each_resource' + - 'pci_doe_for_each_off' + - 'pcl_for_each_chunk' + - 'pcl_for_each_segment' + - 'pcm_for_each_format' + - 'perf_config_items__for_each_entry' + - 'perf_config_sections__for_each_entry' + - 'perf_config_set__for_each_entry' + - 'perf_cpu_map__for_each_cpu' + - 'perf_evlist__for_each_entry' + - 'perf_evlist__for_each_entry_reverse' + - 'perf_evlist__for_each_entry_safe' + - 'perf_evlist__for_each_evsel' + - 'perf_evlist__for_each_mmap' + - 'perf_hpp_list__for_each_format' + - 'perf_hpp_list__for_each_format_safe' + - 'perf_hpp_list__for_each_sort_list' + - 'perf_hpp_list__for_each_sort_list_safe' + - 'perf_pmu__for_each_hybrid_pmu' + - 'ping_portaddr_for_each_entry' + - 'ping_portaddr_for_each_entry_rcu' + - 'plist_for_each' + - 'plist_for_each_continue' + - 'plist_for_each_entry' + - 'plist_for_each_entry_continue' + - 'plist_for_each_entry_safe' + - 'plist_for_each_safe' + - 'pnp_for_each_card' + - 'pnp_for_each_dev' + - 'protocol_for_each_card' + - 'protocol_for_each_dev' + - 'queue_for_each_hw_ctx' + - 'radix_tree_for_each_slot' + - 'radix_tree_for_each_tagged' + - 'rb_for_each' + - 'rbtree_postorder_for_each_entry_safe' + - 'rdma_for_each_block' + - 'rdma_for_each_port' + - 'rdma_umem_for_each_dma_block' + - 'resort_rb__for_each_entry' + - 'resource_list_for_each_entry' + - 'resource_list_for_each_entry_safe' + - 'rhl_for_each_entry_rcu' + - 'rhl_for_each_rcu' + - 'rht_for_each' + - 'rht_for_each_entry' + - 'rht_for_each_entry_from' + - 'rht_for_each_entry_rcu' + - 'rht_for_each_entry_rcu_from' + - 'rht_for_each_entry_safe' + - 'rht_for_each_from' + - 'rht_for_each_rcu' + - 'rht_for_each_rcu_from' + - 'rq_for_each_bvec' + - 'rq_for_each_segment' + - 'rq_list_for_each' + - 'rq_list_for_each_safe' + - 'scsi_for_each_prot_sg' + - 'scsi_for_each_sg' + - 'sctp_for_each_hentry' + - 'sctp_skb_for_each' + - 'sec_for_each_insn' + - 'sec_for_each_insn_continue' + - 'sec_for_each_insn_from' + - 'shdma_for_each_chan' + - 'shost_for_each_device' + - 'sk_for_each' + - 'sk_for_each_bound' + - 'sk_for_each_entry_offset_rcu' + - 'sk_for_each_from' + - 'sk_for_each_rcu' + - 'sk_for_each_safe' + - 'sk_nulls_for_each' + - 'sk_nulls_for_each_from' + - 'sk_nulls_for_each_rcu' + - 'snd_array_for_each' + - 'snd_pcm_group_for_each_entry' + - 'snd_soc_dapm_widget_for_each_path' + - 'snd_soc_dapm_widget_for_each_path_safe' + - 'snd_soc_dapm_widget_for_each_sink_path' + - 'snd_soc_dapm_widget_for_each_source_path' + - 'strlist__for_each_entry' + - 'strlist__for_each_entry_safe' + - 'sym_for_each_insn' + - 'sym_for_each_insn_continue_reverse' + - 'symbols__for_each_entry' + - 'tb_property_for_each' + - 'tcf_act_for_each_action' + - 'tcf_exts_for_each_action' + - 'udp_portaddr_for_each_entry' + - 'udp_portaddr_for_each_entry_rcu' + - 'usb_hub_for_each_child' + - 'v4l2_device_for_each_subdev' + - 'v4l2_m2m_for_each_dst_buf' + - 'v4l2_m2m_for_each_dst_buf_safe' + - 'v4l2_m2m_for_each_src_buf' + - 'v4l2_m2m_for_each_src_buf_safe' + - 'virtio_device_for_each_vq' + - 'while_for_each_ftrace_op' + - 'xa_for_each' + - 'xa_for_each_marked' + - 'xa_for_each_range' + - 'xa_for_each_start' + - 'xas_for_each' + - 'xas_for_each_conflict' + - 'xas_for_each_marked' + - 'xbc_array_for_each_value' + - 'xbc_for_each_key_value' + - 'xbc_node_for_each_array_value' + - 'xbc_node_for_each_child' + - 'xbc_node_for_each_key_value' + - 'xbc_node_for_each_subkey' + - 'zorro_for_each_dev' + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/kmod/vpnhide_kmod.c b/kmod/vpnhide_kmod.c index ad469a0..d4bfb2d 100644 --- a/kmod/vpnhide_kmod.c +++ b/kmod/vpnhide_kmod.c @@ -43,7 +43,7 @@ /* VPN interface name matching */ /* ------------------------------------------------------------------ */ -static const char * const vpn_prefixes[] = { +static const char *const vpn_prefixes[] = { "tun", "ppp", "tap", "wg", "ipsec", "xfrm", "utun", "l2tp", "gre", }; @@ -55,8 +55,8 @@ static bool is_vpn_ifname(const char *name) return false; for (i = 0; i < ARRAY_SIZE(vpn_prefixes); i++) { - if (strncmp(name, vpn_prefixes[i], - strlen(vpn_prefixes[i])) == 0) + if (strncmp(name, vpn_prefixes[i], strlen(vpn_prefixes[i])) == + 0) return true; } if (strstr(name, "vpn") || strstr(name, "VPN")) @@ -158,10 +158,10 @@ static int targets_open(struct inode *inode, struct file *file) } static const struct proc_ops targets_proc_ops = { - .proc_open = targets_open, - .proc_read = seq_read, - .proc_write = targets_write, - .proc_lseek = seq_lseek, + .proc_open = targets_open, + .proc_read = seq_read, + .proc_write = targets_write, + .proc_lseek = seq_lseek, .proc_release = single_release, }; @@ -183,8 +183,7 @@ struct dev_ioctl_data { struct ifreq *kifr; /* kernel pointer, saved from x2 */ }; -static int dev_ioctl_entry(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int dev_ioctl_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { struct dev_ioctl_data *data = (void *)ri->data; @@ -197,8 +196,7 @@ static int dev_ioctl_entry(struct kretprobe_instance *ri, return 0; } -static int dev_ioctl_ret(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int dev_ioctl_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { struct dev_ioctl_data *data = (void *)ri->data; char name[IFNAMSIZ]; @@ -227,11 +225,11 @@ static int dev_ioctl_ret(struct kretprobe_instance *ri, } static struct kretprobe dev_ioctl_krp = { - .handler = dev_ioctl_ret, - .entry_handler = dev_ioctl_entry, - .data_size = sizeof(struct dev_ioctl_data), - .maxactive = 20, - .kp.symbol_name = "dev_ioctl", + .handler = dev_ioctl_ret, + .entry_handler = dev_ioctl_entry, + .data_size = sizeof(struct dev_ioctl_data), + .maxactive = 20, + .kp.symbol_name = "dev_ioctl", }; /* ================================================================== */ @@ -249,8 +247,7 @@ struct dev_ifconf_data { bool target; }; -static int dev_ifconf_entry(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int dev_ifconf_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { struct dev_ifconf_data *data = (void *)ri->data; @@ -259,8 +256,7 @@ static int dev_ifconf_entry(struct kretprobe_instance *ri, return 0; } -static int dev_ifconf_ret(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int dev_ifconf_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { struct dev_ifconf_data *data = (void *)ri->data; struct ifconf ifc; @@ -304,11 +300,11 @@ static int dev_ifconf_ret(struct kretprobe_instance *ri, } static struct kretprobe dev_ifconf_krp = { - .handler = dev_ifconf_ret, - .entry_handler = dev_ifconf_entry, - .data_size = sizeof(struct dev_ifconf_data), - .maxactive = 20, - .kp.symbol_name = "dev_ifconf", + .handler = dev_ifconf_ret, + .entry_handler = dev_ifconf_entry, + .data_size = sizeof(struct dev_ifconf_data), + .maxactive = 20, + .kp.symbol_name = "dev_ifconf", }; /* ================================================================== */ @@ -325,8 +321,7 @@ struct rtnl_fill_data { bool should_filter; }; -static int rtnl_fill_entry(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int rtnl_fill_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { struct rtnl_fill_data *data = (void *)ri->data; struct net_device *dev; @@ -351,8 +346,7 @@ static int rtnl_fill_entry(struct kretprobe_instance *ri, return 0; } -static int rtnl_fill_ret(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int rtnl_fill_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { struct rtnl_fill_data *data = (void *)ri->data; @@ -363,11 +357,11 @@ static int rtnl_fill_ret(struct kretprobe_instance *ri, } static struct kretprobe rtnl_fill_krp = { - .handler = rtnl_fill_ret, - .entry_handler = rtnl_fill_entry, - .data_size = sizeof(struct rtnl_fill_data), - .maxactive = 20, - .kp.symbol_name = "rtnl_fill_ifinfo", + .handler = rtnl_fill_ret, + .entry_handler = rtnl_fill_entry, + .data_size = sizeof(struct rtnl_fill_data), + .maxactive = 20, + .kp.symbol_name = "rtnl_fill_ifinfo", }; /* ================================================================== */ @@ -393,8 +387,7 @@ struct inet6_fill_data { bool should_filter; }; -static int inet6_fill_entry(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int inet6_fill_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { struct inet6_fill_data *data = (void *)ri->data; struct inet6_ifaddr *ifa; @@ -422,8 +415,7 @@ static int inet6_fill_entry(struct kretprobe_instance *ri, return 0; } -static int inet6_fill_ret(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int inet6_fill_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { struct inet6_fill_data *data = (void *)ri->data; @@ -437,11 +429,11 @@ static int inet6_fill_ret(struct kretprobe_instance *ri, } static struct kretprobe inet6_fill_krp = { - .handler = inet6_fill_ret, - .entry_handler = inet6_fill_entry, - .data_size = sizeof(struct inet6_fill_data), - .maxactive = 20, - .kp.symbol_name = "inet6_fill_ifaddr", + .handler = inet6_fill_ret, + .entry_handler = inet6_fill_entry, + .data_size = sizeof(struct inet6_fill_data), + .maxactive = 20, + .kp.symbol_name = "inet6_fill_ifaddr", }; /* ================================================================== */ @@ -459,8 +451,7 @@ struct inet_fill_data { bool should_filter; }; -static int inet_fill_entry(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int inet_fill_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { struct inet_fill_data *data = (void *)ri->data; struct in_ifaddr *ifa; @@ -484,8 +475,7 @@ static int inet_fill_entry(struct kretprobe_instance *ri, return 0; } -static int inet_fill_ret(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int inet_fill_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { struct inet_fill_data *data = (void *)ri->data; @@ -498,11 +488,11 @@ static int inet_fill_ret(struct kretprobe_instance *ri, } static struct kretprobe inet_fill_krp = { - .handler = inet_fill_ret, - .entry_handler = inet_fill_entry, - .data_size = sizeof(struct inet_fill_data), - .maxactive = 20, - .kp.symbol_name = "inet_fill_ifaddr", + .handler = inet_fill_ret, + .entry_handler = inet_fill_entry, + .data_size = sizeof(struct inet_fill_data), + .maxactive = 20, + .kp.symbol_name = "inet_fill_ifaddr", }; /* ================================================================== */ @@ -522,8 +512,7 @@ struct fib_route_data { bool target; }; -static int fib_route_entry(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int fib_route_entry(struct kretprobe_instance *ri, struct pt_regs *regs) { struct fib_route_data *data = (void *)ri->data; @@ -549,8 +538,7 @@ static int fib_route_entry(struct kretprobe_instance *ri, * synchronously under its own fd context — no concurrent access to * the same seq_file is possible between our entry and return handlers. */ -static int fib_route_ret(struct kretprobe_instance *ri, - struct pt_regs *regs) +static int fib_route_ret(struct kretprobe_instance *ri, struct pt_regs *regs) { struct fib_route_data *data = (void *)ri->data; struct seq_file *seq = data->seq; @@ -583,7 +571,8 @@ static int fib_route_ret(struct kretprobe_instance *ri, /* Extract the interface name (first field, tab-delimited) */ for (j = 0; j < IFNAMSIZ - 1 && j < (int)line_len && - src[j] != '\t' && src[j] != '\n'; j++) + src[j] != '\t' && src[j] != '\n'; + j++) ifname[j] = src[j]; ifname[j] = '\0'; @@ -605,11 +594,11 @@ static int fib_route_ret(struct kretprobe_instance *ri, } static struct kretprobe fib_route_krp = { - .handler = fib_route_ret, - .entry_handler = fib_route_entry, - .data_size = sizeof(struct fib_route_data), - .maxactive = 20, - .kp.symbol_name = "fib_route_seq_show", + .handler = fib_route_ret, + .entry_handler = fib_route_entry, + .data_size = sizeof(struct fib_route_data), + .maxactive = 20, + .kp.symbol_name = "fib_route_seq_show", }; /* ================================================================== */ @@ -625,12 +614,12 @@ struct kretprobe_reg { }; static struct kretprobe_reg probes[] = { - { &dev_ioctl_krp, "dev_ioctl", false }, - { &dev_ifconf_krp, "dev_ifconf", false }, - { &rtnl_fill_krp, "rtnl_fill_ifinfo", false }, - { &inet6_fill_krp, "inet6_fill_ifaddr", false }, - { &inet_fill_krp, "inet_fill_ifaddr", false }, - { &fib_route_krp, "fib_route_seq_show", false }, + { &dev_ioctl_krp, "dev_ioctl", false }, + { &dev_ifconf_krp, "dev_ifconf", false }, + { &rtnl_fill_krp, "rtnl_fill_ifinfo", false }, + { &inet6_fill_krp, "inet6_fill_ifaddr", false }, + { &inet_fill_krp, "inet_fill_ifaddr", false }, + { &fib_route_krp, "fib_route_seq_show", false }, }; static int __init vpnhide_init(void) @@ -656,13 +645,13 @@ static int __init vpnhide_init(void) } if (ok < ARRAY_SIZE(probes)) pr_warn(MODNAME ": only %d/%zu kretprobes registered — " - "some detection paths are not covered\n", + "some detection paths are not covered\n", ok, ARRAY_SIZE(probes)); /* 0600: root-only read/write. UIDs are written here by service.sh * and WebUI (both root). Apps must not see the target list. */ - targets_entry = proc_create("vpnhide_targets", 0600, NULL, - &targets_proc_ops); + targets_entry = + proc_create("vpnhide_targets", 0600, NULL, &targets_proc_ops); pr_info(MODNAME ": loaded — write UIDs to /proc/vpnhide_targets\n"); return 0; @@ -679,7 +668,7 @@ static void __exit vpnhide_exit(void) if (probes[i].registered) { unregister_kretprobe(probes[i].krp); pr_info(MODNAME ": kretprobe(%s) unregistered " - "(missed %d)\n", + "(missed %d)\n", probes[i].name, probes[i].krp->nmissed); } } diff --git a/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/HookEntry.kt b/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/HookEntry.kt index d14f419..beb4929 100644 --- a/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/HookEntry.kt +++ b/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/HookEntry.kt @@ -34,16 +34,16 @@ import java.util.concurrent.atomic.AtomicBoolean * Only "System Framework" needs to be in LSPosed scope. */ class HookEntry : IXposedHookLoadPackage { - private val hookInstalled = AtomicBoolean(false) override fun handleLoadPackage(lpparam: XC_LoadPackage.LoadPackageParam) { // Only hook system_server. handleLoadPackage fires multiple times // in system_server (once per hosted package / APEX), so we use // compareAndSet to install hooks exactly once. - val inSystemServer = hookInstalled.get() || - lpparam.processName == "android" || - android.os.Process.myUid() == 1000 + val inSystemServer = + hookInstalled.get() || + lpparam.processName == "android" || + android.os.Process.myUid() == 1000 if (!inSystemServer) return @@ -53,7 +53,10 @@ class HookEntry : IXposedHookLoadPackage { } } - private inline fun tryHook(name: String, block: () -> Unit) { + private inline fun tryHook( + name: String, + block: () -> Unit, + ) { try { block() } catch (t: Throwable) { @@ -85,6 +88,7 @@ class HookEntry : IXposedHookLoadPackage { // ================================================================== @Volatile private var systemServerTargetUids: Set? = null + @Volatile private var targetUidsFileObserver: android.os.FileObserver? = null private val uidLock = Any() @@ -135,7 +139,7 @@ class HookEntry : IXposedHookLoadPackage { tryHook("NC.writeToParcel") { hookNCWriteToParcel() } tryHook("NI.writeToParcel") { hookNIWriteToParcel() } tryHook("LP.writeToParcel") { hookLPWriteToParcel() } - tryHook("FileObserver") { watchTargetUidsFile() } + tryHook("FileObserver") { watchTargetUidsFile() } } /** @@ -146,17 +150,21 @@ class HookEntry : IXposedHookLoadPackage { private fun watchTargetUidsFile() { val dir = "/data/system" val filename = "vpnhide_uids.txt" - val observer = object : android.os.FileObserver( - File(dir), - CREATE or CLOSE_WRITE or MOVED_TO or MODIFY - ) { - override fun onEvent(event: Int, path: String?) { - if (path == filename) { - XposedBridge.log("VpnHide: $filename changed (event=$event), invalidating UID cache") - systemServerTargetUids = null + val observer = + object : android.os.FileObserver( + File(dir), + CREATE or CLOSE_WRITE or MOVED_TO or MODIFY, + ) { + override fun onEvent( + event: Int, + path: String?, + ) { + if (path == filename) { + XposedBridge.log("VpnHide: $filename changed (event=$event), invalidating UID cache") + systemServerTargetUids = null + } } } - } targetUidsFileObserver = observer observer.startWatching() XposedBridge.log("VpnHide: watching $dir for $filename changes (inotify)") @@ -170,8 +178,10 @@ class HookEntry : IXposedHookLoadPackage { */ private fun hookNCWriteToParcel() { XposedHelpers.findAndHookMethod( - NetworkCapabilities::class.java, "writeToParcel", - android.os.Parcel::class.java, Integer.TYPE, + NetworkCapabilities::class.java, + "writeToParcel", + android.os.Parcel::class.java, + Integer.TYPE, object : XC_MethodHook() { private val savedTransport = ThreadLocal() private val savedCaps = ThreadLocal() @@ -188,7 +198,12 @@ class HookEntry : IXposedHookLoadPackage { // Read all original values before mutating anything val caps = XposedHelpers.getLongField(nc, "mNetworkCapabilities") - val ti = try { XposedHelpers.getObjectField(nc, "mTransportInfo") } catch (_: Throwable) { null } + val ti = + try { + XposedHelpers.getObjectField(nc, "mTransportInfo") + } catch (_: Throwable) { + null + } // Mutate — if any step fails, restore everything try { @@ -226,9 +241,10 @@ class HookEntry : IXposedHookLoadPackage { XposedHelpers.setLongField(nc, "mTransportTypes", origTransport) if (origCaps != null) XposedHelpers.setLongField(nc, "mNetworkCapabilities", origCaps) XposedHelpers.setObjectField(nc, "mTransportInfo", origTi) - } catch (_: Throwable) {} + } catch (_: Throwable) { + } } - } + }, ) XposedBridge.log("VpnHide: hooked NetworkCapabilities.writeToParcel") } @@ -241,8 +257,10 @@ class HookEntry : IXposedHookLoadPackage { */ private fun hookNIWriteToParcel() { XposedHelpers.findAndHookMethod( - NetworkInfo::class.java, "writeToParcel", - android.os.Parcel::class.java, Integer.TYPE, + NetworkInfo::class.java, + "writeToParcel", + android.os.Parcel::class.java, + Integer.TYPE, object : XC_MethodHook() { private val savedType = ThreadLocal() @@ -265,9 +283,10 @@ class HookEntry : IXposedHookLoadPackage { savedType.remove() try { XposedHelpers.setIntField(param.thisObject, "mNetworkType", origType) - } catch (_: Throwable) {} + } catch (_: Throwable) { + } } - } + }, ) XposedBridge.log("VpnHide: hooked NetworkInfo.writeToParcel") } @@ -280,8 +299,10 @@ class HookEntry : IXposedHookLoadPackage { */ private fun hookLPWriteToParcel() { XposedHelpers.findAndHookMethod( - LinkProperties::class.java, "writeToParcel", - android.os.Parcel::class.java, Integer.TYPE, + LinkProperties::class.java, + "writeToParcel", + android.os.Parcel::class.java, + Integer.TYPE, object : XC_MethodHook() { private val savedIfname = ThreadLocal() @@ -304,9 +325,10 @@ class HookEntry : IXposedHookLoadPackage { savedIfname.remove() try { XposedHelpers.setObjectField(param.thisObject, "mIfaceName", origIfname) - } catch (_: Throwable) {} + } catch (_: Throwable) { + } } - } + }, ) XposedBridge.log("VpnHide: hooked LinkProperties.writeToParcel") } diff --git a/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/MainActivity.kt b/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/MainActivity.kt index 0e015d8..eaa60f2 100644 --- a/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/MainActivity.kt +++ b/lsposed/app/src/main/kotlin/dev/okhsunrog/vpnhide/MainActivity.kt @@ -54,8 +54,8 @@ class MainActivity : ComponentActivity() { } } -private fun suExec(cmd: String): Pair { - return try { +private fun suExec(cmd: String): Pair = + try { val proc = Runtime.getRuntime().exec(arrayOf("su", "-c", cmd)) try { // Drain stderr in the background to prevent the process from @@ -73,21 +73,20 @@ private fun suExec(cmd: String): Pair { Log.e(TAG, "su exec failed: ${e.message}") -1 to "" } -} -private suspend fun suExecAsync(cmd: String): Pair = - withContext(Dispatchers.IO) { suExec(cmd) } +private suspend fun suExecAsync(cmd: String): Pair = withContext(Dispatchers.IO) { suExec(cmd) } @OptIn(ExperimentalMaterial3Api::class) @Composable fun VpnHideApp() { val darkTheme = isSystemInDarkTheme() - val colorScheme = if (android.os.Build.VERSION.SDK_INT >= 31) { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } else { - if (darkTheme) darkColorScheme() else lightColorScheme() - } + val colorScheme = + if (android.os.Build.VERSION.SDK_INT >= 31) { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } else { + if (darkTheme) darkColorScheme() else lightColorScheme() + } MaterialTheme(colorScheme = colorScheme) { val context = LocalContext.current @@ -111,44 +110,56 @@ fun VpnHideApp() { LaunchedEffect(Unit) { withContext(Dispatchers.IO) { - val (_, targetsRaw) = suExec( - "cat $KMOD_TARGETS 2>/dev/null || cat $ZYGISK_TARGETS 2>/dev/null || true" - ) - val selected = targetsRaw.lines() - .map { it.trim() } - .filter { it.isNotEmpty() && !it.startsWith("#") } - .toSet() + val (_, targetsRaw) = + suExec( + "cat $KMOD_TARGETS 2>/dev/null || cat $ZYGISK_TARGETS 2>/dev/null || true", + ) + val selected = + targetsRaw + .lines() + .map { it.trim() } + .filter { it.isNotEmpty() && !it.startsWith("#") } + .toSet() val installedApps = pm.getInstalledApplications(PackageManager.GET_META_DATA) - val entries = installedApps.map { info -> - val label = try { - pm.getApplicationLabel(info).toString() - } catch (_: Exception) { info.packageName } - val icon = try { - pm.getApplicationIcon(info) - } catch (_: Exception) { null } - val isSystem = (info.flags and ApplicationInfo.FLAG_SYSTEM) != 0 - AppEntry( - packageName = info.packageName, - label = label, - icon = icon, - isSystem = isSystem, - selected = info.packageName in selected, - ) - }.sortedWith(compareByDescending { it.selected }.thenBy { it.label.lowercase() }) + val entries = + installedApps + .map { info -> + val label = + try { + pm.getApplicationLabel(info).toString() + } catch (_: Exception) { + info.packageName + } + val icon = + try { + pm.getApplicationIcon(info) + } catch (_: Exception) { + null + } + val isSystem = (info.flags and ApplicationInfo.FLAG_SYSTEM) != 0 + AppEntry( + packageName = info.packageName, + label = label, + icon = icon, + isSystem = isSystem, + selected = info.packageName in selected, + ) + }.sortedWith(compareByDescending { it.selected }.thenBy { it.label.lowercase() }) allApps = entries loading = false } } - val filteredApps = remember(allApps, searchQuery, showSystem) { - val q = searchQuery.trim().lowercase() - allApps.filter { app -> - (showSystem || !app.isSystem || app.selected) && - (q.isEmpty() || app.label.lowercase().contains(q) || app.packageName.lowercase().contains(q)) + val filteredApps = + remember(allApps, searchQuery, showSystem) { + val q = searchQuery.trim().lowercase() + allApps.filter { app -> + (showSystem || !app.isSystem || app.selected) && + (q.isEmpty() || app.label.lowercase().contains(q) || app.packageName.lowercase().contains(q)) + } } - } val selectedCount = remember(allApps) { allApps.count { it.selected } } @@ -157,19 +168,21 @@ fun VpnHideApp() { topBar = { TopAppBar( title = { Text("VPN Hide") }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, - ) + colors = + TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ), ) }, bottomBar = { Surface(tonalElevation = 3.dp) { Row( - modifier = Modifier - .fillMaxWidth() - .navigationBarsPadding() - .padding(horizontal = 16.dp, vertical = 12.dp), + modifier = + Modifier + .fillMaxWidth() + .navigationBarsPadding() + .padding(horizontal = 16.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically, ) { Text( @@ -189,18 +202,20 @@ fun VpnHideApp() { } } } - } + }, ) { innerPadding -> Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) + modifier = + Modifier + .fillMaxSize() + .padding(innerPadding), ) { // Search + system toggle Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { OutlinedTextField( @@ -231,11 +246,12 @@ fun VpnHideApp() { AppRow( app = app, onToggle = { - allApps = allApps.map { - if (it.packageName == app.packageName) it.copy(selected = !it.selected) else it - } + allApps = + allApps.map { + if (it.packageName == app.packageName) it.copy(selected = !it.selected) else it + } dirty = true - } + }, ) } } @@ -247,9 +263,10 @@ fun VpnHideApp() { if (saving) { LaunchedEffect(Unit) { val selected = allApps.filter { it.selected }.map { it.packageName }.sorted() - val body = "# Managed by VPN Hide app\n" + - selected.joinToString("\n") + - if (selected.isNotEmpty()) "\n" else "" + val body = + "# Managed by VPN Hide app\n" + + selected.joinToString("\n") + + if (selected.isNotEmpty()) "\n" else "" try { val (exitCode, _) = suExecAsync(buildSaveCommand(body, selected)) @@ -269,7 +286,10 @@ fun VpnHideApp() { } } -private fun buildSaveCommand(body: String, selectedPackages: List): String { +private fun buildSaveCommand( + body: String, + selectedPackages: List, +): String { val b64 = android.util.Base64.encodeToString(body.toByteArray(), android.util.Base64.NO_WRAP) val parts = mutableListOf() @@ -287,21 +307,22 @@ private fun buildSaveCommand(body: String, selectedPackages: List): Stri // Uses the same approach as kmod/service.sh — real newlines in $UIDS via heredoc-style // accumulation, not printf \n escapes. if (selectedPackages.isNotEmpty()) { - val uidResolution = buildString { - append("ALL_PKGS=\"\$(pm list packages -U 2>/dev/null)\"") - append("; UIDS=\"\"") - for (pkg in selectedPackages) { - append("; U=\$(echo \"\$ALL_PKGS\" | grep '^package:$pkg ' | sed 's/.*uid://')") - append("; if [ -n \"\$U\" ]; then if [ -z \"\$UIDS\" ]; then UIDS=\"\$U\"; else UIDS=\"\$UIDS") - // Real newline in the shell string — not \n escape - append("\n") - append("\$U\"; fi; fi") + val uidResolution = + buildString { + append("ALL_PKGS=\"\$(pm list packages -U 2>/dev/null)\"") + append("; UIDS=\"\"") + for (pkg in selectedPackages) { + append("; U=\$(echo \"\$ALL_PKGS\" | grep '^package:$pkg ' | sed 's/.*uid://')") + append("; if [ -n \"\$U\" ]; then if [ -z \"\$UIDS\" ]; then UIDS=\"\$U\"; else UIDS=\"\$UIDS") + // Real newline in the shell string — not \n escape + append("\n") + append("\$U\"; fi; fi") + } + append("; if [ -n \"\$UIDS\" ]; then echo \"\$UIDS\" > $PROC_TARGETS 2>/dev/null; echo \"\$UIDS\" > $SS_UIDS_FILE") + append("; else echo > $PROC_TARGETS 2>/dev/null; echo > $SS_UIDS_FILE; fi") + append("; chmod 644 $SS_UIDS_FILE 2>/dev/null") + append("; chcon u:object_r:system_data_file:s0 $SS_UIDS_FILE 2>/dev/null") } - append("; if [ -n \"\$UIDS\" ]; then echo \"\$UIDS\" > $PROC_TARGETS 2>/dev/null; echo \"\$UIDS\" > $SS_UIDS_FILE") - append("; else echo > $PROC_TARGETS 2>/dev/null; echo > $SS_UIDS_FILE; fi") - append("; chmod 644 $SS_UIDS_FILE 2>/dev/null") - append("; chcon u:object_r:system_data_file:s0 $SS_UIDS_FILE 2>/dev/null") - } parts += uidResolution } else { // No targets — clear the UIDs files. echo -n writes a zero-length @@ -314,12 +335,16 @@ private fun buildSaveCommand(body: String, selectedPackages: List): Stri } @Composable -private fun AppRow(app: AppEntry, onToggle: () -> Unit) { +private fun AppRow( + app: AppEntry, + onToggle: () -> Unit, +) { Row( - modifier = Modifier - .fillMaxWidth() - .clickable(onClick = onToggle) - .padding(horizontal = 16.dp, vertical = 10.dp), + modifier = + Modifier + .fillMaxWidth() + .clickable(onClick = onToggle) + .padding(horizontal = 16.dp, vertical = 10.dp), verticalAlignment = Alignment.CenterVertically, ) { Checkbox( diff --git a/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/MainActivity.kt b/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/MainActivity.kt index b1756c4..df9a753 100644 --- a/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/MainActivity.kt +++ b/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/MainActivity.kt @@ -42,20 +42,21 @@ private val VPN_PREFIXES = listOf("tun", "wg", "ppp", "tap", "ipsec", "xfrm") data class CheckResult( val name: String, - val passed: Boolean?, // null = informational - val detail: String + val passed: Boolean?, // null = informational + val detail: String, ) @OptIn(ExperimentalMaterial3Api::class) @Composable fun VpnHideTestApp() { val darkTheme = isSystemInDarkTheme() - val colorScheme = if (android.os.Build.VERSION.SDK_INT >= 31) { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } else { - if (darkTheme) darkColorScheme() else lightColorScheme() - } + val colorScheme = + if (android.os.Build.VERSION.SDK_INT >= 31) { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } else { + if (darkTheme) darkColorScheme() else lightColorScheme() + } MaterialTheme(colorScheme = colorScheme) { val context = LocalContext.current @@ -75,18 +76,20 @@ fun VpnHideTestApp() { topBar = { TopAppBar( title = { Text("VPNHide Diagnostics") }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, - ) + colors = + TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.primaryContainer, + titleContentColor = MaterialTheme.colorScheme.onPrimaryContainer, + ), ) - } + }, ) { innerPadding -> Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = 16.dp) + modifier = + Modifier + .fillMaxSize() + .padding(innerPadding) + .padding(horizontal = 16.dp), ) { Spacer(Modifier.height(8.dp)) @@ -94,7 +97,7 @@ fun VpnHideTestApp() { text = summary, style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.Bold, - modifier = Modifier.padding(bottom = 8.dp) + modifier = Modifier.padding(bottom = 8.dp), ) Button( @@ -106,7 +109,7 @@ fun VpnHideTestApp() { val passed = scored.count { it.passed == true } summary = "$passed/${scored.size} passed" }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Text("Run All Checks") } @@ -114,10 +117,11 @@ fun VpnHideTestApp() { Spacer(Modifier.height(8.dp)) Column( - modifier = Modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()), - verticalArrangement = Arrangement.spacedBy(6.dp) + modifier = + Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(6.dp), ) { for (r in results) { CheckCard(r) @@ -131,48 +135,58 @@ fun VpnHideTestApp() { @Composable fun CheckCard(r: CheckResult) { - val containerColor = when (r.passed) { - true -> Color(0xFFE8F5E9) - false -> Color(0xFFFFEBEE) - null -> MaterialTheme.colorScheme.surfaceVariant - } - val darkTheme = isSystemInDarkTheme() - val actualColor = if (darkTheme) { + val containerColor = when (r.passed) { - true -> Color(0xFF1B5E20).copy(alpha = 0.3f) - false -> Color(0xFFB71C1C).copy(alpha = 0.3f) + true -> Color(0xFFE8F5E9) + false -> Color(0xFFFFEBEE) null -> MaterialTheme.colorScheme.surfaceVariant } - } else containerColor + val darkTheme = isSystemInDarkTheme() + val actualColor = + if (darkTheme) { + when (r.passed) { + true -> Color(0xFF1B5E20).copy(alpha = 0.3f) + false -> Color(0xFFB71C1C).copy(alpha = 0.3f) + null -> MaterialTheme.colorScheme.surfaceVariant + } + } else { + containerColor + } - val badgeColor = when (r.passed) { - true -> Color(0xFF2E7D32) - false -> Color(0xFFC62828) - null -> MaterialTheme.colorScheme.onSurfaceVariant - } + val badgeColor = + when (r.passed) { + true -> Color(0xFF2E7D32) + false -> Color(0xFFC62828) + null -> MaterialTheme.colorScheme.onSurfaceVariant + } Card( shape = RoundedCornerShape(8.dp), colors = CardDefaults.cardColors(containerColor = actualColor), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) { Column(modifier = Modifier.padding(12.dp)) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Text( text = r.name, style = MaterialTheme.typography.bodyLarge, fontWeight = FontWeight.Bold, - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), ) Text( - text = when (r.passed) { true -> "PASS"; false -> "FAIL"; null -> "INFO" }, + text = + when (r.passed) { + true -> "PASS" + false -> "FAIL" + null -> "INFO" + }, fontWeight = FontWeight.Bold, fontSize = 13.sp, - color = badgeColor + color = badgeColor, ) } Spacer(Modifier.height(4.dp)) @@ -180,7 +194,7 @@ fun CheckCard(r: CheckResult) { text = r.detail, style = MaterialTheme.typography.bodySmall, fontFamily = FontFamily.Monospace, - color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f) + color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.8f), ) } } @@ -230,8 +244,11 @@ private fun runAllChecks(cm: ConnectivityManager): List { return results } -private fun nativeCheck(name: String, block: () -> String): CheckResult { - return try { +private fun nativeCheck( + name: String, + block: () -> String, +): CheckResult = + try { val raw = block() val passed = raw.startsWith("PASS") Log.i(TAG, "[$name] ${if (passed) "PASS" else "FAIL"}: $raw") @@ -241,23 +258,24 @@ private fun nativeCheck(name: String, block: () -> String): CheckResult { Log.e(TAG, "[$name] $detail", e) CheckResult(name, false, detail) } -} private fun checkHasTransportVpn(cm: ConnectivityManager): CheckResult { val name = "7. hasTransport(VPN)" Log.i(TAG, "=== CHECK: $name ===") val net = cm.activeNetwork if (net == null) return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } - val caps = cm.getNetworkCapabilities(net) - ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } + val caps = + cm.getNetworkCapabilities(net) + ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } val hasVpn = caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN) val hasWifi = caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) val hasCellular = caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) - val detail = if (!hasVpn) { - "PASS: hasTransport(VPN)=false, WIFI=$hasWifi, CELLULAR=$hasCellular" - } else { - "FAIL: hasTransport(VPN)=true, WIFI=$hasWifi, CELLULAR=$hasCellular" - } + val detail = + if (!hasVpn) { + "PASS: hasTransport(VPN)=false, WIFI=$hasWifi, CELLULAR=$hasCellular" + } else { + "FAIL: hasTransport(VPN)=true, WIFI=$hasWifi, CELLULAR=$hasCellular" + } Log.i(TAG, "[$name] $detail") Log.i(TAG, "[$name] caps.toString(): $caps") return CheckResult(name, !hasVpn, detail) @@ -266,10 +284,12 @@ private fun checkHasTransportVpn(cm: ConnectivityManager): CheckResult { private fun checkHasCapabilityNotVpn(cm: ConnectivityManager): CheckResult { val name = "8. hasCapability(NOT_VPN)" Log.i(TAG, "=== CHECK: $name ===") - val net = cm.activeNetwork - ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } - val caps = cm.getNetworkCapabilities(net) - ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } + val net = + cm.activeNetwork + ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } + val caps = + cm.getNetworkCapabilities(net) + ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } val notVpn = caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) val detail = if (notVpn) "PASS: NOT_VPN capability present" else "FAIL: NOT_VPN capability MISSING" Log.i(TAG, "[$name] $detail") @@ -279,10 +299,12 @@ private fun checkHasCapabilityNotVpn(cm: ConnectivityManager): CheckResult { private fun checkTransportInfo(cm: ConnectivityManager): CheckResult { val name = "9. getTransportInfo()" Log.i(TAG, "=== CHECK: $name ===") - val net = cm.activeNetwork - ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } - val caps = cm.getNetworkCapabilities(net) - ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } + val net = + cm.activeNetwork + ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } + val caps = + cm.getNetworkCapabilities(net) + ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } val info = caps.transportInfo val className = info?.javaClass?.name ?: "null" Log.i(TAG, "[$name] transportInfo class=$className, value=$info") @@ -296,8 +318,9 @@ private fun checkNetworkInterfaceEnum(): CheckResult { val name = "10. NetworkInterface enum" Log.i(TAG, "=== CHECK: $name ===") return try { - val ifaces = NetworkInterface.getNetworkInterfaces() - ?: return CheckResult(name, true, "PASS: returned null").also { Log.i(TAG, "[$name] ${it.detail}") } + val ifaces = + NetworkInterface.getNetworkInterfaces() + ?: return CheckResult(name, true, "PASS: returned null").also { Log.i(TAG, "[$name] ${it.detail}") } val allNames = mutableListOf() val vpnNames = mutableListOf() for (iface in ifaces) { @@ -305,11 +328,12 @@ private fun checkNetworkInterfaceEnum(): CheckResult { Log.i(TAG, "[$name] ${iface.name} up=${iface.isUp} loopback=${iface.isLoopback} p2p=${iface.isPointToPoint}") if (VPN_PREFIXES.any { iface.name.startsWith(it) }) vpnNames.add(iface.name) } - val detail = if (vpnNames.isEmpty()) { - "PASS: ${allNames.size} ifaces [${allNames.joinToString()}], no VPN" - } else { - "FAIL: VPN [${vpnNames.joinToString()}] in [${allNames.joinToString()}]" - } + val detail = + if (vpnNames.isEmpty()) { + "PASS: ${allNames.size} ifaces [${allNames.joinToString()}], no VPN" + } else { + "FAIL: VPN [${vpnNames.joinToString()}] in [${allNames.joinToString()}]" + } Log.i(TAG, "[$name] $detail") CheckResult(name, vpnNames.isEmpty(), detail) } catch (e: Exception) { @@ -341,11 +365,12 @@ private fun checkAllNetworksVpn(cm: ConnectivityManager): CheckResult { Log.i(TAG, "[$name] Network $net: [${transports.joinToString()}] VPN=$hasVpn") if (hasVpn) vpnNetworks.add(net.toString()) } - val detail = if (vpnNetworks.isEmpty()) { - "PASS: ${networks.size} networks, none have TRANSPORT_VPN" - } else { - "FAIL: ${vpnNetworks.size} network(s) with TRANSPORT_VPN: [${vpnNetworks.joinToString()}]" - } + val detail = + if (vpnNetworks.isEmpty()) { + "PASS: ${networks.size} networks, none have TRANSPORT_VPN" + } else { + "FAIL: ${vpnNetworks.size} network(s) with TRANSPORT_VPN: [${vpnNetworks.joinToString()}]" + } Log.i(TAG, "[$name] $detail") return CheckResult(name, vpnNetworks.isEmpty(), detail) } @@ -353,10 +378,12 @@ private fun checkAllNetworksVpn(cm: ConnectivityManager): CheckResult { private fun checkActiveNetworkVpn(cm: ConnectivityManager): CheckResult { val name = "11. ActiveNetwork transports" Log.i(TAG, "=== CHECK: $name ===") - val net = cm.activeNetwork - ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } - val caps = cm.getNetworkCapabilities(net) - ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } + val net = + cm.activeNetwork + ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } + val caps = + cm.getNetworkCapabilities(net) + ?: return CheckResult(name, true, "PASS: no capabilities").also { Log.i(TAG, "[$name] ${it.detail}") } val transports = mutableListOf() mapOf( NetworkCapabilities.TRANSPORT_CELLULAR to "CELLULAR", @@ -367,11 +394,12 @@ private fun checkActiveNetworkVpn(cm: ConnectivityManager): CheckResult { NetworkCapabilities.TRANSPORT_WIFI_AWARE to "WIFI_AWARE", ).forEach { (id, label) -> if (caps.hasTransport(id)) transports.add(label) } val hasVpn = caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN) - val detail = if (!hasVpn) { - "PASS: transports=[${transports.joinToString()}], no VPN" - } else { - "FAIL: transports include VPN: [${transports.joinToString()}]" - } + val detail = + if (!hasVpn) { + "PASS: transports=[${transports.joinToString()}], no VPN" + } else { + "FAIL: transports include VPN: [${transports.joinToString()}]" + } Log.i(TAG, "[$name] $detail") return CheckResult(name, !hasVpn, detail) } @@ -379,10 +407,12 @@ private fun checkActiveNetworkVpn(cm: ConnectivityManager): CheckResult { private fun checkLinkPropertiesIfname(cm: ConnectivityManager): CheckResult { val name = "12. LinkProperties ifname" Log.i(TAG, "=== CHECK: $name ===") - val net = cm.activeNetwork - ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } - val lp = cm.getLinkProperties(net) - ?: return CheckResult(name, true, "PASS: no link properties").also { Log.i(TAG, "[$name] ${it.detail}") } + val net = + cm.activeNetwork + ?: return CheckResult(name, true, "PASS: no active network").also { Log.i(TAG, "[$name] ${it.detail}") } + val lp = + cm.getLinkProperties(net) + ?: return CheckResult(name, true, "PASS: no link properties").also { Log.i(TAG, "[$name] ${it.detail}") } val ifname = lp.interfaceName ?: "(null)" val routes = lp.routes.map { "${it.destination} via ${it.gateway} dev ${it.`interface`}" } val dns = lp.dnsServers.map { it.hostAddress ?: "?" } @@ -391,11 +421,12 @@ private fun checkLinkPropertiesIfname(cm: ConnectivityManager): CheckResult { Log.i(TAG, "[$name] dns=${dns.joinToString(", ")}") Log.i(TAG, "[$name] httpProxy=${lp.httpProxy}") val isVpn = VPN_PREFIXES.any { ifname.startsWith(it) } - val detail = if (!isVpn) { - "PASS: ifname=$ifname, ${routes.size} routes, dns=[${dns.joinToString()}]" - } else { - "FAIL: ifname=$ifname is a VPN interface" - } + val detail = + if (!isVpn) { + "PASS: ifname=$ifname, ${routes.size} routes, dns=[${dns.joinToString()}]" + } else { + "FAIL: ifname=$ifname is a VPN interface" + } Log.i(TAG, "[$name] $detail") return CheckResult(name, !isVpn, detail) } @@ -409,11 +440,12 @@ private fun checkProxyHost(): CheckResult { val socksPort = System.getProperty("socksProxyPort") Log.i(TAG, "[$name] http=$httpHost:$httpPort, socks=$socksHost:$socksPort") val hasProxy = !httpHost.isNullOrEmpty() || !socksHost.isNullOrEmpty() - val detail = if (!hasProxy) { - "PASS: no proxy (http=$httpHost, socks=$socksHost)" - } else { - "FAIL: proxy found — http=$httpHost:$httpPort, socks=$socksHost:$socksPort" - } + val detail = + if (!hasProxy) { + "PASS: no proxy (http=$httpHost, socks=$socksHost)" + } else { + "FAIL: proxy found — http=$httpHost:$httpPort, socks=$socksHost:$socksPort" + } Log.i(TAG, "[$name] $detail") return CheckResult(name, !hasProxy, detail) } @@ -432,11 +464,12 @@ private fun checkProcNetRouteJava(): CheckResult { if (VPN_PREFIXES.any { line!!.startsWith(it) }) vpnLines.add(line!!.take(60)) } } - val detail = if (vpnLines.isEmpty()) { - "PASS: ${allLines.size} lines, no VPN entries" - } else { - "FAIL: ${vpnLines.size} VPN lines:\n${vpnLines.joinToString("\n") { " $it" }}" - } + val detail = + if (vpnLines.isEmpty()) { + "PASS: ${allLines.size} lines, no VPN entries" + } else { + "FAIL: ${vpnLines.size} VPN lines:\n${vpnLines.joinToString("\n") { " $it" }}" + } Log.i(TAG, "[$name] $detail") CheckResult(name, vpnLines.isEmpty(), detail) } catch (e: Exception) { diff --git a/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/NativeChecks.kt b/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/NativeChecks.kt index e621c66..3f23e49 100644 --- a/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/NativeChecks.kt +++ b/test-app/app/src/main/java/dev/okhsunrog/vpnhide/test/NativeChecks.kt @@ -6,18 +6,32 @@ object NativeChecks { } external fun checkIoctlSiocgifflags(): String + external fun checkIoctlSiocgifconf(): String + external fun checkGetifaddrs(): String + external fun checkProcNetRoute(): String + external fun checkProcNetIfInet6(): String + external fun checkNetlinkGetlink(): String + external fun checkNetlinkGetroute(): String + external fun checkProcNetIpv6Route(): String + external fun checkProcNetTcp(): String + external fun checkProcNetTcp6(): String + external fun checkProcNetUdp(): String + external fun checkProcNetUdp6(): String + external fun checkProcNetDev(): String + external fun checkProcNetFibTrie(): String + external fun checkSysClassNet(): String }