Showing preview only (1,983K chars total). Download the full file or copy to clipboard to get everything.
Repository: MatsuriDayo/NekoBoxForAndroid
Branch: main
Commit: 5768494d8ae3
Files: 524
Total size: 1.8 MB
Directory structure:
gitextract_rsn9ha2i/
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug-report-en.md
│ │ ├── bug-report-zh_cn.md
│ │ ├── feature_request-en.md
│ │ └── feature_request-zh_cn.md
│ └── workflows/
│ ├── preview.yml
│ └── release.yml
├── .gitignore
├── AUTHORS
├── LICENSE
├── README.md
├── app/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── executableSo/
│ │ └── .gitignore
│ ├── proguard-rules.pro
│ ├── schemas/
│ │ ├── io.nekohasekai.sagernet.database.SagerDatabase/
│ │ │ ├── 1.json
│ │ │ ├── 2.json
│ │ │ ├── 3.json
│ │ │ ├── 4.json
│ │ │ ├── 5.json
│ │ │ └── 6.json
│ │ ├── io.nekohasekai.sagernet.database.preference.PublicDatabase/
│ │ │ └── 1.json
│ │ └── moe.matsuri.nb4a.TempDatabase/
│ │ └── 1.json
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── aidl/
│ │ └── io/
│ │ └── nekohasekai/
│ │ └── sagernet/
│ │ └── aidl/
│ │ ├── ISagerNetService.aidl
│ │ ├── ISagerNetServiceCallback.aidl
│ │ ├── SpeedDisplayData.aidl
│ │ └── TrafficData.aidl
│ ├── assets/
│ │ ├── LICENSE
│ │ ├── proxy_packagename.txt
│ │ └── yacd.version.txt
│ ├── java/
│ │ ├── com/
│ │ │ └── github/
│ │ │ └── shadowsocks/
│ │ │ └── plugin/
│ │ │ ├── Utils.kt
│ │ │ └── fragment/
│ │ │ └── AlertDialogFragment.kt
│ │ ├── io/
│ │ │ └── nekohasekai/
│ │ │ └── sagernet/
│ │ │ ├── BootReceiver.kt
│ │ │ ├── Constants.kt
│ │ │ ├── QuickToggleShortcut.kt
│ │ │ ├── SagerNet.kt
│ │ │ ├── aidl/
│ │ │ │ ├── SpeedDisplayData.kt
│ │ │ │ └── TrafficData.kt
│ │ │ ├── bg/
│ │ │ │ ├── AbstractInstance.kt
│ │ │ │ ├── BaseService.kt
│ │ │ │ ├── Executable.kt
│ │ │ │ ├── GuardedProcessPool.kt
│ │ │ │ ├── ProxyService.kt
│ │ │ │ ├── SagerConnection.kt
│ │ │ │ ├── ServiceNotification.kt
│ │ │ │ ├── SubscriptionUpdater.kt
│ │ │ │ ├── TileService.kt
│ │ │ │ ├── VpnService.kt
│ │ │ │ └── proto/
│ │ │ │ ├── BoxInstance.kt
│ │ │ │ ├── ProxyInstance.kt
│ │ │ │ ├── TestInstance.kt
│ │ │ │ ├── TrafficLooper.kt
│ │ │ │ ├── TrafficUpdater.kt
│ │ │ │ └── UrlTest.kt
│ │ │ ├── database/
│ │ │ │ ├── DataStore.kt
│ │ │ │ ├── GroupManager.kt
│ │ │ │ ├── ParcelizeBridge.java
│ │ │ │ ├── ProfileManager.kt
│ │ │ │ ├── ProxyEntity.kt
│ │ │ │ ├── ProxyGroup.kt
│ │ │ │ ├── RuleEntity.kt
│ │ │ │ ├── SagerDatabase.kt
│ │ │ │ ├── StringCollectionConverter.kt
│ │ │ │ ├── SubscriptionBean.java
│ │ │ │ └── preference/
│ │ │ │ ├── EditTextPreferenceModifiers.kt
│ │ │ │ ├── KeyValuePair.kt
│ │ │ │ ├── OnPreferenceDataStoreChangeListener.kt
│ │ │ │ ├── PublicDatabase.kt
│ │ │ │ └── RoomPreferenceDataStore.kt
│ │ │ ├── fmt/
│ │ │ │ ├── AbstractBean.java
│ │ │ │ ├── ConfigBuilder.kt
│ │ │ │ ├── KryoConverters.java
│ │ │ │ ├── PluginEntry.kt
│ │ │ │ ├── Serializable.kt
│ │ │ │ ├── TypeMap.kt
│ │ │ │ ├── UniversalFmt.kt
│ │ │ │ ├── gson/
│ │ │ │ │ └── GsonConverters.java
│ │ │ │ ├── http/
│ │ │ │ │ ├── HttpBean.java
│ │ │ │ │ └── HttpFmt.kt
│ │ │ │ ├── hysteria/
│ │ │ │ │ ├── HysteriaBean.java
│ │ │ │ │ └── HysteriaFmt.kt
│ │ │ │ ├── internal/
│ │ │ │ │ ├── ChainBean.java
│ │ │ │ │ └── InternalBean.java
│ │ │ │ ├── mieru/
│ │ │ │ │ ├── MieruBean.java
│ │ │ │ │ └── MieruFmt.kt
│ │ │ │ ├── naive/
│ │ │ │ │ ├── NaiveBean.java
│ │ │ │ │ └── NaiveFmt.kt
│ │ │ │ ├── shadowsocks/
│ │ │ │ │ ├── ShadowsocksBean.java
│ │ │ │ │ └── ShadowsocksFmt.kt
│ │ │ │ ├── socks/
│ │ │ │ │ ├── SOCKSBean.java
│ │ │ │ │ └── SOCKSFmt.kt
│ │ │ │ ├── ssh/
│ │ │ │ │ ├── SSHBean.java
│ │ │ │ │ └── SSHFmt.kt
│ │ │ │ ├── trojan/
│ │ │ │ │ ├── TrojanBean.java
│ │ │ │ │ └── TrojanFmt.kt
│ │ │ │ ├── trojan_go/
│ │ │ │ │ ├── TrojanGoBean.java
│ │ │ │ │ └── TrojanGoFmt.kt
│ │ │ │ ├── tuic/
│ │ │ │ │ ├── TuicBean.java
│ │ │ │ │ └── TuicFmt.kt
│ │ │ │ ├── v2ray/
│ │ │ │ │ ├── StandardV2RayBean.java
│ │ │ │ │ ├── V2RayFmt.kt
│ │ │ │ │ └── VMessBean.java
│ │ │ │ └── wireguard/
│ │ │ │ ├── WireGuardBean.java
│ │ │ │ └── WireGuardFmt.kt
│ │ │ ├── group/
│ │ │ │ ├── GroupInterfaceAdapter.kt
│ │ │ │ ├── GroupUpdater.kt
│ │ │ │ └── RawUpdater.kt
│ │ │ ├── ktx/
│ │ │ │ ├── Asyncs.kt
│ │ │ │ ├── Browsers.kt
│ │ │ │ ├── Dialogs.kt
│ │ │ │ ├── Dimens.kt
│ │ │ │ ├── Formats.kt
│ │ │ │ ├── Kryos.kt
│ │ │ │ ├── Layouts.kt
│ │ │ │ ├── Logs.kt
│ │ │ │ ├── Nets.kt
│ │ │ │ ├── Preferences.kt
│ │ │ │ └── Utils.kt
│ │ │ ├── plugin/
│ │ │ │ └── PluginManager.kt
│ │ │ ├── ui/
│ │ │ │ ├── AboutFragment.kt
│ │ │ │ ├── AppListActivity.kt
│ │ │ │ ├── AppManagerActivity.kt
│ │ │ │ ├── AssetsActivity.kt
│ │ │ │ ├── BackupFragment.kt
│ │ │ │ ├── BlankActivity.kt
│ │ │ │ ├── ConfigurationFragment.kt
│ │ │ │ ├── GroupFragment.kt
│ │ │ │ ├── GroupSettingsActivity.kt
│ │ │ │ ├── LogcatFragment.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── NamedFragment.kt
│ │ │ │ ├── NetworkFragment.kt
│ │ │ │ ├── ProfileSelectActivity.kt
│ │ │ │ ├── QuickDisableShortcut.kt
│ │ │ │ ├── QuickEnableShortcut.kt
│ │ │ │ ├── RouteFragment.kt
│ │ │ │ ├── RouteSettingsActivity.kt
│ │ │ │ ├── ScannerActivity.kt
│ │ │ │ ├── SettingsFragment.kt
│ │ │ │ ├── SettingsPreferenceFragment.kt
│ │ │ │ ├── StunActivity.kt
│ │ │ │ ├── SwitchActivity.kt
│ │ │ │ ├── ThemedActivity.kt
│ │ │ │ ├── ToolbarFragment.kt
│ │ │ │ ├── ToolsFragment.kt
│ │ │ │ ├── VpnRequestActivity.kt
│ │ │ │ ├── WebviewFragment.kt
│ │ │ │ └── profile/
│ │ │ │ ├── ChainSettingsActivity.kt
│ │ │ │ ├── ConfigEditActivity.kt
│ │ │ │ ├── HttpSettingsActivity.kt
│ │ │ │ ├── HysteriaSettingsActivity.kt
│ │ │ │ ├── MieruSettingsActivity.kt
│ │ │ │ ├── NaiveSettingsActivity.kt
│ │ │ │ ├── ProfileSettingsActivity.kt
│ │ │ │ ├── SSHSettingsActivity.kt
│ │ │ │ ├── ShadowsocksSettingsActivity.kt
│ │ │ │ ├── SocksSettingsActivity.kt
│ │ │ │ ├── StandardV2RaySettingsActivity.kt
│ │ │ │ ├── TrojanGoSettingsActivity.kt
│ │ │ │ ├── TrojanSettingsActivity.kt
│ │ │ │ ├── TuicSettingsActivity.kt
│ │ │ │ ├── VMessSettingsActivity.kt
│ │ │ │ └── WireGuardSettingsActivity.kt
│ │ │ ├── utils/
│ │ │ │ ├── Commandline.kt
│ │ │ │ ├── CrashHandler.kt
│ │ │ │ ├── DefaultNetworkListener.kt
│ │ │ │ ├── PackageCache.kt
│ │ │ │ ├── Subnet.kt
│ │ │ │ └── Theme.kt
│ │ │ └── widget/
│ │ │ ├── AppListPreference.kt
│ │ │ ├── AutoCollapseTextView.kt
│ │ │ ├── FabProgressBehavior.kt
│ │ │ ├── GroupPreference.kt
│ │ │ ├── LinkOrContentPreference.kt
│ │ │ ├── OutboundPreference.kt
│ │ │ ├── QRCodeDialog.kt
│ │ │ ├── ServiceButton.kt
│ │ │ ├── StatsBar.kt
│ │ │ ├── UndoSnackbarManager.kt
│ │ │ ├── UserAgentPreference.kt
│ │ │ └── WindowInsetsListeners.kt
│ │ └── moe/
│ │ └── matsuri/
│ │ └── nb4a/
│ │ ├── NativeInterface.kt
│ │ ├── Protocols.kt
│ │ ├── SingBoxOptions.java
│ │ ├── SingBoxOptionsUtil.kt
│ │ ├── TempDatabase.kt
│ │ ├── net/
│ │ │ └── LocalResolverImpl.kt
│ │ ├── plugin/
│ │ │ └── Plugins.kt
│ │ ├── proxy/
│ │ │ ├── PreferenceBinding.kt
│ │ │ ├── PreferenceBindingManager.kt
│ │ │ ├── anytls/
│ │ │ │ ├── AnyTLSBean.java
│ │ │ │ ├── AnyTLSFmt.kt
│ │ │ │ └── AnyTLSSettingsActivity.kt
│ │ │ ├── config/
│ │ │ │ ├── ConfigBean.java
│ │ │ │ └── ConfigSettingActivity.kt
│ │ │ ├── neko/
│ │ │ │ └── NekoBean.java
│ │ │ └── shadowtls/
│ │ │ ├── ShadowTLSBean.java
│ │ │ ├── ShadowTLSFmt.kt
│ │ │ └── ShadowTLSSettingsActivity.kt
│ │ ├── ui/
│ │ │ ├── ColorPickerPreference.kt
│ │ │ ├── ConnectionTestNotification.kt
│ │ │ ├── Dialogs.kt
│ │ │ ├── EditConfigPreference.kt
│ │ │ ├── ExtendedKeyboard.kt
│ │ │ ├── LongClickListPreference.kt
│ │ │ ├── LongClickMenuPreference.kt
│ │ │ ├── LongClickSwitchPreference.kt
│ │ │ ├── MTUPreference.kt
│ │ │ ├── SimpleMenuPreference.kt
│ │ │ └── UrlTestPreference.kt
│ │ └── utils/
│ │ ├── JavaUtil.java
│ │ ├── KotlinUtil.kt
│ │ ├── NGUtil.kt
│ │ ├── SendLog.kt
│ │ ├── Util.kt
│ │ └── WebViewUtil.kt
│ └── res/
│ ├── color/
│ │ ├── chip_background.xml
│ │ ├── chip_ripple_color.xml
│ │ ├── chip_text_color.xml
│ │ ├── navigation_icon.xml
│ │ └── navigation_item.xml
│ ├── drawable/
│ │ ├── baseline_arrow_back_24.xml
│ │ ├── baseline_construction_24.xml
│ │ ├── baseline_delete_sweep_24.xml
│ │ ├── baseline_developer_board_24.xml
│ │ ├── baseline_flight_takeoff_24.xml
│ │ ├── baseline_keyboard_tab_24.xml
│ │ ├── baseline_public_24.xml
│ │ ├── baseline_redo_24.xml
│ │ ├── baseline_save_24.xml
│ │ ├── baseline_send_24.xml
│ │ ├── baseline_translate_24.xml
│ │ ├── baseline_undo_24.xml
│ │ ├── baseline_widgets_24.xml
│ │ ├── baseline_wrap_text_24.xml
│ │ ├── ic_action_copyright.xml
│ │ ├── ic_action_delete.xml
│ │ ├── ic_action_description.xml
│ │ ├── ic_action_dns.xml
│ │ ├── ic_action_done.xml
│ │ ├── ic_action_lock.xml
│ │ ├── ic_action_lock_open.xml
│ │ ├── ic_action_note_add.xml
│ │ ├── ic_action_settings.xml
│ │ ├── ic_app_shortcut_background.xml
│ │ ├── ic_av_playlist_add.xml
│ │ ├── ic_baseline_add_road_24.xml
│ │ ├── ic_baseline_airplanemode_active_24.xml
│ │ ├── ic_baseline_android_24.xml
│ │ ├── ic_baseline_bug_report_24.xml
│ │ ├── ic_baseline_camera_24.xml
│ │ ├── ic_baseline_card_giftcard_24.xml
│ │ ├── ic_baseline_cast_connected_24.xml
│ │ ├── ic_baseline_center_focus_weak_24.xml
│ │ ├── ic_baseline_color_lens_24.xml
│ │ ├── ic_baseline_compare_arrows_24.xml
│ │ ├── ic_baseline_domain_24.xml
│ │ ├── ic_baseline_download_24.xml
│ │ ├── ic_baseline_emoji_emotions_24.xml
│ │ ├── ic_baseline_fast_forward_24.xml
│ │ ├── ic_baseline_fiber_manual_record_24.xml
│ │ ├── ic_baseline_fingerprint_24.xml
│ │ ├── ic_baseline_flip_camera_android_24.xml
│ │ ├── ic_baseline_format_align_left_24.xml
│ │ ├── ic_baseline_grid_3x3_24.xml
│ │ ├── ic_baseline_home_24.xml
│ │ ├── ic_baseline_http_24.xml
│ │ ├── ic_baseline_https_24.xml
│ │ ├── ic_baseline_import_contacts_24.xml
│ │ ├── ic_baseline_info_24.xml
│ │ ├── ic_baseline_layers_24.xml
│ │ ├── ic_baseline_legend_toggle_24.xml
│ │ ├── ic_baseline_link_24.xml
│ │ ├── ic_baseline_local_bar_24.xml
│ │ ├── ic_baseline_location_on_24.xml
│ │ ├── ic_baseline_lock_24.xml
│ │ ├── ic_baseline_low_priority_24.xml
│ │ ├── ic_baseline_manage_search_24.xml
│ │ ├── ic_baseline_more_vert_24.xml
│ │ ├── ic_baseline_multiline_chart_24.xml
│ │ ├── ic_baseline_multiple_stop_24.xml
│ │ ├── ic_baseline_nat_24.xml
│ │ ├── ic_baseline_nfc_24.xml
│ │ ├── ic_baseline_no_encryption_gmailerrorred_24.xml
│ │ ├── ic_baseline_person_24.xml
│ │ ├── ic_baseline_push_pin_24.xml
│ │ ├── ic_baseline_refresh_24.xml
│ │ ├── ic_baseline_rule_folder_24.xml
│ │ ├── ic_baseline_running_with_errors_24.xml
│ │ ├── ic_baseline_sanitizer_24.xml
│ │ ├── ic_baseline_security_24.xml
│ │ ├── ic_baseline_shuffle_24.xml
│ │ ├── ic_baseline_shutter_speed_24.xml
│ │ ├── ic_baseline_speed_24.xml
│ │ ├── ic_baseline_stream_24.xml
│ │ ├── ic_baseline_texture_24.xml
│ │ ├── ic_baseline_timelapse_24.xml
│ │ ├── ic_baseline_transform_24.xml
│ │ ├── ic_baseline_transgender_24.xml
│ │ ├── ic_baseline_update_24.xml
│ │ ├── ic_baseline_view_list_24.xml
│ │ ├── ic_baseline_vpn_key_24.xml
│ │ ├── ic_baseline_warning_24.xml
│ │ ├── ic_baseline_wb_sunny_24.xml
│ │ ├── ic_communication_phonelink_ring.xml
│ │ ├── ic_device_data_usage.xml
│ │ ├── ic_device_developer_mode.xml
│ │ ├── ic_file_cloud_queue.xml
│ │ ├── ic_file_file_upload.xml
│ │ ├── ic_hardware_router.xml
│ │ ├── ic_image_camera_alt.xml
│ │ ├── ic_image_edit.xml
│ │ ├── ic_image_looks_6.xml
│ │ ├── ic_image_photo.xml
│ │ ├── ic_maps_360.xml
│ │ ├── ic_maps_directions.xml
│ │ ├── ic_maps_directions_boat.xml
│ │ ├── ic_navigation_apps.xml
│ │ ├── ic_navigation_close.xml
│ │ ├── ic_navigation_menu.xml
│ │ ├── ic_notification_enhanced_encryption.xml
│ │ ├── ic_qu_camera_launcher.xml
│ │ ├── ic_qu_shadowsocks_foreground.xml
│ │ ├── ic_qu_shadowsocks_launcher.xml
│ │ ├── ic_service_active.xml
│ │ ├── ic_service_busy.xml
│ │ ├── ic_service_connected.xml
│ │ ├── ic_service_connecting.xml
│ │ ├── ic_service_idle.xml
│ │ ├── ic_service_stopped.xml
│ │ ├── ic_service_stopping.xml
│ │ ├── ic_settings_password.xml
│ │ ├── ic_social_emoji_symbols.xml
│ │ ├── ic_social_share.xml
│ │ └── terminal_scroll_shape.xml
│ ├── drawable-v26/
│ │ ├── ic_qu_camera_launcher.xml
│ │ └── ic_qu_shadowsocks_launcher.xml
│ ├── layout/
│ │ ├── item_keyboard_key.xml
│ │ ├── layout_about.xml
│ │ ├── layout_add_entity.xml
│ │ ├── layout_app_list.xml
│ │ ├── layout_app_placeholder.xml
│ │ ├── layout_appbar.xml
│ │ ├── layout_apps.xml
│ │ ├── layout_apps_item.xml
│ │ ├── layout_asset_item.xml
│ │ ├── layout_assets.xml
│ │ ├── layout_backup.xml
│ │ ├── layout_chain_settings.xml
│ │ ├── layout_config_settings.xml
│ │ ├── layout_debug.xml
│ │ ├── layout_edit_config.xml
│ │ ├── layout_edit_group.xml
│ │ ├── layout_empty.xml
│ │ ├── layout_empty_route.xml
│ │ ├── layout_group.xml
│ │ ├── layout_group_item.xml
│ │ ├── layout_group_list.xml
│ │ ├── layout_icon_list_item_2.xml
│ │ ├── layout_import.xml
│ │ ├── layout_loading.xml
│ │ ├── layout_logcat.xml
│ │ ├── layout_loglevel_help.xml
│ │ ├── layout_main.xml
│ │ ├── layout_mtu_help.xml
│ │ ├── layout_network.xml
│ │ ├── layout_password_dialog.xml
│ │ ├── layout_profile.xml
│ │ ├── layout_profile_list.xml
│ │ ├── layout_progress.xml
│ │ ├── layout_progress_list.xml
│ │ ├── layout_route.xml
│ │ ├── layout_route_item.xml
│ │ ├── layout_scanner.xml
│ │ ├── layout_settings_activity.xml
│ │ ├── layout_stun.xml
│ │ ├── layout_tools.xml
│ │ ├── layout_urltest_preference_dialog.xml
│ │ ├── layout_webview.xml
│ │ └── simple_menu_dropdown_item.xml
│ ├── menu/
│ │ ├── add_group_menu.xml
│ │ ├── add_profile_menu.xml
│ │ ├── add_route_menu.xml
│ │ ├── app_list_menu.xml
│ │ ├── group_action_menu.xml
│ │ ├── import_asset_menu.xml
│ │ ├── logcat_menu.xml
│ │ ├── main_drawer_menu.xml
│ │ ├── per_app_proxy_menu.xml
│ │ ├── profile_apply_menu.xml
│ │ ├── profile_config_menu.xml
│ │ ├── profile_share_menu.xml
│ │ ├── scanner_menu.xml
│ │ ├── traffic_item_menu.xml
│ │ ├── traffic_menu.xml
│ │ └── yacd_menu.xml
│ ├── mipmap-anydpi-v26/
│ │ └── ic_launcher.xml
│ ├── raw/
│ │ ├── insecure.txt
│ │ ├── not_encrypted.txt
│ │ ├── shadowsocks_stream_cipher.txt
│ │ └── vmess_md5_auth.txt
│ ├── raw-zh-rCN/
│ │ ├── insecure.txt
│ │ ├── not_encrypted.txt
│ │ ├── shadowsocks_stream_cipher.txt
│ │ └── vmess_md5_auth.txt
│ ├── resources.properties
│ ├── values/
│ │ ├── arrays.xml
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ ├── values-ar/
│ │ └── strings.xml
│ ├── values-be/
│ │ └── strings.xml
│ ├── values-de/
│ │ └── strings.xml
│ ├── values-es/
│ │ └── strings.xml
│ ├── values-fa/
│ │ └── strings.xml
│ ├── values-fr/
│ │ └── strings.xml
│ ├── values-in/
│ │ └── strings.xml
│ ├── values-it/
│ │ └── strings.xml
│ ├── values-ja/
│ │ └── strings.xml
│ ├── values-ko/
│ │ └── strings.xml
│ ├── values-nb-rNO/
│ │ └── strings.xml
│ ├── values-night/
│ │ └── colors.xml
│ ├── values-nl/
│ │ └── strings.xml
│ ├── values-pt-rBR/
│ │ └── strings.xml
│ ├── values-ru/
│ │ └── strings.xml
│ ├── values-tr/
│ │ └── strings.xml
│ ├── values-uk/
│ │ └── strings.xml
│ ├── values-zh-rCN/
│ │ └── strings.xml
│ ├── values-zh-rHK/
│ │ └── strings.xml
│ ├── values-zh-rTW/
│ │ └── strings.xml
│ └── xml/
│ ├── anytls_preferences.xml
│ ├── backup_descriptor.xml
│ ├── backup_rules.xml
│ ├── balancer_preferences.xml
│ ├── cache_paths.xml
│ ├── config_preferences.xml
│ ├── global_preferences.xml
│ ├── group_preferences.xml
│ ├── hysteria_preferences.xml
│ ├── mieru_preferences.xml
│ ├── naive_preferences.xml
│ ├── name_preferences.xml
│ ├── neko_preferences.xml
│ ├── network_security_config.xml
│ ├── route_preferences.xml
│ ├── shadowsocks_preferences.xml
│ ├── shadowtls_preferences.xml
│ ├── shortcuts.xml
│ ├── socks_preferences.xml
│ ├── ssh_preferences.xml
│ ├── standard_v2ray_preferences.xml
│ ├── trojan_go_preferences.xml
│ ├── tuic_preferences.xml
│ └── wireguard_preferences.xml
├── build.gradle.kts
├── buildScript/
│ ├── copyLocal.sh
│ ├── fdroid/
│ │ └── prebuild.sh
│ ├── init/
│ │ ├── action/
│ │ │ └── gradle.sh
│ │ ├── env.sh
│ │ └── env_ndk.sh
│ └── lib/
│ ├── assets.sh
│ ├── core/
│ │ ├── build.sh
│ │ ├── get_source.sh
│ │ ├── get_source_env.sh
│ │ └── init.sh
│ └── core.sh
├── buildSrc/
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── kotlin/
│ └── Helpers.kt
├── gradle/
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── libcore/
│ ├── .gitignore
│ ├── LICENSE
│ ├── assets.go
│ ├── assets_android.go
│ ├── assets_other.go
│ ├── box.go
│ ├── box_include.go
│ ├── build.sh
│ ├── certs.go
│ ├── crypto.go
│ ├── device/
│ │ ├── debug.go
│ │ └── device.go
│ ├── dns_android.go
│ ├── dns_box.go
│ ├── ech/
│ │ └── ech.go
│ ├── fix.go
│ ├── geoip.go
│ ├── geosite.go
│ ├── go.mod
│ ├── go.sum
│ ├── http.go
│ ├── init.sh
│ ├── interface_monitor.go
│ ├── io.go
│ ├── nb4a.go
│ ├── platform_box.go
│ ├── platform_java.go
│ ├── procfs/
│ │ └── procfs.go
│ ├── stun/
│ │ ├── README
│ │ ├── attribute.go
│ │ ├── client.go
│ │ ├── const.go
│ │ ├── discover.go
│ │ ├── doc.go
│ │ ├── host.go
│ │ ├── log.go
│ │ ├── net.go
│ │ ├── packet.go
│ │ ├── response.go
│ │ ├── tests.go
│ │ └── utils.go
│ └── stun.go
├── lint.xml
├── nb4a.properties
├── release.keystore
├── repositories.gradle.kts
├── run
└── settings.gradle.kts
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report-en.md
================================================
---
name: 'Bug Report'
about: 'Please troubleshoot server-side issues and upgrade to the latest client before raising a question.'
title: 'BUG: '
labels: ''
assignees: ''
---
## Describe the problem
Expected behavior:
Actual behavior:
## How to reproduce
Provide helpful screenshots, videos, text descriptions, subscription links, etc.
## log
If you have logs, please upload them. Please see the detailed steps for exporting logs in the documentation.
================================================
FILE: .github/ISSUE_TEMPLATE/bug-report-zh_cn.md
================================================
---
name: '问题反馈'
about: '在提出问题前请先自行排除服务器端问题和升级到最新客户端。'
title: 'BUG: '
labels: ''
assignees: ''
---
## 描述问题
预期行为:
实际行为:
## 如何复现
提供有帮助的截图,录像,文字说明,订阅链接等。
## 日志
如果有日志,请上传。请在文档内查看导出日志的详细步骤。
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request-en.md
================================================
---
name: 'Feature Request'
about: 'Make suggestions for new features of the software'
title: ''
labels: ''
assignees: ''
---
## Description suggestions
## Necessity of recommendations
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request-zh_cn.md
================================================
---
name: '功能请求'
about: '对软件的新功能提出建议。'
title: ''
labels: ''
assignees: ''
---
## 描述建议
## 建议的必要性
================================================
FILE: .github/workflows/preview.yml
================================================
name: Preview Build
on:
workflow_dispatch:
inputs:
jobs:
libcore:
name: Native Build (LibCore)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Golang Status
run: find buildScript libcore/*.sh | xargs cat | sha1sum > golang_status
- name: Libcore Status
run: git ls-files libcore | xargs cat | sha1sum > libcore_status
- name: LibCore Cache
id: cache
uses: actions/cache@v4
with:
path: |
app/libs/libcore.aar
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
- name: Install Golang
if: steps.cache.outputs.cache-hit != 'true'
uses: actions/setup-go@v5
with:
go-version: ^1.25
- name: Native Build
if: steps.cache.outputs.cache-hit != 'true'
run: ./run lib core
build:
name: Build OSS APK
runs-on: ubuntu-latest
needs:
- libcore
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Golang Status
run: find buildScript libcore/*.sh | xargs cat | sha1sum > golang_status
- name: Libcore Status
run: git ls-files libcore | xargs cat | sha1sum > libcore_status
- name: LibCore Cache
uses: actions/cache@v4
with:
path: |
app/libs/libcore.aar
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
- name: Gradle cache
uses: actions/cache@v4
with:
path: ~/.gradle
key: gradle-oss-${{ hashFiles('**/*.gradle.kts') }}
- name: Gradle Build
env:
BUILD_PLUGIN: none
run: |
echo "sdk.dir=${ANDROID_HOME}" > local.properties
echo "ndk.dir=${ANDROID_HOME}/ndk/25.0.8775105" >> local.properties
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./run init action gradle
./gradlew app:assemblePreviewRelease
APK=$(find app/build/outputs/apk -name '*arm64-v8a*.apk')
APK=$(dirname $APK)
echo "APK=$APK" >> $GITHUB_ENV
- uses: actions/upload-artifact@v4
with:
name: APKs
path: ${{ env.APK }}
================================================
FILE: .github/workflows/release.yml
================================================
name: Release Build
on:
workflow_dispatch:
inputs:
tag:
description: "Release Tag"
required: true
publish:
description: "Publish: If want ignore"
required: false
play:
description: "Play: If want ignore"
required: false
jobs:
libcore:
name: Native Build (LibCore)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Golang Status
run: find buildScript libcore/*.sh | xargs cat | sha1sum > golang_status
- name: Libcore Status
run: git ls-files libcore | xargs cat | sha1sum > libcore_status
- name: LibCore Cache
id: cache
uses: actions/cache@v4
with:
path: |
app/libs/libcore.aar
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
- name: Install Golang
if: steps.cache.outputs.cache-hit != 'true'
uses: actions/setup-go@v5
with:
go-version: ^1.25
- name: Native Build
if: steps.cache.outputs.cache-hit != 'true'
run: ./run lib core
build:
name: Build OSS APK
runs-on: ubuntu-latest
needs:
- libcore
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Golang Status
run: find buildScript libcore/*.sh | xargs cat | sha1sum > golang_status
- name: Libcore Status
run: git ls-files libcore | xargs cat | sha1sum > libcore_status
- name: LibCore Cache
uses: actions/cache@v4
with:
path: |
app/libs/libcore.aar
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
- name: Gradle cache
uses: actions/cache@v4
with:
path: ~/.gradle
key: gradle-oss-${{ hashFiles('**/*.gradle.kts') }}
- name: Gradle Build
env:
BUILD_PLUGIN: none
run: |
echo "sdk.dir=${ANDROID_HOME}" > local.properties
echo "ndk.dir=${ANDROID_HOME}/ndk/25.0.8775105" >> local.properties
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./run init action gradle
./gradlew app:assembleOssRelease
APK=$(find app/build/outputs/apk -name '*arm64-v8a*.apk')
APK=$(dirname $APK)
echo "APK=$APK" >> $GITHUB_ENV
- uses: actions/upload-artifact@v4
with:
name: APKs
path: ${{ env.APK }}
publish:
name: Publish Release
if: github.event.inputs.publish != 'y'
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Donwload Artifacts
uses: actions/download-artifact@v4
with:
name: APKs
path: artifacts
- name: Release
run: |
wget -O ghr.tar.gz https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz
tar -xvf ghr.tar.gz
mv ghr*linux_amd64/ghr .
mkdir apks
find artifacts -name "*.apk" -exec cp {} apks \;
./ghr -delete -t "${{ github.token }}" -n "${{ github.event.inputs.tag }}" "${{ github.event.inputs.tag }}" apks
play:
name: Build Play Bundle
if: github.event.inputs.play != 'y'
runs-on: ubuntu-latest
needs:
- libcore
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Golang Status
run: find buildScript libcore/*.sh | xargs cat | sha1sum > golang_status
- name: Libcore Status
run: git ls-files libcore | xargs cat | sha1sum > libcore_status
- name: LibCore Cache
uses: actions/cache@v4
with:
path: |
app/libs/libcore.aar
key: ${{ hashFiles('.github/workflows/*', 'golang_status', 'libcore_status') }}
- name: Gradle cache
uses: actions/cache@v4
with:
path: ~/.gradle
key: gradle-play-${{ hashFiles('**/*.gradle.kts') }}
- name: Checkout Library
run: |
git submodule update --init 'app/*'
- name: Gradle Build
run: |
echo "sdk.dir=${ANDROID_HOME}" > local.properties
echo "ndk.dir=${ANDROID_HOME}/ndk/25.0.8775105" >> local.properties
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}"
./run init action gradle
./gradlew bundlePlayRelease
- uses: actions/upload-artifact@v3
with:
name: AAB
path: app/build/outputs/bundle/playRelease/app-play-release.aab
================================================
FILE: .gitignore
================================================
*.iml
.gradle
.idea
.vscode
.DS_Store
build/
/captures
.externalNativeBuild
.cxx
local.properties
/app/libs/
/app/src/main/assets/sing-box
/service_account_credentials.json
jniLibs/
/library/libcore_build/
.idea/deploymentTargetDropDown.xml
/nkmr
# submodules
/external
================================================
FILE: AUTHORS
================================================
SagerNet was originally created in late 2021, by
nekohasekai <contact-sagernet@sekai.icu>.
Here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS --
people who have submitted patches, fixed bugs, added translations, and
generally made SagerNet that much better:
https://github.com/SagerNet/SagerNet/graphs/contributors
================================================
FILE: LICENSE
================================================
Copyright (C) 2021 by nekohasekai <contact-sagernet@sekai.icu>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
================================================
FILE: README.md
================================================
# NekoBox for Android
[](https://android-arsenal.com/api?level=21)
[](https://github.com/MatsuriDayo/NekoBoxForAndroid/releases)
[](https://www.gnu.org/licenses/gpl-3.0)
sing-box / universal proxy toolchain for Android.
一款使用 sing-box 的 Android 通用代理软件.
## 下载 / Downloads
[](https://github.com/Matsuridayo/NekoBoxForAndroid/releases)
[GitHub Releases 下载](https://github.com/Matsuridayo/NekoBoxForAndroid/releases)
**Google Play 版本自 2024 年 5 月起已被第三方控制,为非开源版本,请不要下载。**
**The Google Play version has been controlled by a third party since May 2024 and is a non-open
source version. Please do not download it.**
## 更新日志 & Telegram 发布频道 / Changelog & Telegram Channel
https://t.me/Matsuridayo
## 项目主页 & 文档 / Homepage & Documents
https://matsuridayo.github.io
## 支持的代理协议 / Supported Proxy Protocols
* SOCKS (4/4a/5)
* HTTP(S)
* SSH
* Shadowsocks
* VMess
* Trojan
* VLESS
* AnyTLS
* ShadowTLS
* TUIC
* Hysteria 1/2
* WireGuard
* Trojan-Go (trojan-go-plugin)
* NaïveProxy (naive-plugin)
* Mieru (mieru-plugin)
请到[这里](https://matsuridayo.github.io/nb4a-plugin/)下载插件以获得完整的代理支持.
Please visit [here](https://matsuridayo.github.io/nb4a-plugin/) to download plugins for full proxy
supports.
## 支持的订阅格式 / Supported Subscription Format
* 一些广泛使用的格式 (如 Shadowsocks, ClashMeta 和 v2rayN)
* sing-box 出站
仅支持解析出站,即节点。分流规则等信息会被忽略。
* Some widely used formats (like Shadowsocks, ClashMeta and v2rayN)
* sing-box outbound
Only resolving outbound, i.e. nodes, is supported. Information such as diversion rules are ignored.
## 捐助 / Donate
<details>
如果这个项目对您有帮助, 可以通过捐赠的方式帮助我们维持这个项目.
捐赠满等额 50 USD 可以在「[捐赠榜](https://mtrdnt.pages.dev/donation_list)」显示头像, 如果您未被添加到这里,
欢迎联系我们补充.
Donations of 50 USD or more can display your avatar on
the [Donation List](https://mtrdnt.pages.dev/donation_list). If you are not added here, please
contact us to add it.
USDT TRC20
`TRhnA7SXE5Sap5gSG3ijxRmdYFiD4KRhPs`
XMR
`49bwESYQjoRL3xmvTcjZKHEKaiGywjLYVQJMUv79bXonGiyDCs8AzE3KiGW2ytTybBCpWJUvov8SjZZEGg66a4e59GXa6k5`
</details>
## Credits
Core:
- [SagerNet/sing-box](https://github.com/SagerNet/sing-box)
Android GUI:
- [shadowsocks/shadowsocks-android](https://github.com/shadowsocks/shadowsocks-android)
- [SagerNet/SagerNet](https://github.com/SagerNet/SagerNet)
Web Dashboard:
- [Yacd-meta](https://github.com/MetaCubeX/Yacd-meta)
================================================
FILE: app/.gitignore
================================================
/build
================================================
FILE: app/build.gradle.kts
================================================
@file:Suppress("UnstableApiUsage")
plugins {
id("com.android.application")
id("kotlin-android")
id("com.google.devtools.ksp")
id("kotlin-parcelize")
}
setupApp()
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
ksp {
arg("room.incremental", "true")
arg("room.schemaLocation", "$projectDir/schemas")
}
bundle {
language {
enableSplit = false
}
}
buildFeatures {
buildConfig = true
viewBinding = true
aidl = true
}
namespace = "io.nekohasekai.sagernet"
packaging {
jniLibs {
useLegacyPackaging = true
}
}
androidResources {
generateLocaleConfig = true
}
}
dependencies {
implementation(fileTree("libs"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4")
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.recyclerview:recyclerview:1.3.0")
implementation("androidx.activity:activity-ktx:1.10.1")
implementation("androidx.fragment:fragment-ktx:1.5.6")
implementation("androidx.browser:browser:1.5.0")
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.navigation:navigation-fragment-ktx:2.5.3")
implementation("androidx.navigation:navigation-ui-ktx:2.5.3")
implementation("androidx.preference:preference-ktx:1.2.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.work:work-runtime-ktx:2.8.1")
implementation("androidx.work:work-multiprocess:2.8.1")
implementation("com.google.android.material:material:1.8.0")
implementation("com.google.code.gson:gson:2.9.0")
implementation("com.github.jenly1314:zxing-lite:2.1.1")
implementation("com.blacksquircle.ui:editorkit:2.6.0")
implementation("com.blacksquircle.ui:language-base:2.6.0")
implementation("com.blacksquircle.ui:language-json:2.6.0")
implementation("com.squareup.okhttp3:okhttp:5.0.0-alpha.3")
implementation("org.yaml:snakeyaml:1.30")
implementation("com.github.daniel-stoneuk:material-about-library:3.2.0-rc01")
implementation("com.jakewharton:process-phoenix:2.1.2")
implementation("com.esotericsoftware:kryo:5.2.1")
implementation("com.google.guava:guava:31.0.1-android")
implementation("org.ini4j:ini4j:0.5.4")
implementation("com.simplecityapps:recyclerview-fastscroll:2.0.1") {
exclude(group = "androidx.recyclerview")
exclude(group = "androidx.appcompat")
}
implementation("androidx.room:room-runtime:2.6.1")
ksp("androidx.room:room-compiler:2.6.1")
implementation("androidx.room:room-ktx:2.6.1")
implementation("com.github.MatrixDev.Roomigrant:RoomigrantLib:0.3.4")
ksp("com.github.MatrixDev.Roomigrant:RoomigrantCompiler:0.3.4")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.3")
}
================================================
FILE: app/executableSo/.gitignore
================================================
*.so
================================================
FILE: app/proguard-rules.pro
================================================
-repackageclasses ''
-allowaccessmodification
-keep class io.nekohasekai.sagernet.** { *;}
-keep class moe.matsuri.nb4a.** { *;}
# Clean Kotlin
-assumenosideeffects class kotlin.jvm.internal.Intrinsics {
static void checkParameterIsNotNull(java.lang.Object, java.lang.String);
static void checkExpressionValueIsNotNull(java.lang.Object, java.lang.String);
static void checkNotNullExpressionValue(java.lang.Object, java.lang.String);
static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
static void checkReturnedValueIsNotNull(java.lang.Object, java.lang.String);
static void checkFieldIsNotNull(java.lang.Object, java.lang.String, java.lang.String);
static void checkFieldIsNotNull(java.lang.Object, java.lang.String);
static void checkNotNull(java.lang.Object);
static void checkNotNull(java.lang.Object, java.lang.String);
static void checkNotNullParameter(java.lang.Object, java.lang.String);
static void throwUninitializedPropertyAccessException(java.lang.String);
}
# ini4j
-keep public class org.ini4j.spi.** { <init>(); }
# SnakeYaml
-keep class org.yaml.snakeyaml.** { *; }
-dontobfuscate
-keepattributes SourceFile
-dontwarn java.beans.BeanInfo
-dontwarn java.beans.FeatureDescriptor
-dontwarn java.beans.IntrospectionException
-dontwarn java.beans.Introspector
-dontwarn java.beans.PropertyDescriptor
-dontwarn java.beans.Transient
-dontwarn java.beans.VetoableChangeListener
-dontwarn java.beans.VetoableChangeSupport
-dontwarn org.apache.harmony.xnet.provider.jsse.SSLParametersImpl
-dontwarn org.bouncycastle.jce.provider.BouncyCastleProvider
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-dontwarn java.beans.PropertyVetoException
================================================
FILE: app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/1.json
================================================
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f66fd943df1d9e86d281a2e32c9fdd47",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f66fd943df1d9e86d281a2e32c9fdd47')"
]
}
}
================================================
FILE: app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/2.json
================================================
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "9ec160533656482a17cbd563e9e3e416",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "shadowTLSBean",
"columnName": "shadowTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9ec160533656482a17cbd563e9e3e416')"
]
}
}
================================================
FILE: app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/3.json
================================================
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "cff00d0142d9e53d2ca24a6a55cd213c",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "mieruBean",
"columnName": "mieruBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "shadowTLSBean",
"columnName": "shadowTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cff00d0142d9e53d2ca24a6a55cd213c')"
]
}
}
================================================
FILE: app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/4.json
================================================
{
"formatVersion": 1,
"database": {
"version": 4,
"identityHash": "cff00d0142d9e53d2ca24a6a55cd213c",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "mieruBean",
"columnName": "mieruBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "shadowTLSBean",
"columnName": "shadowTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cff00d0142d9e53d2ca24a6a55cd213c')"
]
}
}
================================================
FILE: app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/5.json
================================================
{
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "1dbf667053726c13d139a4d83c41f895",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `anyTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "mieruBean",
"columnName": "mieruBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "shadowTLSBean",
"columnName": "shadowTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "anyTLSBean",
"columnName": "anyTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1dbf667053726c13d139a4d83c41f895')"
]
}
}
================================================
FILE: app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/6.json
================================================
{
"formatVersion": 1,
"database": {
"version": 6,
"identityHash": "3d3db9106a89d6f20ef3fde6e81dbaa9",
"entities": [
{
"tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ungrouped",
"columnName": "ungrouped",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "subscription",
"columnName": "subscription",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "order",
"columnName": "order",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isSelector",
"columnName": "isSelector",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "frontProxy",
"columnName": "frontProxy",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "proxy_entities",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `groupId` INTEGER NOT NULL, `type` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `status` INTEGER NOT NULL, `ping` INTEGER NOT NULL, `uuid` TEXT NOT NULL, `error` TEXT, `socksBean` BLOB, `httpBean` BLOB, `ssBean` BLOB, `vmessBean` BLOB, `trojanBean` BLOB, `trojanGoBean` BLOB, `mieruBean` BLOB, `naiveBean` BLOB, `hysteriaBean` BLOB, `tuicBean` BLOB, `sshBean` BLOB, `wgBean` BLOB, `shadowTLSBean` BLOB, `anyTLSBean` BLOB, `chainBean` BLOB, `nekoBean` BLOB, `configBean` BLOB)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "groupId",
"columnName": "groupId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "type",
"columnName": "type",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "status",
"columnName": "status",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ping",
"columnName": "ping",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "uuid",
"columnName": "uuid",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "error",
"columnName": "error",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "socksBean",
"columnName": "socksBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "httpBean",
"columnName": "httpBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "ssBean",
"columnName": "ssBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "vmessBean",
"columnName": "vmessBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanBean",
"columnName": "trojanBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "trojanGoBean",
"columnName": "trojanGoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "mieruBean",
"columnName": "mieruBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "naiveBean",
"columnName": "naiveBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "hysteriaBean",
"columnName": "hysteriaBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "tuicBean",
"columnName": "tuicBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "sshBean",
"columnName": "sshBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "wgBean",
"columnName": "wgBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "shadowTLSBean",
"columnName": "shadowTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "anyTLSBean",
"columnName": "anyTLSBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "chainBean",
"columnName": "chainBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "nekoBean",
"columnName": "nekoBean",
"affinity": "BLOB",
"notNull": false
},
{
"fieldPath": "configBean",
"columnName": "configBean",
"affinity": "BLOB",
"notNull": false
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [
{
"name": "groupId",
"unique": false,
"columnNames": [
"groupId"
],
"orders": [],
"createSql": "CREATE INDEX IF NOT EXISTS `groupId` ON `${TABLE_NAME}` (`groupId`)"
}
],
"foreignKeys": []
},
{
"tableName": "rules",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NOT NULL, `config` TEXT NOT NULL DEFAULT '', `userOrder` INTEGER NOT NULL, `enabled` INTEGER NOT NULL, `domains` TEXT NOT NULL, `ip` TEXT NOT NULL, `port` TEXT NOT NULL, `sourcePort` TEXT NOT NULL, `network` TEXT NOT NULL, `source` TEXT NOT NULL, `protocol` TEXT NOT NULL, `outbound` INTEGER NOT NULL, `packages` TEXT NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "config",
"columnName": "config",
"affinity": "TEXT",
"notNull": true,
"defaultValue": "''"
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "enabled",
"columnName": "enabled",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "domains",
"columnName": "domains",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "ip",
"columnName": "ip",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "port",
"columnName": "port",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "sourcePort",
"columnName": "sourcePort",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "network",
"columnName": "network",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "protocol",
"columnName": "protocol",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "outbound",
"columnName": "outbound",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packages",
"columnName": "packages",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": true,
"columnNames": [
"id"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3d3db9106a89d6f20ef3fde6e81dbaa9')"
]
}
}
================================================
FILE: app/schemas/io.nekohasekai.sagernet.database.preference.PublicDatabase/1.json
================================================
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
]
}
}
================================================
FILE: app/schemas/moe.matsuri.nb4a.TempDatabase/1.json
================================================
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f1aab1fb633378621635c344dbc8ac7b')"
]
}
}
================================================
FILE: app/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:installLocation="internalOnly">
<uses-sdk tools:overrideLibrary="com.google.zxing.client.android, com.blacksquircle.ui.editorkit" />
<permission
android:name="${applicationId}.SERVICE"
android:protectionLevel="signature" />
<uses-permission android:name="${applicationId}.SERVICE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SYSTEM_EXEMPTED" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="PackageVisibilityPolicy" />
<uses-permission android:name="com.android.permission.GET_INSTALLED_APPS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<queries>
<intent>
<action android:name="io.nekohasekai.sagernet.plugin.ACTION_NATIVE_PLUGIN" />
</intent>
</queries>
<application
android:name="io.nekohasekai.sagernet.SagerNet"
android:allowBackup="true"
android:autoRevokePermissions="allowed"
android:banner="@mipmap/ic_launcher"
android:dataExtractionRules="@xml/backup_rules"
android:fullBackupContent="@xml/backup_descriptor"
android:fullBackupOnly="true"
android:hardwareAccelerated="true"
android:hasFragileUserData="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:networkSecurityConfig="@xml/network_security_config"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/Theme.Start">
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
<activity
android:name="io.nekohasekai.sagernet.ui.BlankActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.MainActivity"
android:configChanges="uiMode"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE_PREFERENCES" />
</intent-filter>
<intent-filter android:label="@string/subscription_import">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="subscription"
android:scheme="sn" />
</intent-filter>
<intent-filter android:label="@string/subscription_import">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="install-config"
android:scheme="clash" />
</intent-filter>
<intent-filter android:label="@string/profile_import">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="sn" />
<data android:scheme="ss" />
<data android:scheme="ssr" />
<data android:scheme="socks" />
<data android:scheme="socks4" />
<data android:scheme="socksa" />
<data android:scheme="sock5" />
<data android:scheme="vmess" />
<data android:scheme="trojan" />
<data android:scheme="trojan-go" />
<data android:scheme="naive+https" />
<data android:scheme="naive+quic" />
<data android:scheme="hysteria" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
<activity
android:name="io.nekohasekai.sagernet.ui.VpnRequestActivity"
android:excludeFromRecents="true"
android:taskAffinity="" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.ConfigEditActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.SocksSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.HttpSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.ShadowsocksSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.VMessSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.TrojanSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.TrojanGoSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.MieruSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.NaiveSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.HysteriaSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.SSHSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.WireGuardSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.TuicSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.profile.ChainSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="moe.matsuri.nb4a.proxy.anytls.AnyTLSSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="moe.matsuri.nb4a.proxy.config.ConfigSettingActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.GroupSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.RouteSettingsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.AssetsActivity"
android:configChanges="uiMode" />
<activity
android:name="io.nekohasekai.sagernet.ui.AppListActivity"
android:configChanges="uiMode" />
<activity
android:name=".QuickToggleShortcut"
android:excludeFromRecents="true"
android:exported="true"
android:label="@string/quick_toggle"
android:launchMode="singleTask"
android:process=":bg"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar">
<intent-filter>
<action android:name="android.intent.action.CREATE_SHORTCUT" />
</intent-filter>
</activity>
<activity
android:name="io.nekohasekai.sagernet.ui.QuickEnableShortcut"
android:excludeFromRecents="true"
android:exported="true"
android:label="@string/quick_enable"
android:launchMode="singleTask"
android:process=":bg"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity
android:name="io.nekohasekai.sagernet.ui.QuickDisableShortcut"
android:excludeFromRecents="true"
android:exported="true"
android:label="@string/quick_disable"
android:launchMode="singleTask"
android:process=":bg"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<activity
android:name="io.nekohasekai.sagernet.ui.AppManagerActivity"
android:configChanges="uiMode"
android:excludeFromRecents="true"
android:label="@string/proxied_apps"
android:launchMode="singleTask"
android:parentActivityName=".ui.MainActivity" />
<activity
android:name="io.nekohasekai.sagernet.ui.ScannerActivity"
android:configChanges="uiMode"
android:excludeFromRecents="true"
android:label="@string/add_profile_methods_scan_qr_code"
android:launchMode="singleTask"
android:parentActivityName="io.nekohasekai.sagernet.ui.MainActivity" />
<activity
android:name="io.nekohasekai.sagernet.ui.ProfileSelectActivity"
android:configChanges="uiMode"
android:label="@string/select_profile"
android:launchMode="singleTask"
android:parentActivityName="io.nekohasekai.sagernet.ui.MainActivity" />
<activity
android:name="io.nekohasekai.sagernet.ui.StunActivity"
android:configChanges="uiMode"
android:launchMode="singleTask"
android:parentActivityName="io.nekohasekai.sagernet.ui.MainActivity" />
<activity
android:name="io.nekohasekai.sagernet.ui.SwitchActivity"
android:configChanges="uiMode"
android:excludeFromRecents="true"
android:launchMode="singleInstance"
android:theme="@style/Theme.SagerNet.Dialog" />
<service
android:name="io.nekohasekai.sagernet.bg.ProxyService"
android:exported="false"
android:foregroundServiceType="systemExempted"
android:process=":bg"
tools:ignore="ForegroundServicePermission" />
<service
android:name="io.nekohasekai.sagernet.bg.VpnService"
android:exported="false"
android:foregroundServiceType="systemExempted"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:process=":bg"
tools:ignore="ForegroundServicePermission">
<intent-filter>
<action android:name="android.net.VpnService" />
</intent-filter>
</service>
<service
android:name="io.nekohasekai.sagernet.bg.TileService"
android:exported="true"
android:foregroundServiceType="systemExempted"
android:icon="@drawable/ic_service_active"
android:label="@string/tile_title"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
android:process=":bg"
tools:ignore="ForegroundServicePermission"
tools:targetApi="n">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
<meta-data
android:name="android.service.quicksettings.TOGGLEABLE_TILE"
android:value="true" />
</service>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.cache"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/cache_paths" />
</provider>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
tools:node="remove" />
<receiver
android:name="io.nekohasekai.sagernet.BootReceiver"
android:enabled="false"
android:exported="true"
android:process=":bg">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
<service
android:name="androidx.room.MultiInstanceInvalidationService"
android:process=":bg" />
</application>
</manifest>
================================================
FILE: app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetService.aidl
================================================
package io.nekohasekai.sagernet.aidl;
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback;
interface ISagerNetService {
int getState();
String getProfileName();
void registerCallback(in ISagerNetServiceCallback cb, int id);
oneway void unregisterCallback(in ISagerNetServiceCallback cb);
int urlTest();
}
================================================
FILE: app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetServiceCallback.aidl
================================================
package io.nekohasekai.sagernet.aidl;
import io.nekohasekai.sagernet.aidl.SpeedDisplayData;
import io.nekohasekai.sagernet.aidl.TrafficData;
oneway interface ISagerNetServiceCallback {
void stateChanged(int state, String profileName, String msg);
void missingPlugin(String profileName, String pluginName);
void cbSpeedUpdate(in SpeedDisplayData stats);
void cbTrafficUpdate(in TrafficData stats);
void cbSelectorUpdate(long id);
}
================================================
FILE: app/src/main/aidl/io/nekohasekai/sagernet/aidl/SpeedDisplayData.aidl
================================================
package io.nekohasekai.sagernet.aidl;
parcelable SpeedDisplayData;
================================================
FILE: app/src/main/aidl/io/nekohasekai/sagernet/aidl/TrafficData.aidl
================================================
package io.nekohasekai.sagernet.aidl;
parcelable TrafficData;
================================================
FILE: app/src/main/assets/LICENSE
================================================
Copyright (C) 2021 by nekohasekai
<contact-sagernet@sekai.icu>
This program is free software: you can
redistribute it and/or modify it under
the terms of the GNU General Public License
as published by the Free Software Foundation,
either version 3 of the License,
or (at your option) any later version.
This program is distributed in the hope
that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the
GNU General Public License along with this program.
If not, see <http://www.gnu.org/licenses/>.
================================================
FILE: app/src/main/assets/proxy_packagename.txt
================================================
amanita_design.samorost3.gp
android
au.com.shiftyjelly.pocketcasts
bbc.mobile.news.ww
be.mygod.vpnhotspot
ch.protonmail.android
cm.aptoide.pt
co.wanqu.android
com.alphainventor.filemanager
com.amazon.kindle
com.amazon.mshop.android.shopping
com.android.chrome
com.android.providers.downloads
com.android.providers.downloads.ui
com.android.providers.telephony
com.android.settings
com.android.vending
com.android6park.m6park
com.apkpure.aegon
com.apkupdater
com.app.pornhub
com.arthurivanets.owly
com.asahi.tida.tablet
com.authy.authy
com.avmovie
com.ballistiq.artstation
com.binance.dev
com.bitly.app
com.brave.browser
com.brave.browser_beta
com.breel.wallpapers18
com.bvanced.android.youtube
com.chrome.beta
com.chrome.canary
com.chrome.dev
com.cl.newt66y
com.cradle.iitc_mobile
org.exarhteam.iitc_mobile
com.cygames.shadowverse
com.dcard.freedom
com.devhd.feedly
com.devolver.reigns2
com.discord
com.downloader.video.tumblr
com.driverbrowser
com.dropbox.android
com.duolingo
com.duckduckgo.mobile.android
com.dv.adm
com.estrongs.android.pop
com.estrongs.android.pop.pro
com.evernote
com.facebook.katana
com.facebook.lite
com.facebook.mlite
com.facebook.orca
com.facebook.services
com.facebook.system
com.fastaccess.github
com.felixfilip.scpae
com.fireproofstudios.theroom4
com.firstrowria.pushnotificationtester
com.flyersoft.moonreaderp
com.fooview.android.fooview
com.fvd.eversync
com.gameloft.android.anmp.glofta8hm
com.gameloft.android.anmp.glofta9hm
com.gianlu.aria2app
com.github.yeriomin.yalpstore
com.google.android.apps.adm
com.google.android.apps.books
com.google.android.apps.docs
com.google.android.apps.docs.editors.sheets
com.google.android.apps.docs.editors.docs
com.google.android.apps.docs.editors.slides
com.google.android.apps.fitness
com.google.android.apps.googleassistant
com.google.android.apps.googlevoice
com.google.android.apps.hangoutsdialer
com.google.android.apps.inbox
com.google.android.apps.magazines
com.google.android.apps.maps
com.google.android.apps.nbu.files
com.google.android.apps.paidtasks
com.google.android.apps.pdfviewer
com.google.android.apps.photos
com.google.android.apps.plus
com.google.android.apps.translate
com.google.android.gm
com.google.android.gms
com.google.android.gms.setup
com.google.android.googlequicksearchbox
com.google.android.gsf
com.google.android.gsf.login
com.google.android.ims
com.google.android.inputmethod.latin
com.google.android.instantapps.supervisor
com.google.android.keep
com.google.android.music
com.google.android.ogyoutube
com.google.android.partnersetup
com.google.android.play.games
com.google.android.street
com.google.android.syncadapters.calendar
com.google.android.syncadapters.contacts
com.google.android.talk
com.google.android.tts
com.google.android.videos
com.google.android.youtube
com.google.ar.lens
com.google.android.apps.authenticator2
com.hochan.coldsoup
com.ifttt.ifttt
com.imgur.mobile
com.innologica.inoreader
com.instagram.android
com.instagram.lite
com.instapaper.android
com.jarvanh.vpntether
com.kapp.youtube.final
com.klinker.android.twitter_l
com.lastpass.lpandroid
com.linecorp.linelite
com.lingodeer
com.ltnnews.news
com.mediapods.tumbpods
com.mgoogle.android.gms
com.microsoft.emmx
com.microsoft.office.powerpoint
com.microsoft.skydrive
com.mixplorer
com.msd.consumerchinese
com.msd.professionalchinese
com.mss2011c.sharehelper
com.netflix.mediaclient
com.newin.nplayer.pro
com.nianticlabs.ingress.prime.qa
com.nianticproject.ingress
com.ninefolders.hd3
com.ninegag.android.app
com.nintendo.zara
com.nytimes.cn
com.oasisfeng.island
com.ocnt.liveapp.hw
com.orekie.search
com.patreon.android
com.paypal.android.p2pmobile
com.perol.asdpl.pixivez
com.pinterest
com.popularapp.periodcalendar
com.popularapp.videodownloaderforinstagram
com.pushbullet.android
com.quoord.tapatalkpro.activity
com.quora.android
com.rayark.cytus2
com.rayark.implosion
com.rayark.pluto
com.reddit.frontpage
com.resilio.sync
com.rhmsoft.edit
com.rubenmayayo.reddit
com.sec.android.app.sbrowser
com.sec.android.app.sbrowser.beta
com.shanga.walli
com.simplehabit.simplehabitapp
com.slack
com.snaptube.premium
com.sololearn
com.sonelli.juicessh
com.sparkslab.dcardreader
com.spotify.music
com.spotify.lite
com.tencent.huatuo
com.termux
com.teslacoilsw.launcher
com.theinitium.news
com.thomsonreuters.reuters
com.thunkable.android.hritvik00.freenom
com.topjohnwu.magisk
com.tripadvisor.tripadvisor
com.tumblr
com.twitter.android
com.u91porn
com.u9porn
com.ubisoft.dance.justdance2015companion
com.udn.news
com.utopia.pxview
com.valvesoftware.android.steam.community
com.vanced.manager
com.vanced.android.youtube
com.vanced.android.apps.youtube.music
com.mgoogle.android.gms
com.vimeo.android.videoapp
com.vivaldi.browser
com.vivaldi.browser.snapshot
com.vkontakte.android
com.whatsapp
com.wire
com.wuxiangai.refactor
com.xda.labs
com.xvideos.app
com.yahoo.mobile.client.android.superapp
com.yandex.browser
com.yandex.browser.beta
com.yandex.browser.alpha
com.z28j.feel
com.zhiliaoapp.musically
con.medium.reader
de.apkgrabber
de.robv.android.xposed.installer
dk.tacit.android.foldersync.full
es.rafalense.telegram.themes
es.rafalense.themes
flipboard.app
fm.moon.app
fr.gouv.etalab.mastodon
github.tornaco.xposedmoduletest
idm.internet.download.manager
idm.internet.download.manager.plus
io.github.javiewer
io.github.skyhacker2.magnetsearch
io.va.exposed
it.mvilla.android.fenix2
jp.bokete.app.android
jp.naver.line.android
jp.pxv.android
luo.speedometergpspro
m.cna.com.tw.App
mark.via.gp
me.tshine.easymark
net.teeha.android.url_shortener
net.tsapps.appsales
onion.fire
org.fdroid.fdroid
org.freedownloadmanager.fdm
org.kustom.widget
org.mozilla.fennec_aurora
org.mozilla.fenix
org.mozilla.fenix.nightly
org.mozilla.firefox
org.mozilla.firefox_beta
org.mozilla.focus
org.schabi.newpipe
org.telegram.messenger
org.telegram.multi
org.telegram.plus
org.thunderdog.challegram
org.torproject.android
org.torproject.torbrowser_alpha
org.wikipedia
org.xbmc.kodi
pl.zdunex25.updater
tv.twitch.android.app
tw.com.gamer.android.activecenter
videodownloader.downloadvideo.downloader
uk.co.bbc.learningenglish
com.ted.android
de.danoeh.antennapod
com.kiwibrowser.browser
nekox.messenger
com.nextcloud.client
com.aurora.store
com.aurora.adroid
chat.simplex.app
im.vector.app
network.loki.messenger
eu.siacs.conversations
xyz.nextalone.nagram
de.danoeh.antennapod
net.programmierecke.radiodroid2
im.fdx.v2ex
ml.docilealligator.infinityforreddit
com.bytemyth.ama
app.vanadium.browser
com.cakewallet.cake_wallet
org.purplei2p.i2pd
dk.tacit.android.foldersync.lite
com.nononsenseapps.feeder
com.m2049r.xmrwallet
com.paypal.android.p2pmobile
com.google.android.apps.googlevoice
com.readdle.spark
org.torproject.torbrowser
com.deepl.mobiletranslator
com.microsoft.bing
com.keylesspalace.tusky
com.ottplay.ottplay
ru.iptvremote.android.iptv.pro
jp.naver.line.android
com.xmflsct.app.tooot
com.forem.android
app.revanced.android.youtube
app.rvx.android.youtube
app.rvx.android.apps.youtube.music
com.mgoogle.android.gms
com.pionex.client
vip.mytokenpocket
im.token.app
com.linekong.mars24
com.feixiaohao
com.aicoin.appandroid
com.binance.dev
com.kraken.trade
com.okinc.okex.gp
com.authy.authy
air.com.rosettastone.mobile.CoursePlayer
com.blizzard.bma
com.amazon.kindle
com.google.android.apps.fitness
net.tsapps.appsales
com.wemesh.android
com.google.android.apps.googleassistant
allen.town.focus.reader
me.hyliu.fluent_reader_lite
com.aljazeera.mobile
com.ft.news
de.marmaro.krt.ffupdater
myradio.radio.fmradio.liveradio.radiostation
com.google.earth
eu.kanade.tachiyomi.j2k
com.audials
com.microsoft.skydrive
com.mb.android.tg
com.melodis.midomiMusicIdentifier.freemium
com.foxnews.android
ch.threema.app
com.briarproject.briar.android
foundation.e.apps
com.valvesoftware.android.steam.friendsui
com.imback.yeetalk
so.onekey.app.wallet
com.xc3fff0e.xmanager
meditofoundation.medito
com.picol.client
com.streetwriters.notesnook
shanghai.panewsApp.com
org.coursera.android
com.positron_it.zlib
com.blizzard.messenger
com.javdb.javrocket
com.picacomic.fregata
com.fxl.chacha
me.proton.android.drive
com.lastpass.lpandroid
com.tradingview.tradingviewapp
com.deviantart.android.damobile
com.fusionmedia.investing
com.ewa.ewaapp
com.duolingo
com.hellotalk
io.github.huskydg.magisk
com.jsy.xpgbox
com.hostloc.app.hostloc
com.dena.pokota
com.vitorpamplona.amethyst
com.zhiliaoapp.musically
us.spotco.fennec_dos
com.fongmi.android.tv
com.pocketprep.android.itcybersecurity
com.cloudtv
com.glassdoor.app
com.indeed.android.jobsearch
com.linkedin.android
com.github.tvbox.osc.bh
com.example.douban
com.sipnetic.app
com.microsoft.rdc.androidx
org.zwanoo.android.speedtest
com.sonelli.juicessh
com.scmp.newspulse
org.lsposed.manager
mnn.Android
com.thomsonretuers.reuters
com.guardian
com.ttxapps.onesyncv2
org.fcitx.fcitx5.android.updater
com.instagram.barcelona
com.deniscerri.ytdl
jp.pokemon.pokemonsleep
com.github.android
com.openai.chatgpt
mega.privacy.android.app
com.taptap.global
tw.com.gamer.android.animad
com.microsoft.copilot
com.google.android.apps.aiwallpapers
ai.x.grok
com.google.android.apps.weather
com.metrolist.music
com.google.android.apps.youtube.creator
================================================
FILE: app/src/main/assets/yacd.version.txt
================================================
3
================================================
FILE: app/src/main/java/com/github/shadowsocks/plugin/Utils.kt
================================================
@file:JvmName("Utils")
package com.github.shadowsocks.plugin
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
class Empty : Parcelable
================================================
FILE: app/src/main/java/com/github/shadowsocks/plugin/fragment/AlertDialogFragment.kt
================================================
package com.github.shadowsocks.plugin.fragment
import android.app.Activity
import android.content.DialogInterface
import android.os.Bundle
import android.os.Parcelable
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.Fragment
import androidx.fragment.app.setFragmentResult
import androidx.fragment.app.setFragmentResultListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder
/**
* Based on: https://android.googlesource.com/platform/
packages/apps/ExactCalculator/+/8c43f06/src/com/android/calculator2/AlertDialogFragment.java
*/
abstract class AlertDialogFragment<Arg : Parcelable, Ret : Parcelable?> :
AppCompatDialogFragment(), DialogInterface.OnClickListener {
companion object {
private const val KEY_RESULT = "result"
private const val KEY_ARG = "arg"
private const val KEY_RET = "ret"
private const val KEY_WHICH = "which"
fun <Ret : Parcelable> setResultListener(fragment: Fragment, requestKey: String,
listener: (Int, Ret?) -> Unit) {
fragment.setFragmentResultListener(requestKey) { _, bundle ->
listener(bundle.getInt(KEY_WHICH, Activity.RESULT_CANCELED), bundle.getParcelable(KEY_RET))
}
}
inline fun <reified T : AlertDialogFragment<*, Ret>, Ret : Parcelable?> setResultListener(
fragment: Fragment, noinline listener: (Int, Ret?) -> Unit) =
setResultListener(fragment, T::class.java.name, listener)
}
protected abstract fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener)
private val resultKey get() = requireArguments().getString(KEY_RESULT)
protected val arg by lazy { requireArguments().getParcelable<Arg>(KEY_ARG)!! }
protected open fun ret(which: Int): Ret? = null
private fun args() = arguments ?: Bundle().also { arguments = it }
fun arg(arg: Arg) = args().putParcelable(KEY_ARG, arg)
fun key(resultKey: String = javaClass.name) = args().putString(KEY_RESULT, resultKey)
override fun onCreateDialog(savedInstanceState: Bundle?): AlertDialog =
MaterialAlertDialogBuilder(requireContext()).also { it.prepare(this) }.create()
override fun onClick(dialog: DialogInterface?, which: Int) {
setFragmentResult(resultKey ?: return, Bundle().apply {
putInt(KEY_WHICH, which)
putParcelable(KEY_RET, ret(which) ?: return@apply)
})
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
onClick(null, Activity.RESULT_CANCELED)
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/BootReceiver.kt
================================================
package io.nekohasekai.sagernet
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import io.nekohasekai.sagernet.bg.SubscriptionUpdater
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
class BootReceiver : BroadcastReceiver() {
companion object {
private val componentName by lazy { ComponentName(app, BootReceiver::class.java) }
var enabled: Boolean
get() = app.packageManager.getComponentEnabledSetting(componentName) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
set(value) = app.packageManager.setComponentEnabledSetting(
componentName, if (value) PackageManager.COMPONENT_ENABLED_STATE_ENABLED
else PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP
)
}
override fun onReceive(context: Context, intent: Intent) {
runOnDefaultDispatcher {
SubscriptionUpdater.reconfigureUpdater()
}
if (!DataStore.persistAcrossReboot) { // sanity check
enabled = false
return
}
val doStart = when (intent.action) {
Intent.ACTION_LOCKED_BOOT_COMPLETED -> false // DataStore.directBootAware
else -> Build.VERSION.SDK_INT < 24 || SagerNet.user.isUserUnlocked
} && DataStore.selectedProxy > 0
if (doStart) SagerNet.startService()
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/Constants.kt
================================================
package io.nekohasekai.sagernet
const val CONNECTION_TEST_URL = "http://cp.cloudflare.com/"
object Key {
const val DB_PUBLIC = "configuration.db"
const val DB_PROFILE = "sager_net.db"
const val PERSIST_ACROSS_REBOOT = "isAutoConnect"
const val APP_EXPERT = "isExpert"
const val APP_THEME = "appTheme"
const val NIGHT_THEME = "nightTheme"
const val SERVICE_MODE = "serviceMode"
const val MODE_VPN = "vpn"
const val MODE_PROXY = "proxy"
const val GLOBAL_CUSTOM_CONFIG = "globalCustomConfig"
const val REMOTE_DNS = "remoteDns"
const val DIRECT_DNS = "directDns"
const val ENABLE_DNS_ROUTING = "enableDnsRouting"
const val ENABLE_FAKEDNS = "enableFakeDns"
const val IPV6_MODE = "ipv6Mode"
const val PROXY_APPS = "proxyApps"
const val BYPASS_MODE = "bypassMode"
const val INDIVIDUAL = "individual"
const val METERED_NETWORK = "meteredNetwork"
const val TRAFFIC_SNIFFING = "trafficSniffing"
const val RESOLVE_DESTINATION = "resolveDestination"
const val BYPASS_LAN = "bypassLan"
const val BYPASS_LAN_IN_CORE = "bypassLanInCore"
const val MIXED_PORT = "mixedPort"
const val ALLOW_ACCESS = "allowAccess"
const val SPEED_INTERVAL = "speedInterval"
const val SHOW_DIRECT_SPEED = "showDirectSpeed"
const val APPEND_HTTP_PROXY = "appendHttpProxy"
const val CONNECTION_TEST_URL = "connectionTestURL"
const val NETWORK_CHANGE_RESET_CONNECTIONS = "networkChangeResetConnections"
const val WAKE_RESET_CONNECTIONS = "wakeResetConnections"
const val RULES_PROVIDER = "rulesProvider"
const val LOG_LEVEL = "logLevel"
const val LOG_BUF_SIZE = "logBufSize"
const val MTU = "mtu"
const val ALWAYS_SHOW_ADDRESS = "alwaysShowAddress"
// Protocol Settings
const val GLOBAL_ALLOW_INSECURE = "globalAllowInsecure"
const val ACQUIRE_WAKE_LOCK = "acquireWakeLock"
const val SHOW_BOTTOM_BAR = "showBottomBar"
const val ALLOW_INSECURE_ON_REQUEST = "allowInsecureOnRequest"
const val TUN_IMPLEMENTATION = "tunImplementation"
const val PROFILE_TRAFFIC_STATISTICS = "profileTrafficStatistics"
const val PROFILE_DIRTY = "profileDirty"
const val PROFILE_ID = "profileId"
const val PROFILE_NAME = "profileName"
const val PROFILE_GROUP = "profileGroup"
const val PROFILE_CURRENT = "profileCurrent"
const val SERVER_ADDRESS = "serverAddress"
const val SERVER_PORT = "serverPort"
const val SERVER_USERNAME = "serverUsername"
const val SERVER_PASSWORD = "serverPassword"
const val SERVER_METHOD = "serverMethod"
const val SERVER_PASSWORD1 = "serverPassword1"
const val PROTOCOL_VERSION = "protocolVersion"
const val SERVER_PROTOCOL = "serverProtocol"
const val SERVER_OBFS = "serverObfs"
const val SERVER_NETWORK = "serverNetwork"
const val SERVER_HOST = "serverHost"
const val SERVER_PATH = "serverPath"
const val SERVER_SNI = "serverSNI"
const val SERVER_ENCRYPTION = "serverEncryption"
const val SERVER_ALPN = "serverALPN"
const val SERVER_CERTIFICATES = "serverCertificates"
const val SERVER_MTU = "serverMTU"
const val SERVER_CONFIG = "serverConfig"
const val SERVER_CUSTOM = "serverCustom"
const val SERVER_CUSTOM_OUTBOUND = "serverCustomOutbound"
const val SERVER_SECURITY_CATEGORY = "serverSecurityCategory"
const val SERVER_TLS_CAMOUFLAGE_CATEGORY = "serverTlsCamouflageCategory"
const val SERVER_ECH_CATEORY = "serverECHCategory"
const val SERVER_WS_CATEGORY = "serverWsCategory"
const val SERVER_SS_CATEGORY = "serverSsCategory"
const val SERVER_HEADERS = "serverHeaders"
const val SERVER_ALLOW_INSECURE = "serverAllowInsecure"
const val SERVER_AUTH_TYPE = "serverAuthType"
const val SERVER_UPLOAD_SPEED = "serverUploadSpeed"
const val SERVER_DOWNLOAD_SPEED = "serverDownloadSpeed"
const val SERVER_STREAM_RECEIVE_WINDOW = "serverStreamReceiveWindow"
const val SERVER_CONNECTION_RECEIVE_WINDOW = "serverConnectionReceiveWindow"
const val SERVER_DISABLE_MTU_DISCOVERY = "serverDisableMtuDiscovery"
const val SERVER_HOP_INTERVAL = "hopInterval"
const val SERVER_PRIVATE_KEY = "serverPrivateKey"
const val SERVER_INSECURE_CONCURRENCY = "serverInsecureConcurrency"
const val SERVER_UDP_RELAY_MODE = "serverUDPRelayMode"
const val SERVER_CONGESTION_CONTROLLER = "serverCongestionController"
const val SERVER_DISABLE_SNI = "serverDisableSNI"
const val SERVER_REDUCE_RTT = "serverReduceRTT"
const val ROUTE_NAME = "routeName"
const val ROUTE_DOMAIN = "routeDomain"
const val ROUTE_IP = "routeIP"
const val ROUTE_PORT = "routePort"
const val ROUTE_SOURCE_PORT = "routeSourcePort"
const val ROUTE_NETWORK = "routeNetwork"
const val ROUTE_SOURCE = "routeSource"
const val ROUTE_PROTOCOL = "routeProtocol"
const val ROUTE_OUTBOUND = "routeOutbound"
const val ROUTE_PACKAGES = "routePackages"
const val GROUP_NAME = "groupName"
const val GROUP_TYPE = "groupType"
const val GROUP_ORDER = "groupOrder"
const val GROUP_IS_SELECTOR = "groupIsSelector"
const val GROUP_FRONT_PROXY = "groupFrontProxy"
const val GROUP_LANDING_PROXY = "groupLandingProxy"
const val GROUP_SUBSCRIPTION = "groupSubscription"
const val SUBSCRIPTION_LINK = "subscriptionLink"
const val SUBSCRIPTION_FORCE_RESOLVE = "subscriptionForceResolve"
const val SUBSCRIPTION_DEDUPLICATION = "subscriptionDeduplication"
const val SUBSCRIPTION_UPDATE = "subscriptionUpdate"
const val SUBSCRIPTION_UPDATE_WHEN_CONNECTED_ONLY = "subscriptionUpdateWhenConnectedOnly"
const val SUBSCRIPTION_USER_AGENT = "subscriptionUserAgent"
const val SUBSCRIPTION_AUTO_UPDATE = "subscriptionAutoUpdate"
const val SUBSCRIPTION_AUTO_UPDATE_DELAY = "subscriptionAutoUpdateDelay"
//
const val APP_TLS_VERSION = "appTLSVersion"
const val ENABLE_CLASH_API = "enableClashAPI"
}
object TunImplementation {
const val GVISOR = 0
const val SYSTEM = 1
const val MIXED = 2
}
object IPv6Mode {
const val DISABLE = 0
const val ENABLE = 1
const val PREFER = 2
const val ONLY = 3
}
object GroupType {
const val BASIC = 0
const val SUBSCRIPTION = 1
}
object GroupOrder {
const val ORIGIN = 0
const val BY_NAME = 1
const val BY_DELAY = 2
}
object Action {
const val SERVICE = "io.nekohasekai.sagernet.SERVICE"
const val CLOSE = "io.nekohasekai.sagernet.CLOSE"
const val RELOAD = "io.nekohasekai.sagernet.RELOAD"
// const val SWITCH_WAKE_LOCK = "io.nekohasekai.sagernet.SWITCH_WAKELOCK"
const val RESET_UPSTREAM_CONNECTIONS = "moe.nb4a.RESET_UPSTREAM_CONNECTIONS"
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/QuickToggleShortcut.kt
================================================
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package io.nekohasekai.sagernet
import android.app.Activity
import android.content.Intent
import android.content.pm.ShortcutManager
import android.os.Build
import android.os.Bundle
import androidx.core.content.getSystemService
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import io.nekohasekai.sagernet.aidl.ISagerNetService
import io.nekohasekai.sagernet.bg.BaseService
import io.nekohasekai.sagernet.bg.SagerConnection
import io.nekohasekai.sagernet.database.DataStore
@Suppress("DEPRECATION")
class QuickToggleShortcut : Activity(), SagerConnection.Callback {
private val connection = SagerConnection(SagerConnection.CONNECTION_ID_SHORTCUT)
private var profileId = -1L
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (intent.action == Intent.ACTION_CREATE_SHORTCUT) {
setResult(RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this,
ShortcutInfoCompat.Builder(this, "toggle")
.setIntent(Intent(this,
QuickToggleShortcut::class.java).setAction(Intent.ACTION_MAIN))
.setIcon(IconCompat.createWithResource(this,
R.drawable.ic_qu_shadowsocks_launcher))
.setShortLabel(getString(R.string.quick_toggle))
.build()))
finish()
} else {
profileId = intent.getLongExtra("profile", -1L)
connection.connect(this, this)
if (Build.VERSION.SDK_INT >= 25) {
getSystemService<ShortcutManager>()!!.reportShortcutUsed(if (profileId >= 0) "shortcut-profile-$profileId" else "toggle")
}
}
}
override fun onServiceConnected(service: ISagerNetService) {
val state = BaseService.State.values()[service.state]
when {
state.canStop -> {
if (profileId == DataStore.selectedProxy || profileId == -1L) {
SagerNet.stopService()
} else {
DataStore.selectedProxy = profileId
SagerNet.reloadService()
}
}
state == BaseService.State.Stopped -> {
if (profileId >= 0L) DataStore.selectedProxy = profileId
SagerNet.startService()
}
}
finish()
}
override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) {}
override fun onDestroy() {
connection.disconnect(this)
super.onDestroy()
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt
================================================
package io.nekohasekai.sagernet
import android.annotation.SuppressLint
import android.app.*
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.ConnectivityManager
import android.net.Network
import android.os.Build
import android.os.PowerManager
import android.os.StrictMode
import android.os.UserManager
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import go.Seq
import io.nekohasekai.sagernet.bg.SagerConnection
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.ktx.isOss
import io.nekohasekai.sagernet.ktx.isPreview
import io.nekohasekai.sagernet.ktx.runOnDefaultDispatcher
import io.nekohasekai.sagernet.ui.MainActivity
import io.nekohasekai.sagernet.utils.*
import kotlinx.coroutines.DEBUG_PROPERTY_NAME
import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON
import libcore.Libcore
import moe.matsuri.nb4a.NativeInterface
import moe.matsuri.nb4a.net.LocalResolverImpl
import moe.matsuri.nb4a.utils.JavaUtil
import moe.matsuri.nb4a.utils.cleanWebview
import java.io.File
import androidx.work.Configuration as WorkConfiguration
class SagerNet : Application(),
WorkConfiguration.Provider {
override fun attachBaseContext(base: Context) {
super.attachBaseContext(base)
application = this
}
private val nativeInterface = NativeInterface()
val externalAssets: File by lazy { getExternalFilesDir(null) ?: filesDir }
val process: String = JavaUtil.getProcessName()
private val isMainProcess = process == BuildConfig.APPLICATION_ID
val isBgProcess = process.endsWith(":bg")
override fun onCreate() {
super.onCreate()
Thread.setDefaultUncaughtExceptionHandler(CrashHandler)
if (isMainProcess || isBgProcess) {
externalAssets.mkdirs()
Seq.setContext(this)
Libcore.initCore(
process,
cacheDir.absolutePath + "/",
filesDir.absolutePath + "/",
externalAssets.absolutePath + "/",
DataStore.logBufSize,
DataStore.logLevel > 0,
nativeInterface, nativeInterface, LocalResolverImpl
)
// fix multi process issue in Android 9+
JavaUtil.handleWebviewDir(this)
runOnDefaultDispatcher {
PackageCache.register()
cleanWebview()
}
}
if (isMainProcess) {
Theme.apply(this)
Theme.applyNightTheme()
runOnDefaultDispatcher {
DefaultNetworkListener.start(this) {
underlyingNetwork = it
}
updateNotificationChannels()
}
}
if (BuildConfig.DEBUG) {
System.setProperty(DEBUG_PROPERTY_NAME, DEBUG_PROPERTY_VALUE_ON)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.detectLeakedRegistrationObjects()
.penaltyLog()
.build()
)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
updateNotificationChannels()
}
override fun getWorkManagerConfiguration(): WorkConfiguration {
return WorkConfiguration.Builder()
.setDefaultProcessName("${BuildConfig.APPLICATION_ID}:bg")
.build()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
Libcore.forceGc()
}
@SuppressLint("InlinedApi")
companion object {
lateinit var application: SagerNet
val isTv by lazy {
uiMode.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION
}
val configureIntent: (Context) -> PendingIntent by lazy {
{
PendingIntent.getActivity(
it,
0,
Intent(
application, MainActivity::class.java
).setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
)
}
}
val activity by lazy { application.getSystemService<ActivityManager>()!! }
val clipboard by lazy { application.getSystemService<ClipboardManager>()!! }
val connectivity by lazy { application.getSystemService<ConnectivityManager>()!! }
val notification by lazy { application.getSystemService<NotificationManager>()!! }
val user by lazy { application.getSystemService<UserManager>()!! }
val uiMode by lazy { application.getSystemService<UiModeManager>()!! }
val power by lazy { application.getSystemService<PowerManager>()!! }
fun getClipboardText(): String {
return clipboard.primaryClip?.takeIf { it.itemCount > 0 }
?.getItemAt(0)?.text?.toString() ?: ""
}
fun trySetPrimaryClip(clip: String) = try {
clipboard.setPrimaryClip(ClipData.newPlainText(null, clip))
true
} catch (e: RuntimeException) {
Logs.w(e)
false
}
fun updateNotificationChannels() {
if (Build.VERSION.SDK_INT >= 26) @RequiresApi(26) {
notification.createNotificationChannels(
listOf(
NotificationChannel(
"service-vpn",
application.getText(R.string.service_vpn),
if (Build.VERSION.SDK_INT >= 28) NotificationManager.IMPORTANCE_MIN
else NotificationManager.IMPORTANCE_LOW
), // #1355
NotificationChannel(
"service-proxy",
application.getText(R.string.service_proxy),
NotificationManager.IMPORTANCE_LOW
), NotificationChannel(
"service-subscription",
application.getText(R.string.service_subscription),
NotificationManager.IMPORTANCE_DEFAULT
), NotificationChannel(
"connection-test",
application.getText(R.string.connection_test),
NotificationManager.IMPORTANCE_DEFAULT
)
)
)
}
}
fun startService() = ContextCompat.startForegroundService(
application, Intent(application, SagerConnection.serviceClass)
)
fun reloadService() =
application.sendBroadcast(Intent(Action.RELOAD).setPackage(application.packageName))
fun stopService() =
application.sendBroadcast(Intent(Action.CLOSE).setPackage(application.packageName))
var underlyingNetwork: Network? = null
var appVersionNameForDisplay = {
var n = BuildConfig.VERSION_NAME
if (isPreview) {
n += " " + BuildConfig.PRE_VERSION_NAME
} else if (!isOss) {
n += " ${BuildConfig.FLAVOR}"
}
if (BuildConfig.DEBUG) {
n += " DEBUG"
}
n
}()
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/aidl/SpeedDisplayData.kt
================================================
package io.nekohasekai.sagernet.aidl
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class SpeedDisplayData(
// Bytes per second
var txRateProxy: Long = 0L,
var rxRateProxy: Long = 0L,
var txRateDirect: Long = 0L,
var rxRateDirect: Long = 0L,
// Bytes for the current session
// Outbound "bypass" usage is not counted
var txTotal: Long = 0L,
var rxTotal: Long = 0L,
) : Parcelable
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/aidl/TrafficData.kt
================================================
package io.nekohasekai.sagernet.aidl
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
data class TrafficData(
var id: Long = 0L,
var tx: Long = 0L,
var rx: Long = 0L,
) : Parcelable
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/AbstractInstance.kt
================================================
package io.nekohasekai.sagernet.bg
import java.io.Closeable
interface AbstractInstance : Closeable {
fun launch()
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
================================================
package io.nekohasekai.sagernet.bg
import android.app.Service
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.*
import android.widget.Toast
import io.nekohasekai.sagernet.Action
import io.nekohasekai.sagernet.BootReceiver
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.aidl.ISagerNetService
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
import io.nekohasekai.sagernet.bg.proto.ProxyInstance
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.plugin.PluginManager
import io.nekohasekai.sagernet.utils.DefaultNetworkListener
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import libcore.Libcore
import moe.matsuri.nb4a.Protocols
import moe.matsuri.nb4a.utils.Util
import java.net.UnknownHostException
class BaseService {
enum class State(
val canStop: Boolean = false,
val started: Boolean = false,
val connected: Boolean = false,
) {
/**
* Idle state is only used by UI and will never be returned by BaseService.
*/
Idle, Connecting(true, true, false), Connected(true, true, true), Stopping, Stopped,
}
interface ExpectedException
class Data internal constructor(private val service: Interface) {
var state = State.Stopped
var proxy: ProxyInstance? = null
var notification: ServiceNotification? = null
val receiver = broadcastReceiver { ctx, intent ->
when (intent.action) {
Intent.ACTION_SHUTDOWN -> service.persistStats()
Action.RELOAD -> service.reload()
// Action.SWITCH_WAKE_LOCK -> runOnDefaultDispatcher { service.switchWakeLock() }
PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (SagerNet.power.isDeviceIdleMode) {
proxy?.box?.sleep()
} else {
proxy?.box?.wake()
if (DataStore.wakeResetConnections) {
Libcore.resetAllConnections(true)
}
}
}
}
Action.RESET_UPSTREAM_CONNECTIONS -> runOnDefaultDispatcher {
Libcore.resetAllConnections(true)
runOnMainDispatcher {
Util.collapseStatusBar(ctx)
Toast.makeText(ctx, "Reset upstream connections done", Toast.LENGTH_SHORT)
.show()
}
}
else -> service.stopRunner()
}
}
var closeReceiverRegistered = false
val binder = Binder(this)
var connectingJob: Job? = null
fun changeState(s: State, msg: String? = null) {
if (state == s && msg == null) return
state = s
DataStore.serviceState = s
binder.stateChanged(s, msg)
}
}
class Binder(private var data: Data? = null) : ISagerNetService.Stub(), CoroutineScope,
AutoCloseable {
private val callbacks = object : RemoteCallbackList<ISagerNetServiceCallback>() {
override fun onCallbackDied(callback: ISagerNetServiceCallback?, cookie: Any?) {
super.onCallbackDied(callback, cookie)
}
}
val callbackIdMap = mutableMapOf<ISagerNetServiceCallback, Int>()
override val coroutineContext = Dispatchers.Main.immediate + Job()
override fun getState(): Int = (data?.state ?: State.Idle).ordinal
override fun getProfileName(): String = data?.proxy?.displayProfileName ?: "Idle"
override fun registerCallback(cb: ISagerNetServiceCallback, id: Int) {
if (id == SagerConnection.CONNECTION_ID_RESTART_BG) {
Runtime.getRuntime().exit(0)
return
}
if (!callbackIdMap.contains(cb)) {
callbacks.register(cb)
}
callbackIdMap[cb] = id
}
private val broadcastMutex = Mutex()
suspend fun broadcast(work: (ISagerNetServiceCallback) -> Unit) {
broadcastMutex.withLock {
val count = callbacks.beginBroadcast()
try {
repeat(count) {
try {
work(callbacks.getBroadcastItem(it))
} catch (_: RemoteException) {
} catch (_: Exception) {
}
}
} finally {
callbacks.finishBroadcast()
}
}
}
override fun unregisterCallback(cb: ISagerNetServiceCallback) {
callbackIdMap.remove(cb)
callbacks.unregister(cb)
}
override fun urlTest(): Int {
if (data?.proxy?.box == null) {
error("core not started")
}
try {
return Libcore.urlTest(
data!!.proxy!!.box, DataStore.connectionTestURL, 3000
)
} catch (e: Exception) {
error(Protocols.genFriendlyMsg(e.readableMessage))
}
}
fun stateChanged(s: State, msg: String?) = launch {
val profileName = profileName
broadcast { it.stateChanged(s.ordinal, profileName, msg) }
}
fun missingPlugin(pluginName: String) = launch {
val profileName = profileName
broadcast { it.missingPlugin(profileName, pluginName) }
}
override fun close() {
callbacks.kill()
cancel()
data = null
}
}
interface Interface {
val data: Data
val tag: String
fun createNotification(profileName: String): ServiceNotification
fun onBind(intent: Intent): IBinder? =
if (intent.action == Action.SERVICE) data.binder else null
fun reload() {
if (DataStore.selectedProxy == 0L) {
stopRunner(false, (this as Context).getString(R.string.profile_empty))
}
if (canReloadSelector()) {
val ent = SagerDatabase.proxyDao.getById(DataStore.selectedProxy)
val tag = data.proxy!!.config.profileTagMap[ent?.id] ?: ""
if (tag.isNotBlank() && ent != null) {
// select from GUI
data.proxy!!.box.selectOutbound(tag)
// or select from webui
// => selector_OnProxySelected
}
return
}
val s = data.state
when {
s == State.Stopped -> startRunner()
s.canStop -> stopRunner(true)
else -> Logs.w("Illegal state $s when invoking use")
}
}
fun canReloadSelector(): Boolean {
if ((data.proxy?.config?.selectorGroupId ?: -1L) < 0) return false
val ent = SagerDatabase.proxyDao.getById(DataStore.selectedProxy) ?: return false
val tmpBox = ProxyInstance(ent)
tmpBox.buildConfigTmp()
if (tmpBox.lastSelectorGroupId == data.proxy?.lastSelectorGroupId) {
return true
}
return false
}
suspend fun startProcesses() {
data.proxy!!.launch()
}
fun startRunner() {
this as Context
if (Build.VERSION.SDK_INT >= 26) startForegroundService(Intent(this, javaClass))
else startService(Intent(this, javaClass))
}
fun killProcesses() {
data.proxy?.close()
wakeLock?.apply {
release()
wakeLock = null
}
runOnDefaultDispatcher {
DefaultNetworkListener.stop(this)
}
}
fun stopRunner(restart: Boolean = false, msg: String? = null) {
DataStore.baseService = null
DataStore.vpnService = null
if (data.state == State.Stopping) return
data.notification?.destroy()
data.notification = null
this as Service
data.changeState(State.Stopping)
runOnMainDispatcher {
data.connectingJob?.cancelAndJoin() // ensure stop connecting first
// we use a coroutineScope here to allow clean-up in parallel
coroutineScope {
killProcesses()
val data = data
if (data.closeReceiverRegistered) {
unregisterReceiver(data.receiver)
data.closeReceiverRegistered = false
}
data.proxy = null
}
// change the state
data.changeState(State.Stopped, msg)
// stop the service if nothing has bound to it
if (restart) startRunner() else {
stopSelf()
}
}
}
fun persistStats() {
// TODO NEW save app stats?
}
// networks
var upstreamInterfaceName: String?
suspend fun preInit() {
DefaultNetworkListener.start(this) {
SagerNet.connectivity.getLinkProperties(it)?.also { link ->
SagerNet.underlyingNetwork = it
DataStore.vpnService?.updateUnderlyingNetwork()
//
val oldName = upstreamInterfaceName
if (oldName != link.interfaceName) {
upstreamInterfaceName = link.interfaceName
}
if (oldName != null && upstreamInterfaceName != null && oldName != upstreamInterfaceName) {
Logs.d("Network changed: $oldName -> $upstreamInterfaceName")
if (DataStore.networkChangeResetConnections) {
Libcore.resetAllConnections(true)
}
}
}
}
}
var wakeLock: PowerManager.WakeLock?
fun acquireWakeLock()
suspend fun lateInit() {
wakeLock?.apply {
release()
wakeLock = null
}
if (DataStore.acquireWakeLock) {
acquireWakeLock()
data.notification?.postNotificationWakeLockStatus(true)
} else {
data.notification?.postNotificationWakeLockStatus(false)
}
}
fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
DataStore.baseService = this
val data = data
if (data.state != State.Stopped) return Service.START_NOT_STICKY
val profile = SagerDatabase.proxyDao.getById(DataStore.selectedProxy)
this as Context
if (profile == null) { // gracefully shutdown: https://stackoverflow.com/q/47337857/2245107
data.notification = createNotification("")
stopRunner(false, getString(R.string.profile_empty))
return Service.START_NOT_STICKY
}
val proxy = ProxyInstance(profile, this)
data.proxy = proxy
BootReceiver.enabled = DataStore.persistAcrossReboot
if (!data.closeReceiverRegistered) {
val filter = IntentFilter().apply {
addAction(Action.RELOAD)
addAction(Intent.ACTION_SHUTDOWN)
addAction(Action.CLOSE)
// addAction(Action.SWITCH_WAKE_LOCK)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)
}
addAction(Action.RESET_UPSTREAM_CONNECTIONS)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(
data.receiver,
filter,
"$packageName.SERVICE",
null,
Context.RECEIVER_EXPORTED
)
} else {
registerReceiver(
data.receiver,
filter,
"$packageName.SERVICE",
null
)
}
data.closeReceiverRegistered = true
}
data.changeState(State.Connecting)
runOnMainDispatcher {
try {
data.notification = createNotification(ServiceNotification.genTitle(profile))
Executable.killAll() // clean up old processes
preInit()
proxy.init()
DataStore.currentProfile = profile.id
proxy.processes = GuardedProcessPool {
Logs.w(it)
stopRunner(false, it.readableMessage)
}
startProcesses()
data.changeState(State.Connected)
lateInit()
} catch (_: CancellationException) { // if the job was cancelled, it is canceller's responsibility to call stopRunner
} catch (_: UnknownHostException) {
stopRunner(false, getString(R.string.invalid_server))
} catch (e: PluginManager.PluginNotFoundException) {
Toast.makeText(this@Interface, e.readableMessage, Toast.LENGTH_SHORT).show()
Logs.w(e)
data.binder.missingPlugin(e.plugin)
stopRunner(false, null)
} catch (exc: Throwable) {
if (exc.javaClass.name.endsWith("proxyerror")) {
// error from golang
Logs.w(exc.readableMessage)
} else {
Logs.w(exc)
}
stopRunner(
false, "${getString(R.string.service_failed)}: ${exc.readableMessage}"
)
} finally {
data.connectingJob = null
}
}
return Service.START_NOT_STICKY
}
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt
================================================
package io.nekohasekai.sagernet.bg
import android.system.ErrnoException
import android.system.Os
import android.system.OsConstants
import io.nekohasekai.sagernet.ktx.Logs
import java.io.File
import java.io.IOException
import androidx.core.text.isDigitsOnly
object Executable {
private val EXECUTABLES = setOf(
"libtrojan.so", "libtrojan-go.so", "libnaive.so", "libtuic.so", "libhysteria.so"
)
fun killAll(alsoKillBg: Boolean = false) {
// kill bg may fail
for (process in File("/proc").listFiles { _, name -> name.isDigitsOnly() } ?: return) {
val exe = File(
try {
File(process, "cmdline").inputStream().bufferedReader().use {
it.readText()
}
} catch (_: IOException) {
continue
}.split(Character.MIN_VALUE, limit = 2).first())
if (EXECUTABLES.contains(exe.name) || (alsoKillBg && exe.name.endsWith(":bg"))) try {
Os.kill(process.name.toInt(), OsConstants.SIGKILL)
Logs.w("SIGKILL ${exe.name} (${process.name}) succeed")
} catch (e: ErrnoException) {
if (e.errno != OsConstants.ESRCH) {
Logs.w("SIGKILL ${exe.absolutePath} (${process.name}) failed")
Logs.w(e)
}
}
}
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt
================================================
package io.nekohasekai.sagernet.bg
import android.os.Build
import android.os.SystemClock
import android.system.ErrnoException
import android.system.Os
import android.system.OsConstants
import androidx.annotation.MainThread
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.utils.Commandline
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import libcore.Libcore
import java.io.File
import java.io.IOException
import java.io.InputStream
import kotlin.concurrent.thread
class GuardedProcessPool(private val onFatal: suspend (IOException) -> Unit) : CoroutineScope {
companion object {
private val pid by lazy {
Class.forName("java.lang.ProcessManager\$ProcessImpl").getDeclaredField("pid")
.apply { isAccessible = true }
}
}
private inner class Guard(
private val cmd: List<String>,
private val env: Map<String, String> = mapOf()
) {
private lateinit var process: Process
private fun streamLogger(input: InputStream, logger: (String) -> Unit) = try {
input.bufferedReader().forEachLine(logger)
} catch (_: IOException) {
} // ignore
fun start() {
process = ProcessBuilder(cmd).directory(SagerNet.application.noBackupFilesDir).apply {
environment().putAll(env)
}.start()
}
@DelicateCoroutinesApi
suspend fun looper(onRestartCallback: (suspend () -> Unit)?) {
var running = true
val cmdName = File(cmd.first()).nameWithoutExtension
val exitChannel = Channel<Int>()
try {
while (true) {
thread(name = "stderr-$cmdName") {
streamLogger(process.errorStream) { Libcore.nekoLogPrintln("[$cmdName] $it") }
}
thread(name = "stdout-$cmdName") {
streamLogger(process.inputStream) { Libcore.nekoLogPrintln("[$cmdName] $it") }
// this thread also acts as a daemon thread for waitFor
runBlocking { exitChannel.send(process.waitFor()) }
}
val startTime = SystemClock.elapsedRealtime()
val exitCode = exitChannel.receive()
running = false
when {
SystemClock.elapsedRealtime() - startTime < 1000 -> throw IOException(
"$cmdName exits too fast (exit code: $exitCode)"
)
exitCode == 128 + OsConstants.SIGKILL -> Logs.w("$cmdName was killed")
else -> Logs.w(IOException("$cmdName unexpectedly exits with code $exitCode"))
}
Logs.i("restart process: ${Commandline.toString(cmd)} (last exit code: $exitCode)")
start()
running = true
onRestartCallback?.invoke()
}
} catch (e: IOException) {
Logs.w("error occurred. stop guard: ${Commandline.toString(cmd)}")
GlobalScope.launch(Dispatchers.Main) { onFatal(e) }
} finally {
if (running) withContext(NonCancellable) { // clean-up cannot be cancelled
if (Build.VERSION.SDK_INT < 24) {
try {
Os.kill(pid.get(process) as Int, OsConstants.SIGTERM)
} catch (e: ErrnoException) {
if (e.errno != OsConstants.ESRCH) Logs.w(e)
} catch (e: ReflectiveOperationException) {
Logs.w(e)
}
if (withTimeoutOrNull(500) { exitChannel.receive() } != null) return@withContext
}
process.destroy() // kill the process
if (Build.VERSION.SDK_INT >= 26) {
if (withTimeoutOrNull(1000) { exitChannel.receive() } != null) return@withContext
process.destroyForcibly() // Force to kill the process if it's still alive
}
exitChannel.receive()
} // otherwise process already exited, nothing to be done
}
}
}
override val coroutineContext = Dispatchers.Main.immediate + Job()
var processCount = 0
@MainThread
fun start(
cmd: List<String>,
env: MutableMap<String, String> = mutableMapOf(),
onRestartCallback: (suspend () -> Unit)? = null
) {
Logs.i("start process: ${Commandline.toString(cmd)}")
Guard(cmd, env).apply {
start() // if start fails, IOException will be thrown directly
launch { looper(onRestartCallback) }
}
processCount += 1
}
@MainThread
fun close(scope: CoroutineScope) {
cancel()
coroutineContext[Job]!!.also { job -> scope.launch { job.cancelAndJoin() } }
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/ProxyService.kt
================================================
package io.nekohasekai.sagernet.bg
import android.annotation.SuppressLint
import android.app.Service
import android.content.Intent
import android.os.PowerManager
import io.nekohasekai.sagernet.SagerNet
class ProxyService : Service(), BaseService.Interface {
override val data = BaseService.Data(this)
override val tag: String get() = "SagerNetProxyService"
override fun createNotification(profileName: String): ServiceNotification =
ServiceNotification(this, profileName, "service-proxy", true)
override var wakeLock: PowerManager.WakeLock? = null
override var upstreamInterfaceName: String? = null
@SuppressLint("WakelockTimeout")
override fun acquireWakeLock() {
wakeLock = SagerNet.power.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "sagernet:proxy")
.apply { acquire() }
}
override fun onBind(intent: Intent) = super.onBind(intent)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
super<BaseService.Interface>.onStartCommand(intent, flags, startId)
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt
================================================
package io.nekohasekai.sagernet.bg
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.os.RemoteException
import io.nekohasekai.sagernet.Action
import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.aidl.ISagerNetService
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
import io.nekohasekai.sagernet.aidl.SpeedDisplayData
import io.nekohasekai.sagernet.aidl.TrafficData
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.ktx.runOnMainDispatcher
class SagerConnection(
private var connectionId: Int,
private var listenForDeath: Boolean = false
) : ServiceConnection, IBinder.DeathRecipient {
companion object {
val serviceClass
get() = when (DataStore.serviceMode) {
Key.MODE_PROXY -> ProxyService::class
Key.MODE_VPN -> VpnService::class
else -> throw UnknownError()
}.java
const val CONNECTION_ID_SHORTCUT = 0
const val CONNECTION_ID_TILE = 1
const val CONNECTION_ID_MAIN_ACTIVITY_FOREGROUND = 2
const val CONNECTION_ID_MAIN_ACTIVITY_BACKGROUND = 3
const val CONNECTION_ID_RESTART_BG = 4
var restartingApp = false
}
interface Callback {
// smaller ISagerNetServiceCallback
fun cbSpeedUpdate(stats: SpeedDisplayData) {}
fun cbTrafficUpdate(data: TrafficData) {}
fun cbSelectorUpdate(id: Long) {}
fun stateChanged(state: BaseService.State, profileName: String?, msg: String?)
fun missingPlugin(profileName: String, pluginName: String) {}
fun onServiceConnected(service: ISagerNetService)
/**
* Different from Android framework, this method will be called even when you call `detachService`.
*/
fun onServiceDisconnected() {}
fun onBinderDied() {}
}
private var connectionActive = false
private var callbackRegistered = false
private var callback: Callback? = null
private val serviceCallback = object : ISagerNetServiceCallback.Stub() {
override fun stateChanged(state: Int, profileName: String?, msg: String?) {
if (state < 0) return // skip private
val s = BaseService.State.values()[state]
DataStore.serviceState = s
val callback = callback ?: return
runOnMainDispatcher {
callback.stateChanged(s, profileName, msg)
}
}
override fun cbSpeedUpdate(stats: SpeedDisplayData) {
val callback = callback ?: return
runOnMainDispatcher {
callback.cbSpeedUpdate(stats)
}
}
override fun cbTrafficUpdate(stats: TrafficData) {
val callback = callback ?: return
runOnMainDispatcher {
callback.cbTrafficUpdate(stats)
}
}
override fun cbSelectorUpdate(id: Long) {
val callback = callback ?: return
runOnMainDispatcher {
callback.cbSelectorUpdate(id)
}
}
override fun missingPlugin(profileName: String, pluginName: String) {
val callback = callback ?: return
runOnMainDispatcher {
callback.missingPlugin(profileName, pluginName)
}
}
}
private var binder: IBinder? = null
var service: ISagerNetService? = null
fun updateConnectionId(id: Int) {
connectionId = id
try {
service?.registerCallback(serviceCallback, id)
} catch (e: Exception) {
e.printStackTrace()
}
}
override fun onServiceConnected(name: ComponentName?, binder: IBinder) {
this.binder = binder
val service = ISagerNetService.Stub.asInterface(binder)!!
this.service = service
try {
if (listenForDeath) binder.linkToDeath(this, 0)
check(!callbackRegistered)
service.registerCallback(serviceCallback, connectionId)
callbackRegistered = true
} catch (e: RemoteException) {
e.printStackTrace()
}
callback?.onServiceConnected(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
unregisterCallback()
callback?.onServiceDisconnected()
service = null
binder = null
}
override fun binderDied() {
service = null
callbackRegistered = false
if (!restartingApp) {
callback?.also { runOnMainDispatcher { it.onBinderDied() } }
}
}
private fun unregisterCallback() {
val service = service
if (service != null && callbackRegistered) try {
service.unregisterCallback(serviceCallback)
} catch (_: RemoteException) {
}
callbackRegistered = false
}
fun connect(context: Context, callback: Callback?) {
if (connectionActive) return
connectionActive = true
check(this.callback == null)
this.callback = callback
val intent = Intent(context, serviceClass).setAction(Action.SERVICE)
context.bindService(intent, this, Context.BIND_AUTO_CREATE)
}
fun disconnect(context: Context) {
unregisterCallback()
if (connectionActive) try {
context.unbindService(this)
} catch (_: IllegalArgumentException) {
} // ignore
connectionActive = false
if (listenForDeath) try {
binder?.unlinkToDeath(this, 0)
} catch (_: NoSuchElementException) {
}
binder = null
service = null
callback = null
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/ServiceNotification.kt
================================================
package io.nekohasekai.sagernet.bg
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
import android.os.Build
import android.text.format.Formatter
import android.widget.Toast
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import io.nekohasekai.sagernet.Action
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.aidl.SpeedDisplayData
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity
import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.ktx.app
import io.nekohasekai.sagernet.ktx.getColorAttr
import io.nekohasekai.sagernet.ktx.runOnMainDispatcher
import io.nekohasekai.sagernet.ui.SwitchActivity
import io.nekohasekai.sagernet.utils.Theme
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/**
* User can customize visibility of notification since Android 8.
* The default visibility:
*
* Android 8.x: always visible due to system limitations
* VPN: always invisible because of VPN notification/icon
* Other: always visible
*
* See also: https://github.com/aosp-mirror/platform_frameworks_base/commit/070d142993403cc2c42eca808ff3fafcee220ac4
*/
class ServiceNotification(
private val service: BaseService.Interface, title: String,
channel: String, visible: Boolean = false,
) : BroadcastReceiver() {
companion object {
const val notificationId = 1
val flags =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0
fun genTitle(ent: ProxyEntity): String {
val gn = if (DataStore.showGroupInNotification)
SagerDatabase.groupDao.getById(ent.groupId)?.displayName() else null
return if (gn == null) ent.displayName() else "[$gn] ${ent.displayName()}"
}
}
var listenPostSpeed = true
suspend fun postNotificationSpeedUpdate(stats: SpeedDisplayData) {
useBuilder {
if (showDirectSpeed) {
val speedDetail = (service as Context).getString(
R.string.speed_detail, service.getString(
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
), service.getString(
R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
), service.getString(
R.string.speed,
Formatter.formatFileSize(service, stats.txRateDirect)
), service.getString(
R.string.speed,
Formatter.formatFileSize(service, stats.rxRateDirect)
)
)
it.setStyle(NotificationCompat.BigTextStyle().bigText(speedDetail))
it.setContentText(speedDetail)
} else {
val speedSimple = (service as Context).getString(
R.string.traffic, service.getString(
R.string.speed, Formatter.formatFileSize(service, stats.txRateProxy)
), service.getString(
R.string.speed, Formatter.formatFileSize(service, stats.rxRateProxy)
)
)
it.setContentText(speedSimple)
}
it.setSubText(
service.getString(
R.string.traffic,
Formatter.formatFileSize(service, stats.txTotal),
Formatter.formatFileSize(service, stats.rxTotal)
)
)
}
update()
}
suspend fun postNotificationTitle(newTitle: String) {
useBuilder {
it.setContentTitle(newTitle)
}
update()
}
suspend fun postNotificationWakeLockStatus(acquired: Boolean) {
updateActions()
useBuilder {
it.priority =
if (acquired) NotificationCompat.PRIORITY_HIGH else NotificationCompat.PRIORITY_LOW
}
update()
}
private val showDirectSpeed = DataStore.showDirectSpeed
private val builder = NotificationCompat.Builder(service as Context, channel)
.setWhen(0)
.setTicker(service.getString(R.string.forward_success))
.setContentTitle(title)
.setOnlyAlertOnce(true)
.setContentIntent(SagerNet.configureIntent(service))
.setSmallIcon(R.drawable.ic_service_active)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(if (visible) NotificationCompat.PRIORITY_LOW else NotificationCompat.PRIORITY_MIN)
private val buildLock = Mutex()
private suspend fun useBuilder(f: (NotificationCompat.Builder) -> Unit) {
buildLock.withLock {
f(builder)
}
}
init {
service as Context
Theme.apply(app)
Theme.apply(service)
builder.color = service.getColorAttr(R.attr.colorPrimary)
service.registerReceiver(this, IntentFilter().apply {
addAction(Intent.ACTION_SCREEN_ON)
addAction(Intent.ACTION_SCREEN_OFF)
})
runOnMainDispatcher {
updateActions()
show()
}
}
private suspend fun updateActions() {
service as Context
useBuilder {
it.clearActions()
val closeAction = NotificationCompat.Action.Builder(
0, service.getText(R.string.stop), PendingIntent.getBroadcast(
service, 0, Intent(Action.CLOSE).setPackage(service.packageName), flags
)
).setShowsUserInterface(false).build()
it.addAction(closeAction)
val switchAction = NotificationCompat.Action.Builder(
0, service.getString(R.string.action_switch), PendingIntent.getActivity(
service, 0, Intent(service, SwitchActivity::class.java), flags
)
).setShowsUserInterface(false).build()
it.addAction(switchAction)
val resetUpstreamAction = NotificationCompat.Action.Builder(
0, service.getString(R.string.reset_connections),
PendingIntent.getBroadcast(
service, 0, Intent(Action.RESET_UPSTREAM_CONNECTIONS), flags
)
).setShowsUserInterface(false).build()
it.addAction(resetUpstreamAction)
}
}
override fun onReceive(context: Context, intent: Intent) {
if (service.data.state == BaseService.State.Connected) {
listenPostSpeed = intent.action == Intent.ACTION_SCREEN_ON
}
}
private suspend fun show() =
useBuilder {
try {
if (Build.VERSION.SDK_INT >= 34) {
(service as Service).startForeground(
notificationId,
it.build(),
FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED
)
} else {
(service as Service).startForeground(notificationId, it.build())
}
} catch (e: Exception) {
Toast.makeText(
SagerNet.application,
"startForeground: $e",
Toast.LENGTH_LONG
).show()
}
}
private suspend fun update() = useBuilder {
NotificationManagerCompat.from(service as Service).notify(notificationId, it.build())
}
fun destroy() {
listenPostSpeed = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
(service as Service).stopForeground(Service.STOP_FOREGROUND_REMOVE)
} else {
(service as Service).stopForeground(true)
}
service.unregisterReceiver(this)
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/SubscriptionUpdater.kt
================================================
package io.nekohasekai.sagernet.bg
import android.content.Context
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy.UPDATE
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkerParameters
import androidx.work.multiprocess.RemoteWorkManager
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.group.GroupUpdater
import io.nekohasekai.sagernet.ktx.Logs
import io.nekohasekai.sagernet.ktx.app
import java.util.concurrent.TimeUnit
object SubscriptionUpdater {
private const val WORK_NAME = "SubscriptionUpdater"
suspend fun reconfigureUpdater() {
RemoteWorkManager.getInstance(app).cancelUniqueWork(WORK_NAME)
val subscriptions = SagerDatabase.groupDao.subscriptions()
.filter { it.subscription!!.autoUpdate }
if (subscriptions.isEmpty()) return
// PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS
var minDelay =
subscriptions.minByOrNull { it.subscription!!.autoUpdateDelay }!!.subscription!!.autoUpdateDelay.toLong()
val now = System.currentTimeMillis() / 1000L
var minInitDelay =
subscriptions.minOf { now - it.subscription!!.lastUpdated - (minDelay * 60) }
if (minDelay < 15) minDelay = 15
if (minInitDelay > 60) minInitDelay = 60
// main process
RemoteWorkManager.getInstance(app).enqueueUniquePeriodicWork(
WORK_NAME,
UPDATE,
PeriodicWorkRequest.Builder(UpdateTask::class.java, minDelay, TimeUnit.MINUTES)
.apply {
if (minInitDelay > 0) setInitialDelay(minInitDelay, TimeUnit.SECONDS)
}
.build()
)
}
class UpdateTask(
appContext: Context, params: WorkerParameters
) : CoroutineWorker(appContext, params) {
val nm = NotificationManagerCompat.from(applicationContext)
val notification = NotificationCompat.Builder(applicationContext, "service-subscription")
.setWhen(0)
.setTicker(applicationContext.getString(R.string.forward_success))
.setContentTitle(applicationContext.getString(R.string.subscription_update))
.setSmallIcon(R.drawable.ic_service_active)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
override suspend fun doWork(): Result {
var subscriptions =
SagerDatabase.groupDao.subscriptions().filter { it.subscription!!.autoUpdate }
if (!DataStore.serviceState.connected) {
Logs.d("work: not connected")
subscriptions = subscriptions.filter { !it.subscription!!.updateWhenConnectedOnly }
}
if (subscriptions.isNotEmpty()) for (profile in subscriptions) {
val subscription = profile.subscription!!
if (((System.currentTimeMillis() / 1000).toInt() - subscription.lastUpdated) < subscription.autoUpdateDelay * 60) {
Logs.d("work: not updating " + profile.displayName())
continue
}
Logs.d("work: updating " + profile.displayName())
notification.setContentText(
applicationContext.getString(
R.string.subscription_update_message, profile.displayName()
)
)
nm.notify(2, notification.build())
GroupUpdater.executeUpdate(profile, false)
}
nm.cancel(2)
return Result.success()
}
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/TileService.kt
================================================
package io.nekohasekai.sagernet.bg
import android.graphics.drawable.Icon
import android.service.quicksettings.Tile
import androidx.annotation.RequiresApi
import io.nekohasekai.sagernet.R
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.aidl.ISagerNetService
import io.nekohasekai.sagernet.database.SagerDatabase
import android.service.quicksettings.TileService as BaseTileService
@RequiresApi(24)
class TileService : BaseTileService(), SagerConnection.Callback {
private val iconIdle by lazy { Icon.createWithResource(this, R.drawable.ic_service_idle) }
private val iconBusy by lazy { Icon.createWithResource(this, R.drawable.ic_service_busy) }
private val iconConnected by lazy {
Icon.createWithResource(this, R.drawable.ic_service_active)
}
private var tapPending = false
private val connection = SagerConnection(SagerConnection.CONNECTION_ID_TILE)
override fun stateChanged(state: BaseService.State, profileName: String?, msg: String?) =
updateTile(state, profileName)
override fun onServiceConnected(service: ISagerNetService) {
updateTile(BaseService.State.values()[service.state], service.profileName)
if (tapPending) {
tapPending = false
onClick()
}
}
override fun cbSelectorUpdate(id: Long) {
val profile = SagerDatabase.proxyDao.getById(id) ?: return
updateTile(BaseService.State.Connected, profile.displayName())
}
override fun onStartListening() {
super.onStartListening()
connection.connect(this, this)
}
override fun onStopListening() {
connection.disconnect(this)
super.onStopListening()
}
override fun onClick() {
if (isLocked) unlockAndRun(this::toggle) else toggle()
}
private fun updateTile(serviceState: BaseService.State, profileName: String?) {
qsTile?.apply {
label = null
when (serviceState) {
BaseService.State.Idle -> error("serviceState")
BaseService.State.Connecting -> {
icon = iconBusy
state = Tile.STATE_ACTIVE
}
BaseService.State.Connected -> {
icon = iconConnected
label = profileName
state = Tile.STATE_ACTIVE
}
BaseService.State.Stopping -> {
icon = iconBusy
state = Tile.STATE_UNAVAILABLE
}
BaseService.State.Stopped -> {
icon = iconIdle
state = Tile.STATE_INACTIVE
}
}
label = label ?: getString(R.string.app_name)
updateTile()
}
}
private fun toggle() {
val service = connection.service
if (service == null) tapPending =
true else BaseService.State.values()[service.state].let { state ->
when {
state.canStop -> SagerNet.stopService()
state == BaseService.State.Stopped -> SagerNet.startService()
}
}
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt
================================================
package io.nekohasekai.sagernet.bg
import android.Manifest
import android.annotation.SuppressLint
import android.app.Service
import android.content.Intent
import android.content.pm.PackageManager
import android.net.ProxyInfo
import android.os.Build
import android.os.ParcelFileDescriptor
import android.os.PowerManager
import io.nekohasekai.sagernet.*
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.fmt.LOCALHOST
import io.nekohasekai.sagernet.fmt.hysteria.HysteriaBean
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.ui.VpnRequestActivity
import io.nekohasekai.sagernet.utils.Subnet
import android.net.VpnService as BaseVpnService
class VpnService : BaseVpnService(),
BaseService.Interface {
companion object {
const val PRIVATE_VLAN4_CLIENT = "172.19.0.1"
const val PRIVATE_VLAN4_ROUTER = "172.19.0.2"
const val FAKEDNS_VLAN4_CLIENT = "198.18.0.0"
const val PRIVATE_VLAN6_CLIENT = "fdfe:dcba:9876::1"
const val PRIVATE_VLAN6_ROUTER = "fdfe:dcba:9876::2"
}
var conn: ParcelFileDescriptor? = null
private var metered = false
override var upstreamInterfaceName: String? = null
override suspend fun startProcesses() {
DataStore.vpnService = this
super.startProcesses() // launch proxy instance
}
override var wakeLock: PowerManager.WakeLock? = null
@SuppressLint("WakelockTimeout")
override fun acquireWakeLock() {
wakeLock = SagerNet.power.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "sagernet:vpn")
.apply { acquire() }
}
@Suppress("EXPERIMENTAL_API_USAGE")
override fun killProcesses() {
conn?.close()
conn = null
super.killProcesses()
}
override fun onBind(intent: Intent) = when (intent.action) {
SERVICE_INTERFACE -> super<BaseVpnService>.onBind(intent)
else -> super<BaseService.Interface>.onBind(intent)
}
override val data = BaseService.Data(this)
override val tag = "SagerNetVpnService"
override fun createNotification(profileName: String) =
ServiceNotification(this, profileName, "service-vpn")
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (DataStore.serviceMode == Key.MODE_VPN) {
if (prepare(this) != null) {
startActivity(
Intent(
this, VpnRequestActivity::class.java
).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} else return super<BaseService.Interface>.onStartCommand(intent, flags, startId)
}
stopRunner()
return Service.START_NOT_STICKY
}
inner class NullConnectionException : NullPointerException(),
BaseService.ExpectedException {
override fun getLocalizedMessage() = getString(R.string.reboot_required)
}
fun startVpn(tunOptionsJson: String, tunPlatformOptionsJson: String): Int {
// Logs.d(tunOptionsJson)
// Logs.d(tunPlatformOptionsJson)
// val tunOptions = JSONObject(tunOptionsJson)
// address & route & MTU ...... use NB4A GUI config
val builder = Builder().setConfigureIntent(SagerNet.configureIntent(this))
.setSession(getString(R.string.app_name))
.setMtu(DataStore.mtu)
val ipv6Mode = DataStore.ipv6Mode
// address
builder.addAddress(PRIVATE_VLAN4_CLIENT, 30)
if (ipv6Mode != IPv6Mode.DISABLE) {
builder.addAddress(PRIVATE_VLAN6_CLIENT, 126)
}
builder.addDnsServer(PRIVATE_VLAN4_ROUTER)
// route
if (DataStore.bypassLan) {
resources.getStringArray(R.array.bypass_private_route).forEach {
val subnet = Subnet.fromString(it)!!
builder.addRoute(subnet.address.hostAddress!!, subnet.prefixSize)
}
builder.addRoute(PRIVATE_VLAN4_ROUTER, 32)
builder.addRoute(FAKEDNS_VLAN4_CLIENT, 15)
// https://issuetracker.google.com/issues/149636790
if (ipv6Mode != IPv6Mode.DISABLE) {
builder.addRoute("2000::", 3)
}
} else {
builder.addRoute("0.0.0.0", 0)
if (ipv6Mode != IPv6Mode.DISABLE) {
builder.addRoute("::", 0)
}
}
updateUnderlyingNetwork(builder)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) builder.setMetered(metered)
// app route
val packageName = packageName
val proxyApps = DataStore.proxyApps
var bypass = DataStore.bypass
val workaroundSYSTEM = false /* DataStore.tunImplementation == TunImplementation.SYSTEM */
val needBypassRootUid = workaroundSYSTEM || data.proxy!!.config.trafficMap.values.any {
it[0].hysteriaBean?.protocol == HysteriaBean.PROTOCOL_FAKETCP
}
if (proxyApps || needBypassRootUid) {
val individual = mutableSetOf<String>()
val allApps by lazy {
packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS).filter {
when (it.packageName) {
packageName -> false
"android" -> true
else -> it.requestedPermissions?.contains(Manifest.permission.INTERNET) == true
}
}.map {
it.packageName
}
}
if (proxyApps) {
individual.addAll(DataStore.individual.split('\n').filter { it.isNotBlank() })
if (bypass && needBypassRootUid) {
val individualNew = allApps.toMutableList()
individualNew.removeAll(individual)
individual.clear()
individual.addAll(individualNew)
bypass = false
}
} else {
individual.addAll(allApps)
bypass = false
}
val added = mutableListOf<String>()
individual.apply {
// Allow Matsuri itself using VPN.
remove(packageName)
if (!bypass) add(packageName)
}.forEach {
try {
if (bypass) {
builder.addDisallowedApplication(it)
} else {
builder.addAllowedApplication(it)
}
added.add(it)
} catch (ex: PackageManager.NameNotFoundException) {
Logs.w(ex)
}
}
if (bypass) {
Logs.d("Add bypass: ${added.joinToString(", ")}")
} else {
Logs.d("Add allow: ${added.joinToString(", ")}")
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && DataStore.appendHttpProxy) {
builder.setHttpProxy(ProxyInfo.buildDirectProxy(LOCALHOST, DataStore.mixedPort))
}
metered = DataStore.meteredNetwork
if (Build.VERSION.SDK_INT >= 29) builder.setMetered(metered)
conn = builder.establish() ?: throw NullConnectionException()
return conn!!.fd
}
fun updateUnderlyingNetwork(builder: Builder? = null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
SagerNet.underlyingNetwork?.let {
builder?.setUnderlyingNetworks(arrayOf(SagerNet.underlyingNetwork))
?: setUnderlyingNetworks(arrayOf(SagerNet.underlyingNetwork))
}
}
}
override fun onRevoke() = stopRunner()
override fun onDestroy() {
DataStore.vpnService = null
super.onDestroy()
data.binder.close()
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
================================================
package io.nekohasekai.sagernet.bg.proto
import android.os.SystemClock
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.bg.AbstractInstance
import io.nekohasekai.sagernet.bg.GuardedProcessPool
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity
import io.nekohasekai.sagernet.fmt.ConfigBuildResult
import io.nekohasekai.sagernet.fmt.buildConfig
import io.nekohasekai.sagernet.fmt.hysteria.HysteriaBean
import io.nekohasekai.sagernet.fmt.hysteria.buildHysteria1Config
import io.nekohasekai.sagernet.fmt.mieru.MieruBean
import io.nekohasekai.sagernet.fmt.mieru.buildMieruConfig
import io.nekohasekai.sagernet.fmt.naive.NaiveBean
import io.nekohasekai.sagernet.fmt.naive.buildNaiveConfig
import io.nekohasekai.sagernet.fmt.trojan_go.TrojanGoBean
import io.nekohasekai.sagernet.fmt.trojan_go.buildTrojanGoConfig
import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.plugin.PluginManager
import kotlinx.coroutines.*
import libcore.BoxInstance
import libcore.Libcore
import moe.matsuri.nb4a.net.LocalResolverImpl
import java.io.File
abstract class BoxInstance(
val profile: ProxyEntity
) : AbstractInstance {
lateinit var config: ConfigBuildResult
lateinit var box: BoxInstance
val pluginPath = hashMapOf<String, PluginManager.InitResult>()
val pluginConfigs = hashMapOf<Int, Pair<Int, String>>()
val externalInstances = hashMapOf<Int, AbstractInstance>()
open lateinit var processes: GuardedProcessPool
private var cacheFiles = ArrayList<File>()
fun isInitialized(): Boolean {
return ::config.isInitialized && ::box.isInitialized
}
protected fun initPlugin(name: String): PluginManager.InitResult {
return pluginPath.getOrPut(name) { PluginManager.init(name)!! }
}
protected open fun buildConfig() {
config = buildConfig(profile)
}
protected open suspend fun loadConfig() {
box = Libcore.newSingBoxInstance(config.config, LocalResolverImpl)
}
open suspend fun init() {
buildConfig()
for ((chain) in config.externalIndex) {
chain.entries.forEachIndexed { index, (port, profile) ->
when (val bean = profile.requireBean()) {
is TrojanGoBean -> {
initPlugin("trojan-go-plugin")
pluginConfigs[port] = profile.type to bean.buildTrojanGoConfig(port)
}
is MieruBean -> {
initPlugin("mieru-plugin")
pluginConfigs[port] = profile.type to bean.buildMieruConfig(port)
}
is NaiveBean -> {
initPlugin("naive-plugin")
pluginConfigs[port] = profile.type to bean.buildNaiveConfig(port)
}
is HysteriaBean -> {
initPlugin("hysteria-plugin")
pluginConfigs[port] = profile.type to bean.buildHysteria1Config(port) {
File(
app.cacheDir, "hysteria_" + SystemClock.elapsedRealtime() + ".ca"
).apply {
parentFile?.mkdirs()
cacheFiles.add(this)
}
}
}
}
}
}
loadConfig()
}
override fun launch() {
// TODO move, this is not box
val cacheDir = File(SagerNet.application.cacheDir, "tmpcfg")
cacheDir.mkdirs()
for ((chain) in config.externalIndex) {
chain.entries.forEachIndexed { index, (port, profile) ->
val bean = profile.requireBean()
val needChain = index != chain.size - 1
val (profileType, config) = pluginConfigs[port] ?: (0 to "")
when {
externalInstances.containsKey(port) -> {
externalInstances[port]!!.launch()
}
bean is TrojanGoBean -> {
val configFile = File(
cacheDir, "trojan_go_" + SystemClock.elapsedRealtime() + ".json"
)
configFile.parentFile?.mkdirs()
configFile.writeText(config)
cacheFiles.add(configFile)
val commands = mutableListOf(
initPlugin("trojan-go-plugin").path, "-config", configFile.absolutePath
)
processes.start(commands)
}
bean is MieruBean -> {
val configFile = File(
cacheDir, "mieru_" + SystemClock.elapsedRealtime() + ".json"
)
configFile.parentFile?.mkdirs()
configFile.writeText(config)
cacheFiles.add(configFile)
val envMap = mutableMapOf<String, String>()
envMap["MIERU_CONFIG_JSON_FILE"] = configFile.absolutePath
envMap["MIERU_PROTECT_PATH"] = "protect_path"
val commands = mutableListOf(
initPlugin("mieru-plugin").path, "run",
)
processes.start(commands, envMap)
}
bean is NaiveBean -> {
val configFile = File(
cacheDir, "naive_" + SystemClock.elapsedRealtime() + ".json"
)
configFile.parentFile?.mkdirs()
configFile.writeText(config)
cacheFiles.add(configFile)
val envMap = mutableMapOf<String, String>()
if (bean.certificates.isNotBlank()) {
val certFile = File(
cacheDir, "naive_" + SystemClock.elapsedRealtime() + ".crt"
)
certFile.parentFile?.mkdirs()
certFile.writeText(bean.certificates)
cacheFiles.add(certFile)
envMap["SSL_CERT_FILE"] = certFile.absolutePath
}
val commands = mutableListOf(
initPlugin("naive-plugin").path, configFile.absolutePath
)
processes.start(commands, envMap)
}
bean is HysteriaBean -> {
val configFile = File(
cacheDir, "hysteria_" + SystemClock.elapsedRealtime() + ".json"
)
configFile.parentFile?.mkdirs()
configFile.writeText(config)
cacheFiles.add(configFile)
val commands = mutableListOf(
initPlugin("hysteria-plugin").path,
"--no-check",
"--config",
configFile.absolutePath,
"--log-level",
if (DataStore.logLevel > 0) "trace" else "warn",
"client"
)
if (bean.protocol == HysteriaBean.PROTOCOL_FAKETCP) {
commands.addAll(0, listOf("su", "-c"))
}
processes.start(commands)
}
}
}
}
box.start()
}
@Suppress("EXPERIMENTAL_API_USAGE")
override fun close() {
for (instance in externalInstances.values) {
runCatching {
instance.close()
}
}
cacheFiles.removeAll { it.delete(); true }
if (::processes.isInitialized) processes.close(GlobalScope + Dispatchers.IO)
if (::box.isInitialized) {
box.close()
}
}
}
================================================
FILE: app/src/main/java/io/nekohasekai/sagernet/bg/proto/ProxyInstance.kt
================================================
package io.nekohasekai.sagernet.bg.proto
import io.nekohaseka
gitextract_rsn9ha2i/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report-en.md │ │ ├── bug-report-zh_cn.md │ │ ├── feature_request-en.md │ │ └── feature_request-zh_cn.md │ └── workflows/ │ ├── preview.yml │ └── release.yml ├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle.kts │ ├── executableSo/ │ │ └── .gitignore │ ├── proguard-rules.pro │ ├── schemas/ │ │ ├── io.nekohasekai.sagernet.database.SagerDatabase/ │ │ │ ├── 1.json │ │ │ ├── 2.json │ │ │ ├── 3.json │ │ │ ├── 4.json │ │ │ ├── 5.json │ │ │ └── 6.json │ │ ├── io.nekohasekai.sagernet.database.preference.PublicDatabase/ │ │ │ └── 1.json │ │ └── moe.matsuri.nb4a.TempDatabase/ │ │ └── 1.json │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── aidl/ │ │ └── io/ │ │ └── nekohasekai/ │ │ └── sagernet/ │ │ └── aidl/ │ │ ├── ISagerNetService.aidl │ │ ├── ISagerNetServiceCallback.aidl │ │ ├── SpeedDisplayData.aidl │ │ └── TrafficData.aidl │ ├── assets/ │ │ ├── LICENSE │ │ ├── proxy_packagename.txt │ │ └── yacd.version.txt │ ├── java/ │ │ ├── com/ │ │ │ └── github/ │ │ │ └── shadowsocks/ │ │ │ └── plugin/ │ │ │ ├── Utils.kt │ │ │ └── fragment/ │ │ │ └── AlertDialogFragment.kt │ │ ├── io/ │ │ │ └── nekohasekai/ │ │ │ └── sagernet/ │ │ │ ├── BootReceiver.kt │ │ │ ├── Constants.kt │ │ │ ├── QuickToggleShortcut.kt │ │ │ ├── SagerNet.kt │ │ │ ├── aidl/ │ │ │ │ ├── SpeedDisplayData.kt │ │ │ │ └── TrafficData.kt │ │ │ ├── bg/ │ │ │ │ ├── AbstractInstance.kt │ │ │ │ ├── BaseService.kt │ │ │ │ ├── Executable.kt │ │ │ │ ├── GuardedProcessPool.kt │ │ │ │ ├── ProxyService.kt │ │ │ │ ├── SagerConnection.kt │ │ │ │ ├── ServiceNotification.kt │ │ │ │ ├── SubscriptionUpdater.kt │ │ │ │ ├── TileService.kt │ │ │ │ ├── VpnService.kt │ │ │ │ └── proto/ │ │ │ │ ├── BoxInstance.kt │ │ │ │ ├── ProxyInstance.kt │ │ │ │ ├── TestInstance.kt │ │ │ │ ├── TrafficLooper.kt │ │ │ │ ├── TrafficUpdater.kt │ │ │ │ └── UrlTest.kt │ │ │ ├── database/ │ │ │ │ ├── DataStore.kt │ │ │ │ ├── GroupManager.kt │ │ │ │ ├── ParcelizeBridge.java │ │ │ │ ├── ProfileManager.kt │ │ │ │ ├── ProxyEntity.kt │ │ │ │ ├── ProxyGroup.kt │ │ │ │ ├── RuleEntity.kt │ │ │ │ ├── SagerDatabase.kt │ │ │ │ ├── StringCollectionConverter.kt │ │ │ │ ├── SubscriptionBean.java │ │ │ │ └── preference/ │ │ │ │ ├── EditTextPreferenceModifiers.kt │ │ │ │ ├── KeyValuePair.kt │ │ │ │ ├── OnPreferenceDataStoreChangeListener.kt │ │ │ │ ├── PublicDatabase.kt │ │ │ │ └── RoomPreferenceDataStore.kt │ │ │ ├── fmt/ │ │ │ │ ├── AbstractBean.java │ │ │ │ ├── ConfigBuilder.kt │ │ │ │ ├── KryoConverters.java │ │ │ │ ├── PluginEntry.kt │ │ │ │ ├── Serializable.kt │ │ │ │ ├── TypeMap.kt │ │ │ │ ├── UniversalFmt.kt │ │ │ │ ├── gson/ │ │ │ │ │ └── GsonConverters.java │ │ │ │ ├── http/ │ │ │ │ │ ├── HttpBean.java │ │ │ │ │ └── HttpFmt.kt │ │ │ │ ├── hysteria/ │ │ │ │ │ ├── HysteriaBean.java │ │ │ │ │ └── HysteriaFmt.kt │ │ │ │ ├── internal/ │ │ │ │ │ ├── ChainBean.java │ │ │ │ │ └── InternalBean.java │ │ │ │ ├── mieru/ │ │ │ │ │ ├── MieruBean.java │ │ │ │ │ └── MieruFmt.kt │ │ │ │ ├── naive/ │ │ │ │ │ ├── NaiveBean.java │ │ │ │ │ └── NaiveFmt.kt │ │ │ │ ├── shadowsocks/ │ │ │ │ │ ├── ShadowsocksBean.java │ │ │ │ │ └── ShadowsocksFmt.kt │ │ │ │ ├── socks/ │ │ │ │ │ ├── SOCKSBean.java │ │ │ │ │ └── SOCKSFmt.kt │ │ │ │ ├── ssh/ │ │ │ │ │ ├── SSHBean.java │ │ │ │ │ └── SSHFmt.kt │ │ │ │ ├── trojan/ │ │ │ │ │ ├── TrojanBean.java │ │ │ │ │ └── TrojanFmt.kt │ │ │ │ ├── trojan_go/ │ │ │ │ │ ├── TrojanGoBean.java │ │ │ │ │ └── TrojanGoFmt.kt │ │ │ │ ├── tuic/ │ │ │ │ │ ├── TuicBean.java │ │ │ │ │ └── TuicFmt.kt │ │ │ │ ├── v2ray/ │ │ │ │ │ ├── StandardV2RayBean.java │ │ │ │ │ ├── V2RayFmt.kt │ │ │ │ │ └── VMessBean.java │ │ │ │ └── wireguard/ │ │ │ │ ├── WireGuardBean.java │ │ │ │ └── WireGuardFmt.kt │ │ │ ├── group/ │ │ │ │ ├── GroupInterfaceAdapter.kt │ │ │ │ ├── GroupUpdater.kt │ │ │ │ └── RawUpdater.kt │ │ │ ├── ktx/ │ │ │ │ ├── Asyncs.kt │ │ │ │ ├── Browsers.kt │ │ │ │ ├── Dialogs.kt │ │ │ │ ├── Dimens.kt │ │ │ │ ├── Formats.kt │ │ │ │ ├── Kryos.kt │ │ │ │ ├── Layouts.kt │ │ │ │ ├── Logs.kt │ │ │ │ ├── Nets.kt │ │ │ │ ├── Preferences.kt │ │ │ │ └── Utils.kt │ │ │ ├── plugin/ │ │ │ │ └── PluginManager.kt │ │ │ ├── ui/ │ │ │ │ ├── AboutFragment.kt │ │ │ │ ├── AppListActivity.kt │ │ │ │ ├── AppManagerActivity.kt │ │ │ │ ├── AssetsActivity.kt │ │ │ │ ├── BackupFragment.kt │ │ │ │ ├── BlankActivity.kt │ │ │ │ ├── ConfigurationFragment.kt │ │ │ │ ├── GroupFragment.kt │ │ │ │ ├── GroupSettingsActivity.kt │ │ │ │ ├── LogcatFragment.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── NamedFragment.kt │ │ │ │ ├── NetworkFragment.kt │ │ │ │ ├── ProfileSelectActivity.kt │ │ │ │ ├── QuickDisableShortcut.kt │ │ │ │ ├── QuickEnableShortcut.kt │ │ │ │ ├── RouteFragment.kt │ │ │ │ ├── RouteSettingsActivity.kt │ │ │ │ ├── ScannerActivity.kt │ │ │ │ ├── SettingsFragment.kt │ │ │ │ ├── SettingsPreferenceFragment.kt │ │ │ │ ├── StunActivity.kt │ │ │ │ ├── SwitchActivity.kt │ │ │ │ ├── ThemedActivity.kt │ │ │ │ ├── ToolbarFragment.kt │ │ │ │ ├── ToolsFragment.kt │ │ │ │ ├── VpnRequestActivity.kt │ │ │ │ ├── WebviewFragment.kt │ │ │ │ └── profile/ │ │ │ │ ├── ChainSettingsActivity.kt │ │ │ │ ├── ConfigEditActivity.kt │ │ │ │ ├── HttpSettingsActivity.kt │ │ │ │ ├── HysteriaSettingsActivity.kt │ │ │ │ ├── MieruSettingsActivity.kt │ │ │ │ ├── NaiveSettingsActivity.kt │ │ │ │ ├── ProfileSettingsActivity.kt │ │ │ │ ├── SSHSettingsActivity.kt │ │ │ │ ├── ShadowsocksSettingsActivity.kt │ │ │ │ ├── SocksSettingsActivity.kt │ │ │ │ ├── StandardV2RaySettingsActivity.kt │ │ │ │ ├── TrojanGoSettingsActivity.kt │ │ │ │ ├── TrojanSettingsActivity.kt │ │ │ │ ├── TuicSettingsActivity.kt │ │ │ │ ├── VMessSettingsActivity.kt │ │ │ │ └── WireGuardSettingsActivity.kt │ │ │ ├── utils/ │ │ │ │ ├── Commandline.kt │ │ │ │ ├── CrashHandler.kt │ │ │ │ ├── DefaultNetworkListener.kt │ │ │ │ ├── PackageCache.kt │ │ │ │ ├── Subnet.kt │ │ │ │ └── Theme.kt │ │ │ └── widget/ │ │ │ ├── AppListPreference.kt │ │ │ ├── AutoCollapseTextView.kt │ │ │ ├── FabProgressBehavior.kt │ │ │ ├── GroupPreference.kt │ │ │ ├── LinkOrContentPreference.kt │ │ │ ├── OutboundPreference.kt │ │ │ ├── QRCodeDialog.kt │ │ │ ├── ServiceButton.kt │ │ │ ├── StatsBar.kt │ │ │ ├── UndoSnackbarManager.kt │ │ │ ├── UserAgentPreference.kt │ │ │ └── WindowInsetsListeners.kt │ │ └── moe/ │ │ └── matsuri/ │ │ └── nb4a/ │ │ ├── NativeInterface.kt │ │ ├── Protocols.kt │ │ ├── SingBoxOptions.java │ │ ├── SingBoxOptionsUtil.kt │ │ ├── TempDatabase.kt │ │ ├── net/ │ │ │ └── LocalResolverImpl.kt │ │ ├── plugin/ │ │ │ └── Plugins.kt │ │ ├── proxy/ │ │ │ ├── PreferenceBinding.kt │ │ │ ├── PreferenceBindingManager.kt │ │ │ ├── anytls/ │ │ │ │ ├── AnyTLSBean.java │ │ │ │ ├── AnyTLSFmt.kt │ │ │ │ └── AnyTLSSettingsActivity.kt │ │ │ ├── config/ │ │ │ │ ├── ConfigBean.java │ │ │ │ └── ConfigSettingActivity.kt │ │ │ ├── neko/ │ │ │ │ └── NekoBean.java │ │ │ └── shadowtls/ │ │ │ ├── ShadowTLSBean.java │ │ │ ├── ShadowTLSFmt.kt │ │ │ └── ShadowTLSSettingsActivity.kt │ │ ├── ui/ │ │ │ ├── ColorPickerPreference.kt │ │ │ ├── ConnectionTestNotification.kt │ │ │ ├── Dialogs.kt │ │ │ ├── EditConfigPreference.kt │ │ │ ├── ExtendedKeyboard.kt │ │ │ ├── LongClickListPreference.kt │ │ │ ├── LongClickMenuPreference.kt │ │ │ ├── LongClickSwitchPreference.kt │ │ │ ├── MTUPreference.kt │ │ │ ├── SimpleMenuPreference.kt │ │ │ └── UrlTestPreference.kt │ │ └── utils/ │ │ ├── JavaUtil.java │ │ ├── KotlinUtil.kt │ │ ├── NGUtil.kt │ │ ├── SendLog.kt │ │ ├── Util.kt │ │ └── WebViewUtil.kt │ └── res/ │ ├── color/ │ │ ├── chip_background.xml │ │ ├── chip_ripple_color.xml │ │ ├── chip_text_color.xml │ │ ├── navigation_icon.xml │ │ └── navigation_item.xml │ ├── drawable/ │ │ ├── baseline_arrow_back_24.xml │ │ ├── baseline_construction_24.xml │ │ ├── baseline_delete_sweep_24.xml │ │ ├── baseline_developer_board_24.xml │ │ ├── baseline_flight_takeoff_24.xml │ │ ├── baseline_keyboard_tab_24.xml │ │ ├── baseline_public_24.xml │ │ ├── baseline_redo_24.xml │ │ ├── baseline_save_24.xml │ │ ├── baseline_send_24.xml │ │ ├── baseline_translate_24.xml │ │ ├── baseline_undo_24.xml │ │ ├── baseline_widgets_24.xml │ │ ├── baseline_wrap_text_24.xml │ │ ├── ic_action_copyright.xml │ │ ├── ic_action_delete.xml │ │ ├── ic_action_description.xml │ │ ├── ic_action_dns.xml │ │ ├── ic_action_done.xml │ │ ├── ic_action_lock.xml │ │ ├── ic_action_lock_open.xml │ │ ├── ic_action_note_add.xml │ │ ├── ic_action_settings.xml │ │ ├── ic_app_shortcut_background.xml │ │ ├── ic_av_playlist_add.xml │ │ ├── ic_baseline_add_road_24.xml │ │ ├── ic_baseline_airplanemode_active_24.xml │ │ ├── ic_baseline_android_24.xml │ │ ├── ic_baseline_bug_report_24.xml │ │ ├── ic_baseline_camera_24.xml │ │ ├── ic_baseline_card_giftcard_24.xml │ │ ├── ic_baseline_cast_connected_24.xml │ │ ├── ic_baseline_center_focus_weak_24.xml │ │ ├── ic_baseline_color_lens_24.xml │ │ ├── ic_baseline_compare_arrows_24.xml │ │ ├── ic_baseline_domain_24.xml │ │ ├── ic_baseline_download_24.xml │ │ ├── ic_baseline_emoji_emotions_24.xml │ │ ├── ic_baseline_fast_forward_24.xml │ │ ├── ic_baseline_fiber_manual_record_24.xml │ │ ├── ic_baseline_fingerprint_24.xml │ │ ├── ic_baseline_flip_camera_android_24.xml │ │ ├── ic_baseline_format_align_left_24.xml │ │ ├── ic_baseline_grid_3x3_24.xml │ │ ├── ic_baseline_home_24.xml │ │ ├── ic_baseline_http_24.xml │ │ ├── ic_baseline_https_24.xml │ │ ├── ic_baseline_import_contacts_24.xml │ │ ├── ic_baseline_info_24.xml │ │ ├── ic_baseline_layers_24.xml │ │ ├── ic_baseline_legend_toggle_24.xml │ │ ├── ic_baseline_link_24.xml │ │ ├── ic_baseline_local_bar_24.xml │ │ ├── ic_baseline_location_on_24.xml │ │ ├── ic_baseline_lock_24.xml │ │ ├── ic_baseline_low_priority_24.xml │ │ ├── ic_baseline_manage_search_24.xml │ │ ├── ic_baseline_more_vert_24.xml │ │ ├── ic_baseline_multiline_chart_24.xml │ │ ├── ic_baseline_multiple_stop_24.xml │ │ ├── ic_baseline_nat_24.xml │ │ ├── ic_baseline_nfc_24.xml │ │ ├── ic_baseline_no_encryption_gmailerrorred_24.xml │ │ ├── ic_baseline_person_24.xml │ │ ├── ic_baseline_push_pin_24.xml │ │ ├── ic_baseline_refresh_24.xml │ │ ├── ic_baseline_rule_folder_24.xml │ │ ├── ic_baseline_running_with_errors_24.xml │ │ ├── ic_baseline_sanitizer_24.xml │ │ ├── ic_baseline_security_24.xml │ │ ├── ic_baseline_shuffle_24.xml │ │ ├── ic_baseline_shutter_speed_24.xml │ │ ├── ic_baseline_speed_24.xml │ │ ├── ic_baseline_stream_24.xml │ │ ├── ic_baseline_texture_24.xml │ │ ├── ic_baseline_timelapse_24.xml │ │ ├── ic_baseline_transform_24.xml │ │ ├── ic_baseline_transgender_24.xml │ │ ├── ic_baseline_update_24.xml │ │ ├── ic_baseline_view_list_24.xml │ │ ├── ic_baseline_vpn_key_24.xml │ │ ├── ic_baseline_warning_24.xml │ │ ├── ic_baseline_wb_sunny_24.xml │ │ ├── ic_communication_phonelink_ring.xml │ │ ├── ic_device_data_usage.xml │ │ ├── ic_device_developer_mode.xml │ │ ├── ic_file_cloud_queue.xml │ │ ├── ic_file_file_upload.xml │ │ ├── ic_hardware_router.xml │ │ ├── ic_image_camera_alt.xml │ │ ├── ic_image_edit.xml │ │ ├── ic_image_looks_6.xml │ │ ├── ic_image_photo.xml │ │ ├── ic_maps_360.xml │ │ ├── ic_maps_directions.xml │ │ ├── ic_maps_directions_boat.xml │ │ ├── ic_navigation_apps.xml │ │ ├── ic_navigation_close.xml │ │ ├── ic_navigation_menu.xml │ │ ├── ic_notification_enhanced_encryption.xml │ │ ├── ic_qu_camera_launcher.xml │ │ ├── ic_qu_shadowsocks_foreground.xml │ │ ├── ic_qu_shadowsocks_launcher.xml │ │ ├── ic_service_active.xml │ │ ├── ic_service_busy.xml │ │ ├── ic_service_connected.xml │ │ ├── ic_service_connecting.xml │ │ ├── ic_service_idle.xml │ │ ├── ic_service_stopped.xml │ │ ├── ic_service_stopping.xml │ │ ├── ic_settings_password.xml │ │ ├── ic_social_emoji_symbols.xml │ │ ├── ic_social_share.xml │ │ └── terminal_scroll_shape.xml │ ├── drawable-v26/ │ │ ├── ic_qu_camera_launcher.xml │ │ └── ic_qu_shadowsocks_launcher.xml │ ├── layout/ │ │ ├── item_keyboard_key.xml │ │ ├── layout_about.xml │ │ ├── layout_add_entity.xml │ │ ├── layout_app_list.xml │ │ ├── layout_app_placeholder.xml │ │ ├── layout_appbar.xml │ │ ├── layout_apps.xml │ │ ├── layout_apps_item.xml │ │ ├── layout_asset_item.xml │ │ ├── layout_assets.xml │ │ ├── layout_backup.xml │ │ ├── layout_chain_settings.xml │ │ ├── layout_config_settings.xml │ │ ├── layout_debug.xml │ │ ├── layout_edit_config.xml │ │ ├── layout_edit_group.xml │ │ ├── layout_empty.xml │ │ ├── layout_empty_route.xml │ │ ├── layout_group.xml │ │ ├── layout_group_item.xml │ │ ├── layout_group_list.xml │ │ ├── layout_icon_list_item_2.xml │ │ ├── layout_import.xml │ │ ├── layout_loading.xml │ │ ├── layout_logcat.xml │ │ ├── layout_loglevel_help.xml │ │ ├── layout_main.xml │ │ ├── layout_mtu_help.xml │ │ ├── layout_network.xml │ │ ├── layout_password_dialog.xml │ │ ├── layout_profile.xml │ │ ├── layout_profile_list.xml │ │ ├── layout_progress.xml │ │ ├── layout_progress_list.xml │ │ ├── layout_route.xml │ │ ├── layout_route_item.xml │ │ ├── layout_scanner.xml │ │ ├── layout_settings_activity.xml │ │ ├── layout_stun.xml │ │ ├── layout_tools.xml │ │ ├── layout_urltest_preference_dialog.xml │ │ ├── layout_webview.xml │ │ └── simple_menu_dropdown_item.xml │ ├── menu/ │ │ ├── add_group_menu.xml │ │ ├── add_profile_menu.xml │ │ ├── add_route_menu.xml │ │ ├── app_list_menu.xml │ │ ├── group_action_menu.xml │ │ ├── import_asset_menu.xml │ │ ├── logcat_menu.xml │ │ ├── main_drawer_menu.xml │ │ ├── per_app_proxy_menu.xml │ │ ├── profile_apply_menu.xml │ │ ├── profile_config_menu.xml │ │ ├── profile_share_menu.xml │ │ ├── scanner_menu.xml │ │ ├── traffic_item_menu.xml │ │ ├── traffic_menu.xml │ │ └── yacd_menu.xml │ ├── mipmap-anydpi-v26/ │ │ └── ic_launcher.xml │ ├── raw/ │ │ ├── insecure.txt │ │ ├── not_encrypted.txt │ │ ├── shadowsocks_stream_cipher.txt │ │ └── vmess_md5_auth.txt │ ├── raw-zh-rCN/ │ │ ├── insecure.txt │ │ ├── not_encrypted.txt │ │ ├── shadowsocks_stream_cipher.txt │ │ └── vmess_md5_auth.txt │ ├── resources.properties │ ├── values/ │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ ├── values-ar/ │ │ └── strings.xml │ ├── values-be/ │ │ └── strings.xml │ ├── values-de/ │ │ └── strings.xml │ ├── values-es/ │ │ └── strings.xml │ ├── values-fa/ │ │ └── strings.xml │ ├── values-fr/ │ │ └── strings.xml │ ├── values-in/ │ │ └── strings.xml │ ├── values-it/ │ │ └── strings.xml │ ├── values-ja/ │ │ └── strings.xml │ ├── values-ko/ │ │ └── strings.xml │ ├── values-nb-rNO/ │ │ └── strings.xml │ ├── values-night/ │ │ └── colors.xml │ ├── values-nl/ │ │ └── strings.xml │ ├── values-pt-rBR/ │ │ └── strings.xml │ ├── values-ru/ │ │ └── strings.xml │ ├── values-tr/ │ │ └── strings.xml │ ├── values-uk/ │ │ └── strings.xml │ ├── values-zh-rCN/ │ │ └── strings.xml │ ├── values-zh-rHK/ │ │ └── strings.xml │ ├── values-zh-rTW/ │ │ └── strings.xml │ └── xml/ │ ├── anytls_preferences.xml │ ├── backup_descriptor.xml │ ├── backup_rules.xml │ ├── balancer_preferences.xml │ ├── cache_paths.xml │ ├── config_preferences.xml │ ├── global_preferences.xml │ ├── group_preferences.xml │ ├── hysteria_preferences.xml │ ├── mieru_preferences.xml │ ├── naive_preferences.xml │ ├── name_preferences.xml │ ├── neko_preferences.xml │ ├── network_security_config.xml │ ├── route_preferences.xml │ ├── shadowsocks_preferences.xml │ ├── shadowtls_preferences.xml │ ├── shortcuts.xml │ ├── socks_preferences.xml │ ├── ssh_preferences.xml │ ├── standard_v2ray_preferences.xml │ ├── trojan_go_preferences.xml │ ├── tuic_preferences.xml │ └── wireguard_preferences.xml ├── build.gradle.kts ├── buildScript/ │ ├── copyLocal.sh │ ├── fdroid/ │ │ └── prebuild.sh │ ├── init/ │ │ ├── action/ │ │ │ └── gradle.sh │ │ ├── env.sh │ │ └── env_ndk.sh │ └── lib/ │ ├── assets.sh │ ├── core/ │ │ ├── build.sh │ │ ├── get_source.sh │ │ ├── get_source_env.sh │ │ └── init.sh │ └── core.sh ├── buildSrc/ │ ├── build.gradle.kts │ └── src/ │ └── main/ │ └── kotlin/ │ └── Helpers.kt ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── libcore/ │ ├── .gitignore │ ├── LICENSE │ ├── assets.go │ ├── assets_android.go │ ├── assets_other.go │ ├── box.go │ ├── box_include.go │ ├── build.sh │ ├── certs.go │ ├── crypto.go │ ├── device/ │ │ ├── debug.go │ │ └── device.go │ ├── dns_android.go │ ├── dns_box.go │ ├── ech/ │ │ └── ech.go │ ├── fix.go │ ├── geoip.go │ ├── geosite.go │ ├── go.mod │ ├── go.sum │ ├── http.go │ ├── init.sh │ ├── interface_monitor.go │ ├── io.go │ ├── nb4a.go │ ├── platform_box.go │ ├── platform_java.go │ ├── procfs/ │ │ └── procfs.go │ ├── stun/ │ │ ├── README │ │ ├── attribute.go │ │ ├── client.go │ │ ├── const.go │ │ ├── discover.go │ │ ├── doc.go │ │ ├── host.go │ │ ├── log.go │ │ ├── net.go │ │ ├── packet.go │ │ ├── response.go │ │ ├── tests.go │ │ └── utils.go │ └── stun.go ├── lint.xml ├── nb4a.properties ├── release.keystore ├── repositories.gradle.kts ├── run └── settings.gradle.kts
SYMBOL INDEX (683 symbols across 60 files)
FILE: app/src/main/java/io/nekohasekai/sagernet/database/ParcelizeBridge.java
class ParcelizeBridge (line 8) | public class ParcelizeBridge {
method createRule (line 10) | public static RuleEntity createRule(Parcel parcel) {
FILE: app/src/main/java/io/nekohasekai/sagernet/database/SubscriptionBean.java
class SubscriptionBean (line 13) | public class SubscriptionBean extends Serializable {
method SubscriptionBean (line 42) | public SubscriptionBean() {
method serializeToBuffer (line 45) | @Override
method serializeForShare (line 64) | public void serializeForShare(ByteBufferOutput output) {
method deserializeFromBuffer (line 77) | @Override
method deserializeFromShare (line 93) | public void deserializeFromShare(ByteBufferInput input) {
method initializeDefaultValues (line 104) | @Override
method newInstance (line 126) | @NonNull
method newArray (line 132) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/AbstractBean.java
class AbstractBean (line 15) | public abstract class AbstractBean extends Serializable {
method displayName (line 31) | public String displayName() {
method displayAddress (line 39) | public String displayAddress() {
method network (line 43) | public String network() {
method canICMPing (line 47) | public boolean canICMPing() {
method canTCPing (line 51) | public boolean canTCPing() {
method canMapping (line 55) | public boolean canMapping() {
method initializeDefaultValues (line 59) | @Override
method serializeToBuffer (line 81) | @Override
method deserializeFromBuffer (line 93) | @Override
method serialize (line 104) | public void serialize(ByteBufferOutput output) {
method deserialize (line 109) | public void deserialize(ByteBufferInput input) {
method clone (line 114) | @NotNull
method equals (line 118) | @Override
method hashCode (line 132) | @Override
method toString (line 142) | @NotNull
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/KryoConverters.java
class KryoConverters (line 34) | public class KryoConverters {
method serialize (line 38) | @TypeConverter
method deserialize (line 49) | public static <T extends Serializable> T deserialize(T bean, byte[] by...
method socksDeserialize (line 62) | @TypeConverter
method httpDeserialize (line 68) | @TypeConverter
method shadowsocksDeserialize (line 74) | @TypeConverter
method configDeserialize (line 80) | @TypeConverter
method vmessDeserialize (line 86) | @TypeConverter
method trojanDeserialize (line 92) | @TypeConverter
method trojanGoDeserialize (line 98) | @TypeConverter
method mieruDeserialize (line 104) | @TypeConverter
method naiveDeserialize (line 110) | @TypeConverter
method hysteriaDeserialize (line 116) | @TypeConverter
method sshDeserialize (line 122) | @TypeConverter
method wireguardDeserialize (line 128) | @TypeConverter
method tuicDeserialize (line 134) | @TypeConverter
method shadowTLSDeserialize (line 140) | @TypeConverter
method anyTLSDeserialize (line 146) | @TypeConverter
method chainDeserialize (line 153) | @TypeConverter
method nekoDeserialize (line 159) | @TypeConverter
method subscriptionDeserialize (line 165) | @TypeConverter
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/gson/GsonConverters.java
class GsonConverters (line 13) | public class GsonConverters {
method toJson (line 15) | @TypeConverter
method toList (line 23) | @TypeConverter
method toSet (line 29) | @TypeConverter
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/http/HttpBean.java
class HttpBean (line 13) | public class HttpBean extends StandardV2RayBean {
method initializeDefaultValues (line 18) | @Override
method serialize (line 25) | @Override
method deserialize (line 33) | @Override
method clone (line 41) | @NotNull
method newInstance (line 48) | @NonNull
method newArray (line 54) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaBean.java
class HysteriaBean (line 15) | public class HysteriaBean extends AbstractBean {
method canMapping (line 49) | @Override
method initializeDefaultValues (line 54) | @Override
method serialize (line 83) | @Override
method deserialize (line 109) | @Override
method displayAddress (line 153) | @Override
method canTCPing (line 158) | @Override
method clone (line 163) | @NotNull
method newInstance (line 170) | @NonNull
method newArray (line 176) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/internal/ChainBean.java
class ChainBean (line 16) | public class ChainBean extends InternalBean {
method displayName (line 20) | @Override
method initializeDefaultValues (line 29) | @Override
method serialize (line 39) | @Override
method deserialize (line 48) | @Override
method clone (line 62) | @NotNull
method newInstance (line 69) | @NonNull
method newArray (line 75) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/internal/InternalBean.java
class InternalBean (line 5) | public abstract class InternalBean extends AbstractBean {
method displayAddress (line 7) | @Override
method canICMPing (line 12) | @Override
method canTCPing (line 17) | @Override
method canMapping (line 22) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/mieru/MieruBean.java
class MieruBean (line 31) | public class MieruBean extends AbstractBean {
method initializeDefaultValues (line 38) | @Override
method serialize (line 47) | @Override
method deserialize (line 59) | @Override
method clone (line 71) | @NotNull
method newInstance (line 78) | @NonNull
method newArray (line 84) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/naive/NaiveBean.java
class NaiveBean (line 13) | public class NaiveBean extends AbstractBean {
method initializeDefaultValues (line 29) | @Override
method serialize (line 43) | @Override
method deserialize (line 58) | @Override
method clone (line 78) | @NotNull
method newInstance (line 85) | @NonNull
method newArray (line 91) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksBean.java
class ShadowsocksBean (line 14) | public class ShadowsocksBean extends AbstractBean {
method initializeDefaultValues (line 22) | @Override
method serialize (line 33) | @Override
method deserialize (line 43) | @Override
method clone (line 53) | @NotNull
method newInstance (line 60) | @NonNull
method newArray (line 66) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/socks/SOCKSBean.java
class SOCKSBean (line 13) | public class SOCKSBean extends AbstractBean {
method protocolVersion (line 19) | public int protocolVersion() {
method protocolName (line 29) | public String protocolName() {
method protocolVersionName (line 40) | public String protocolVersionName() {
method network (line 58) | @Override
method initializeDefaultValues (line 64) | @Override
method serialize (line 74) | @Override
method deserialize (line 84) | @Override
method clone (line 98) | @NotNull
method newInstance (line 105) | @NonNull
method newArray (line 111) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/ssh/SSHBean.java
class SSHBean (line 13) | public class SSHBean extends AbstractBean {
method initializeDefaultValues (line 26) | @Override
method serialize (line 40) | @Override
method deserialize (line 60) | @Override
method clone (line 80) | @NotNull
method newInstance (line 87) | @NonNull
method newArray (line 93) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/trojan/TrojanBean.java
class TrojanBean (line 13) | public class TrojanBean extends StandardV2RayBean {
method initializeDefaultValues (line 17) | @Override
method serialize (line 24) | @Override
method deserialize (line 31) | @Override
method clone (line 50) | @NotNull
method newInstance (line 57) | @NonNull
method newArray (line 63) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/trojan_go/TrojanGoBean.java
class TrojanGoBean (line 14) | public class TrojanGoBean extends AbstractBean {
method initializeDefaultValues (line 92) | @Override
method serialize (line 106) | @Override
method deserialize (line 126) | @Override
method clone (line 149) | @NotNull
method newInstance (line 156) | @NonNull
method newArray (line 162) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/tuic/TuicBean.java
class TuicBean (line 13) | public class TuicBean extends AbstractBean {
method initializeDefaultValues (line 36) | @Override
method serialize (line 55) | @Override
method deserialize (line 75) | @Override
method canTCPing (line 101) | @Override
method clone (line 106) | @NotNull
method newInstance (line 113) | @NonNull
method newArray (line 119) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java
class StandardV2RayBean (line 10) | public abstract class StandardV2RayBean extends AbstractBean {
method initializeDefaultValues (line 69) | @Override
method serialize (line 113) | @Override
method deserialize (line 170) | @Override
method isVLESS (line 261) | public boolean isVLESS() {
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/VMessBean.java
class VMessBean (line 10) | public class VMessBean extends StandardV2RayBean {
method initializeDefaultValues (line 14) | @Override
method clone (line 27) | @NotNull
method newInstance (line 34) | @NonNull
method newArray (line 40) | @Override
FILE: app/src/main/java/io/nekohasekai/sagernet/fmt/wireguard/WireGuardBean.java
class WireGuardBean (line 13) | public class WireGuardBean extends AbstractBean {
method initializeDefaultValues (line 22) | @Override
method serialize (line 33) | @Override
method deserialize (line 45) | @Override
method canTCPing (line 57) | @Override
method clone (line 62) | @NotNull
method newInstance (line 69) | @NonNull
method newArray (line 75) | @Override
FILE: app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java
class SingBoxOptions (line 21) | public class SingBoxOptions {
class SingBoxOption (line 34) | public static class SingBoxOption {
method SingBoxOption (line 40) | public SingBoxOption() {
method asMap (line 44) | public Map<String, Object> asMap() {
class CustomSingBoxOption (line 50) | public static final class CustomSingBoxOption extends SingBoxOption {
method CustomSingBoxOption (line 54) | public CustomSingBoxOption(String config) {
method getBasicMap (line 59) | public Map<String, Object> getBasicMap() {
class SingBoxOptionSerializer (line 69) | public static class SingBoxOptionSerializer implements JsonSerializer<...
method serialize (line 70) | @Override
class User (line 100) | public static class User {
class MyOptions (line 105) | public static class MyOptions extends SingBoxOption {
class ClashAPIOptions (line 124) | public static class ClashAPIOptions extends SingBoxOption {
class SelectorOutboundOptions (line 142) | public static class SelectorOutboundOptions extends SingBoxOption {
class URLTestOutboundOptions (line 151) | public static class URLTestOutboundOptions extends SingBoxOption {
class Options (line 164) | public static class Options extends SingBoxOption {
class LogOptions (line 184) | public static class LogOptions extends SingBoxOption {
class DebugOptions (line 198) | public static class DebugOptions extends SingBoxOption {
class DirectInboundOptions (line 219) | public static class DirectInboundOptions extends SingBoxOption {
class DirectOutboundOptions (line 263) | public static class DirectOutboundOptions extends SingBoxOption {
class DNSOptions (line 304) | public static class DNSOptions extends SingBoxOption {
class DNSServerOptions (line 330) | public static class DNSServerOptions extends SingBoxOption {
class DNSClientOptions (line 348) | public static class DNSClientOptions extends SingBoxOption {
class DNSFakeIPOptions (line 360) | public static class DNSFakeIPOptions extends SingBoxOption {
class ExperimentalOptions (line 370) | public static class ExperimentalOptions extends SingBoxOption {
class CacheFile (line 382) | public static class CacheFile extends SingBoxOption {
class HysteriaInboundOptions (line 394) | public static class HysteriaInboundOptions extends SingBoxOption {
class HysteriaUser (line 454) | public static class HysteriaUser extends SingBoxOption {
class HysteriaOutboundOptions (line 465) | public static class HysteriaOutboundOptions extends SingBoxOption {
class Hysteria2InboundOptions (line 536) | public static class Hysteria2InboundOptions extends SingBoxOption {
class Hysteria2Obfs (line 588) | public static class Hysteria2Obfs extends SingBoxOption {
class Hysteria2User (line 596) | public static class Hysteria2User extends SingBoxOption {
class Hysteria2OutboundOptions (line 604) | public static class Hysteria2OutboundOptions extends SingBoxOption {
class Inbound (line 663) | public static class Inbound extends SingBoxOption {
class InboundOptions (line 703) | public static class InboundOptions extends SingBoxOption {
class ListenOptions (line 715) | public static class ListenOptions extends SingBoxOption {
class NaiveInboundOptions (line 750) | public static class NaiveInboundOptions extends SingBoxOption {
class NTPOptions (line 794) | public static class NTPOptions extends SingBoxOption {
class Outbound (line 843) | public static class Outbound extends SingBoxOption {
class DialerOptions (line 885) | public static class DialerOptions extends SingBoxOption {
class ServerOptions (line 917) | public static class ServerOptions extends SingBoxOption {
class MultiplexOptions (line 925) | public static class MultiplexOptions extends SingBoxOption {
class OnDemandOptions (line 941) | public static class OnDemandOptions extends SingBoxOption {
class OnDemandRule (line 949) | public static class OnDemandRule extends SingBoxOption {
class RedirectInboundOptions (line 969) | public static class RedirectInboundOptions extends SingBoxOption {
class TProxyInboundOptions (line 1007) | public static class TProxyInboundOptions extends SingBoxOption {
class RouteOptions (line 1047) | public static class RouteOptions extends SingBoxOption {
class Rule (line 1069) | public static class Rule extends SingBoxOption {
class RuleSet (line 1079) | public static class RuleSet extends SingBoxOption {
class DefaultRule (line 1093) | public static class DefaultRule extends SingBoxOption {
class DNSRule (line 1163) | public static class DNSRule extends SingBoxOption {
class DefaultDNSRule (line 1173) | public static class DefaultDNSRule extends SingBoxOption {
class ShadowsocksInboundOptions (line 1252) | public static class ShadowsocksInboundOptions extends SingBoxOption {
class ShadowsocksUser (line 1300) | public static class ShadowsocksUser extends SingBoxOption {
class ShadowsocksDestination (line 1308) | public static class ShadowsocksDestination extends SingBoxOption {
class ShadowsocksOutboundOptions (line 1323) | public static class ShadowsocksOutboundOptions extends SingBoxOption {
class ShadowsocksROutboundOptions (line 1379) | public static class ShadowsocksROutboundOptions extends SingBoxOption {
class ShadowTLSInboundOptions (line 1435) | public static class ShadowTLSInboundOptions extends SingBoxOption {
class ShadowTLSUser (line 1485) | public static class ShadowTLSUser extends SingBoxOption {
class ShadowTLSHandshakeOptions (line 1493) | public static class ShadowTLSHandshakeOptions extends SingBoxOption {
class ShadowTLSOutboundOptions (line 1535) | public static class ShadowTLSOutboundOptions extends SingBoxOption {
class SocksInboundOptions (line 1583) | public static class SocksInboundOptions extends SingBoxOption {
class HTTPMixedInboundOptions (line 1623) | public static class HTTPMixedInboundOptions extends SingBoxOption {
class SocksOutboundOptions (line 1667) | public static class SocksOutboundOptions extends SingBoxOption {
class HTTPOutboundOptions (line 1719) | public static class HTTPOutboundOptions extends SingBoxOption {
class SSHOutboundOptions (line 1771) | public static class SSHOutboundOptions extends SingBoxOption {
class InboundTLSOptions (line 1831) | public static class InboundTLSOptions extends SingBoxOption {
class OutboundTLSOptions (line 1867) | public static class OutboundTLSOptions extends SingBoxOption {
class InboundRealityOptions (line 1899) | public static class InboundRealityOptions extends SingBoxOption {
class InboundRealityHandshakeOptions (line 1914) | public static class InboundRealityHandshakeOptions extends SingBoxOpti...
class InboundECHOptions (line 1956) | public static class InboundECHOptions extends SingBoxOption {
class OutboundECHOptions (line 1967) | public static class OutboundECHOptions extends SingBoxOption {
class OutboundUTLSOptions (line 1978) | public static class OutboundUTLSOptions extends SingBoxOption {
class OutboundRealityOptions (line 1986) | public static class OutboundRealityOptions extends SingBoxOption {
class InboundACMEOptions (line 1996) | public static class InboundACMEOptions extends SingBoxOption {
class ACMEExternalAccountOptions (line 2021) | public static class ACMEExternalAccountOptions extends SingBoxOption {
class TorOutboundOptions (line 2029) | public static class TorOutboundOptions extends SingBoxOption {
class TrojanInboundOptions (line 2072) | public static class TrojanInboundOptions extends SingBoxOption {
class TrojanUser (line 2120) | public static class TrojanUser extends SingBoxOption {
class TrojanOutboundOptions (line 2128) | public static class TrojanOutboundOptions extends SingBoxOption {
class TUICInboundOptions (line 2180) | public static class TUICInboundOptions extends SingBoxOption {
class TUICUser (line 2230) | public static class TUICUser extends SingBoxOption {
class TUICOutboundOptions (line 2240) | public static class TUICOutboundOptions extends SingBoxOption {
class TunInboundOptions (line 2300) | public static class TunInboundOptions extends SingBoxOption {
class TunPlatformOptions (line 2370) | public static class TunPlatformOptions extends SingBoxOption {
class HTTPProxyOptions (line 2376) | public static class HTTPProxyOptions extends SingBoxOption {
class UDPOverTCPOptions (line 2390) | public static class UDPOverTCPOptions extends SingBoxOption {
class V2RayAPIOptions (line 2398) | public static class V2RayAPIOptions extends SingBoxOption {
class V2RayStatsServiceOptions (line 2406) | public static class V2RayStatsServiceOptions extends SingBoxOption {
class V2RayTransportOptions (line 2419) | public static class V2RayTransportOptions extends SingBoxOption {
class V2RayHTTPOptions (line 2433) | public static class V2RayHTTPOptions extends SingBoxOption {
class V2RayWebsocketOptions (line 2450) | public static class V2RayWebsocketOptions extends SingBoxOption {
class V2RayGRPCOptions (line 2463) | public static class V2RayGRPCOptions extends SingBoxOption {
class V2RayHTTPUpgradeOptions (line 2477) | public static class V2RayHTTPUpgradeOptions extends SingBoxOption {
class VLESSInboundOptions (line 2487) | public static class VLESSInboundOptions extends SingBoxOption {
class VLESSUser (line 2531) | public static class VLESSUser extends SingBoxOption {
class VLESSOutboundOptions (line 2541) | public static class VLESSOutboundOptions extends SingBoxOption {
class VMessInboundOptions (line 2597) | public static class VMessInboundOptions extends SingBoxOption {
class VMessUser (line 2641) | public static class VMessUser extends SingBoxOption {
class VMessOutboundOptions (line 2651) | public static class VMessOutboundOptions extends SingBoxOption {
class WireGuardOutboundOptions (line 2713) | public static class WireGuardOutboundOptions extends SingBoxOption {
class WireGuardPeer (line 2779) | public static class WireGuardPeer extends SingBoxOption {
class Inbound_TunOptions (line 2800) | public static class Inbound_TunOptions extends Inbound {
class Inbound_RedirectOptions (line 2870) | public static class Inbound_RedirectOptions extends Inbound {
class Inbound_TProxyOptions (line 2907) | public static class Inbound_TProxyOptions extends Inbound {
class Inbound_DirectOptions (line 2946) | public static class Inbound_DirectOptions extends Inbound {
class Inbound_SocksOptions (line 2989) | public static class Inbound_SocksOptions extends Inbound {
class Inbound_HTTPOptions (line 3028) | public static class Inbound_HTTPOptions extends Inbound {
class Inbound_MixedOptions (line 3071) | public static class Inbound_MixedOptions extends Inbound {
class Inbound_ShadowsocksOptions (line 3114) | public static class Inbound_ShadowsocksOptions extends Inbound {
class Inbound_VMessOptions (line 3161) | public static class Inbound_VMessOptions extends Inbound {
class Inbound_TrojanOptions (line 3204) | public static class Inbound_TrojanOptions extends Inbound {
class Inbound_NaiveOptions (line 3251) | public static class Inbound_NaiveOptions extends Inbound {
class Inbound_HysteriaOptions (line 3294) | public static class Inbound_HysteriaOptions extends Inbound {
class Inbound_ShadowTLSOptions (line 3353) | public static class Inbound_ShadowTLSOptions extends Inbound {
class Inbound_VLESSOptions (line 3402) | public static class Inbound_VLESSOptions extends Inbound {
class Inbound_TUICOptions (line 3445) | public static class Inbound_TUICOptions extends Inbound {
class Inbound_Hysteria2Options (line 3494) | public static class Inbound_Hysteria2Options extends Inbound {
class Outbound_DirectOptions (line 3545) | public static class Outbound_DirectOptions extends Outbound {
class Outbound_SocksOptions (line 3585) | public static class Outbound_SocksOptions extends Outbound {
class Outbound_HTTPOptions (line 3636) | public static class Outbound_HTTPOptions extends Outbound {
class Outbound_ShadowsocksOptions (line 3687) | public static class Outbound_ShadowsocksOptions extends Outbound {
class Outbound_VMessOptions (line 3742) | public static class Outbound_VMessOptions extends Outbound {
class Outbound_TrojanOptions (line 3803) | public static class Outbound_TrojanOptions extends Outbound {
class Outbound_WireGuardOptions (line 3854) | public static class Outbound_WireGuardOptions extends Outbound {
class Outbound_HysteriaOptions (line 3919) | public static class Outbound_HysteriaOptions extends Outbound {
class Outbound_TorOptions (line 3989) | public static class Outbound_TorOptions extends Outbound {
class Outbound_SSHOptions (line 4031) | public static class Outbound_SSHOptions extends Outbound {
class Outbound_ShadowTLSOptions (line 4090) | public static class Outbound_ShadowTLSOptions extends Outbound {
class Outbound_ShadowsocksROptions (line 4137) | public static class Outbound_ShadowsocksROptions extends Outbound {
class Outbound_VLESSOptions (line 4192) | public static class Outbound_VLESSOptions extends Outbound {
class Outbound_TUICOptions (line 4247) | public static class Outbound_TUICOptions extends Outbound {
class Outbound_Hysteria2Options (line 4306) | public static class Outbound_Hysteria2Options extends Outbound {
class Outbound_SelectorOptions (line 4363) | public static class Outbound_SelectorOptions extends Outbound {
class Outbound_URLTestOptions (line 4372) | public static class Outbound_URLTestOptions extends Outbound {
class Rule_DefaultOptions (line 4384) | public static class Rule_DefaultOptions extends Rule {
class DNSRule_DefaultOptions (line 4461) | public static class DNSRule_DefaultOptions extends DNSRule {
class V2RayTransportOptions_HTTPOptions (line 4539) | public static class V2RayTransportOptions_HTTPOptions extends V2RayTra...
class V2RayTransportOptions_WebsocketOptions (line 4556) | public static class V2RayTransportOptions_WebsocketOptions extends V2R...
class V2RayTransportOptions_GRPCOptions (line 4569) | public static class V2RayTransportOptions_GRPCOptions extends V2RayTra...
class V2RayTransportOptions_HTTPUpgradeOptions (line 4582) | public static class V2RayTransportOptions_HTTPUpgradeOptions extends V...
class Outbound_AnyTLSOptions (line 4593) | public static class Outbound_AnyTLSOptions extends Outbound {
FILE: app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSBean.java
class AnyTLSBean (line 13) | public class AnyTLSBean extends AbstractBean {
method newInstance (line 16) | @NonNull
method newArray (line 22) | @Override
method initializeDefaultValues (line 39) | @Override
method serialize (line 51) | @Override
method deserialize (line 64) | @Override
method clone (line 77) | @NotNull
FILE: app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigBean.java
class ConfigBean (line 15) | public class ConfigBean extends InternalBean {
method initializeDefaultValues (line 20) | @Override
method serialize (line 27) | @Override
method deserialize (line 35) | @Override
method displayName (line 43) | @Override
method displayType (line 52) | public String displayType() {
method clone (line 65) | @NotNull
method newInstance (line 72) | @NonNull
method newArray (line 78) | @Override
FILE: app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoBean.java
class NekoBean (line 15) | public class NekoBean extends AbstractBean {
method initializeDefaultValues (line 21) | @Override
method serialize (line 28) | @Override
method deserialize (line 37) | @Override
method tryParseJSON (line 46) | @NotNull
method displayType (line 58) | public String displayType() {
method canMapping (line 62) | @Override
method canICMPing (line 67) | @Override
method canTCPing (line 72) | @Override
method clone (line 77) | @NotNull
method newInstance (line 84) | @NonNull
method newArray (line 90) | @Override
FILE: app/src/main/java/moe/matsuri/nb4a/proxy/shadowtls/ShadowTLSBean.java
class ShadowTLSBean (line 13) | public class ShadowTLSBean extends StandardV2RayBean {
method initializeDefaultValues (line 18) | @Override
method serialize (line 27) | @Override
method deserialize (line 35) | @Override
method clone (line 43) | @NotNull
method newInstance (line 50) | @NonNull
method newArray (line 56) | @Override
FILE: app/src/main/java/moe/matsuri/nb4a/utils/JavaUtil.java
class JavaUtil (line 29) | public class JavaUtil {
method unescapeString (line 44) | public static String unescapeString(CharSequence encodedString) {
method handleWebviewDir (line 71) | public static void handleWebviewDir(Context context) {
method tryLockOrRecreateFile (line 113) | @TargetApi(Build.VERSION_CODES.P)
method createFile (line 132) | private static void createFile(File file, boolean deleted) {
method checkIsHuaweiRom (line 142) | private static boolean checkIsHuaweiRom() {
method getProcessName (line 146) | @SuppressLint("PrivateApi")
method isNullOrBlank (line 166) | public static boolean isNullOrBlank(String str) {
method isNotBlank (line 170) | public static boolean isNotBlank(String str) {
method bytesToHex (line 176) | public static String bytesToHex(byte[] bytes) {
method isEmpty (line 186) | public static boolean isEmpty(byte[] array) {
FILE: libcore/assets.go
constant geoipDat (line 4) | geoipDat = "geoip.db"
constant geositeDat (line 5) | geositeDat = "geosite.db"
constant geoipVersion (line 6) | geoipVersion = "geoip.version.txt"
constant geositeVersion (line 7) | geositeVersion = "geosite.version.txt"
constant yacdDstFolder (line 9) | yacdDstFolder = "yacd"
constant yacdVersion (line 10) | yacdVersion = "yacd.version.txt"
FILE: libcore/assets_android.go
function extractAssets (line 16) | func extractAssets() {
function extractAssetName (line 32) | func extractAssetName(name string, useOfficialAssets bool) error {
function extractAsset (line 168) | func extractAsset(i asset.File, path string) error {
FILE: libcore/assets_other.go
function extractAssets (line 5) | func extractAssets() {}
FILE: libcore/box.go
function init (line 31) | func init() {
function VersionBox (line 37) | func VersionBox() string {
function ResetAllConnections (line 61) | func ResetAllConnections(system bool) {
type BoxInstance (line 70) | type BoxInstance struct
method Start (line 128) | func (b *BoxInstance) Start() (err error) {
method Close (line 141) | func (b *BoxInstance) Close() (err error) {
method Sleep (line 170) | func (b *BoxInstance) Sleep() {
method Wake (line 177) | func (b *BoxInstance) Wake() {
method SetAsMain (line 183) | func (b *BoxInstance) SetAsMain() {
method SetV2rayStats (line 188) | func (b *BoxInstance) SetV2rayStats(outbounds string) {
method QueryStats (line 202) | func (b *BoxInstance) QueryStats(tag, direct string) int64 {
method SelectOutbound (line 209) | func (b *BoxInstance) SelectOutbound(tag string) bool {
function NewSingBoxInstance (line 82) | func NewSingBoxInstance(config string, localTransport LocalDNSTransport)...
function UrlTest (line 216) | func UrlTest(i *BoxInstance, link string, timeout int32) (latency int32,...
function goServeProtect (line 239) | func goServeProtect(start bool) {
FILE: libcore/box_include.go
function nekoboxAndroidInboundRegistry (line 45) | func nekoboxAndroidInboundRegistry() *inbound.Registry {
function nekoboxAndroidOutboundRegistry (line 60) | func nekoboxAndroidOutboundRegistry() *outbound.Registry {
function nekoboxAndroidEndpointRegistry (line 91) | func nekoboxAndroidEndpointRegistry() *endpoint.Registry {
function nekoboxAndroidDNSTransportRegistry (line 99) | func nekoboxAndroidDNSTransportRegistry(localTransport LocalDNSTransport...
function nekoboxAndroidServiceRegistry (line 124) | func nekoboxAndroidServiceRegistry() *service.Registry {
FILE: libcore/certs.go
function updateRootCACerts (line 12) | func updateRootCACerts(pem []byte) {
function initSystemRoots (line 24) | func initSystemRoots()
FILE: libcore/crypto.go
function Sha1 (line 9) | func Sha1(data []byte) []byte {
function Sha256Hex (line 14) | func Sha256Hex(data []byte) string {
FILE: libcore/device/debug.go
function GoDebug (line 10) | func GoDebug(any interface{}) {
function DeferPanicToError (line 16) | func DeferPanicToError(name string, onError func(error)) {
FILE: libcore/device/device.go
function NumUDPWorkers (line 7) | func NumUDPWorkers() int {
FILE: libcore/dns_android.go
function init (line 37) | func init() {
FILE: libcore/dns_box.go
type LocalDNSTransport (line 26) | type LocalDNSTransport interface
type platformLocalDNSTransport (line 35) | type platformLocalDNSTransport struct
method Start (line 49) | func (p *platformLocalDNSTransport) Start(stage adapter.StartStage) er...
method Close (line 53) | func (p *platformLocalDNSTransport) Close() error {
method Exchange (line 57) | func (p *platformLocalDNSTransport) Exchange(ctx context.Context, mess...
function newPlatformTransport (line 41) | func newPlatformTransport(iif LocalDNSTransport, tag string, options opt...
type Func (line 123) | type Func interface
type ExchangeContext (line 127) | type ExchangeContext struct
method OnCancel (line 135) | func (c *ExchangeContext) OnCancel(callback Func) {
method Success (line 142) | func (c *ExchangeContext) Success(result string) {
method RawSuccess (line 150) | func (c *ExchangeContext) RawSuccess(result []byte) {
method ErrorCode (line 158) | func (c *ExchangeContext) ErrorCode(code int32) {
method ErrnoCode (line 163) | func (c *ExchangeContext) ErrnoCode(code int32) {
FILE: libcore/ech/ech.go
type ECHClientConfig (line 17) | type ECHClientConfig struct
method Client (line 33) | func (s *ECHClientConfig) Client(ctx context.Context, conn net.Conn) (...
method fetchEchKeys (line 42) | func (s *ECHClientConfig) fetchEchKeys(ctx context.Context, conn net.C...
function NewECHClientConfig (line 23) | func NewECHClientConfig(domain string, tlsConfig *tls.Config, localDnsTr...
FILE: libcore/fix.go
type StringBox (line 6) | type StringBox struct
function wrapString (line 10) | func wrapString(value string) *StringBox {
FILE: libcore/geoip.go
type geoip (line 15) | type geoip struct
method Open (line 19) | func (g *geoip) Open(path string) error {
method Rules (line 25) | func (g *geoip) Rules(countryCode string) ([]option.HeadlessRule, erro...
function init (line 61) | func init() {
FILE: libcore/geosite.go
type geosite (line 13) | type geosite struct
method Open (line 17) | func (g *geosite) Open(path string) error {
method Rules (line 23) | func (g *geosite) Rules(code string) ([]option.HeadlessRule, error) {
function init (line 46) | func init() {
FILE: libcore/http.go
type HTTPClient (line 34) | type HTTPClient interface
type HTTPRequest (line 46) | type HTTPRequest interface
type HTTPResponse (line 57) | type HTTPResponse interface
type httpClient (line 70) | type httpClient struct
method ModernTLS (line 86) | func (c *httpClient) ModernTLS() {
method RestrictedTLS (line 91) | func (c *httpClient) RestrictedTLS() {
method PinnedTLS12 (line 100) | func (c *httpClient) PinnedTLS12() {
method PinnedSHA256 (line 105) | func (c *httpClient) PinnedSHA256(sumHex string) {
method TrySocks5 (line 117) | func (c *httpClient) TrySocks5(port int32) {
method TryH3Direct (line 142) | func (c *httpClient) TryH3Direct() {
method KeepAlive (line 146) | func (c *httpClient) KeepAlive() {
method NewRequest (line 151) | func (c *httpClient) NewRequest() HTTPRequest {
method Close (line 160) | func (c *httpClient) Close() {
function NewHttpClient (line 78) | func NewHttpClient() HTTPClient {
type httpRequest (line 164) | type httpRequest struct
method AllowInsecure (line 169) | func (r *httpRequest) AllowInsecure() {
method SetURL (line 173) | func (r *httpRequest) SetURL(link string) (err error) {
method SetMethod (line 186) | func (r *httpRequest) SetMethod(method string) {
method SetHeader (line 190) | func (r *httpRequest) SetHeader(key string, value string) {
method SetUserAgent (line 194) | func (r *httpRequest) SetUserAgent(userAgent string) {
method SetContent (line 198) | func (r *httpRequest) SetContent(content []byte) {
method SetContentString (line 205) | func (r *httpRequest) SetContentString(content string) {
method Execute (line 209) | func (r *httpRequest) Execute() (HTTPResponse, error) {
method doH3Direct (line 232) | func (r *httpRequest) doH3Direct() (HTTPResponse, error) {
type requestFunc (line 230) | type requestFunc
type httpResponse (line 345) | type httpResponse struct
method errorString (line 353) | func (h *httpResponse) errorString() string {
method GetHeader (line 364) | func (h *httpResponse) GetHeader(key string) *StringBox {
method GetContent (line 368) | func (h *httpResponse) GetContent() ([]byte, error) {
method GetContentString (line 376) | func (h *httpResponse) GetContentString() (*StringBox, error) {
method getContentString (line 384) | func (h *httpResponse) getContentString() (string, error) {
method WriteTo (line 392) | func (h *httpResponse) WriteTo(path string) error {
FILE: libcore/interface_monitor.go
type interfaceMonitorStub (line 11) | type interfaceMonitorStub struct
method Start (line 13) | func (s *interfaceMonitorStub) Start() error {
method Close (line 17) | func (s *interfaceMonitorStub) Close() error {
method DefaultInterface (line 21) | func (s *interfaceMonitorStub) DefaultInterface() *control.Interface {
method OverrideAndroidVPN (line 25) | func (s *interfaceMonitorStub) OverrideAndroidVPN() bool {
method AndroidVPNEnabled (line 29) | func (s *interfaceMonitorStub) AndroidVPNEnabled() bool {
method RegisterCallback (line 33) | func (s *interfaceMonitorStub) RegisterCallback(callback tun.DefaultIn...
method UnregisterCallback (line 37) | func (s *interfaceMonitorStub) UnregisterCallback(element *list.Elemen...
method RegisterMyInterface (line 40) | func (s *interfaceMonitorStub) RegisterMyInterface(interfaceName strin...
method MyInterface (line 43) | func (s *interfaceMonitorStub) MyInterface() string {
FILE: libcore/io.go
function Unxz (line 14) | func Unxz(archive string, path string) error {
function Unzip (line 34) | func Unzip(archive string, path string) error {
FILE: libcore/nb4a.go
function NekoLogPrintln (line 24) | func NekoLogPrintln(s string) {
function NekoLogClear (line 28) | func NekoLogClear() {
function ForceGc (line 32) | func ForceGc() {
function InitCore (line 36) | func InitCore(process, cachePath, internalAssets, externalAssets string,
function sendFdToProtect (line 88) | func sendFdToProtect(fd int, path string) error {
FILE: libcore/platform_box.go
type boxPlatformInterfaceWrapper (line 28) | type boxPlatformInterfaceWrapper struct
method ReadWIFIState (line 30) | func (w *boxPlatformInterfaceWrapper) ReadWIFIState() adapter.WIFIState {
method Initialize (line 38) | func (w *boxPlatformInterfaceWrapper) Initialize(n adapter.NetworkMana...
method UsePlatformAutoDetectInterfaceControl (line 42) | func (w *boxPlatformInterfaceWrapper) UsePlatformAutoDetectInterfaceCo...
method AutoDetectInterfaceControl (line 46) | func (w *boxPlatformInterfaceWrapper) AutoDetectInterfaceControl(fd in...
method OpenTun (line 56) | func (w *boxPlatformInterfaceWrapper) OpenTun(options *tun.Options, pl...
method CloseTun (line 79) | func (w *boxPlatformInterfaceWrapper) CloseTun() error {
method UsePlatformDefaultInterfaceMonitor (line 83) | func (w *boxPlatformInterfaceWrapper) UsePlatformDefaultInterfaceMonit...
method CreateDefaultInterfaceMonitor (line 87) | func (w *boxPlatformInterfaceWrapper) CreateDefaultInterfaceMonitor(l ...
method UsePlatformInterfaceGetter (line 91) | func (w *boxPlatformInterfaceWrapper) UsePlatformInterfaceGetter() bool {
method Interfaces (line 95) | func (w *boxPlatformInterfaceWrapper) Interfaces() ([]adapter.NetworkI...
method IncludeAllNetworks (line 99) | func (w *boxPlatformInterfaceWrapper) IncludeAllNetworks() bool {
method SendNotification (line 103) | func (w *boxPlatformInterfaceWrapper) SendNotification(notification *p...
method SystemCertificates (line 107) | func (s *boxPlatformInterfaceWrapper) SystemCertificates() []string {
method UnderNetworkExtension (line 113) | func (w *boxPlatformInterfaceWrapper) UnderNetworkExtension() bool {
method ClearDNSCache (line 117) | func (w *boxPlatformInterfaceWrapper) ClearDNSCache() {
method FindProcessInfo (line 122) | func (w *boxPlatformInterfaceWrapper) FindProcessInfo(ctx context.Cont...
method Write (line 153) | func (w *boxPlatformInterfaceWrapper) Write(p []byte) (n int, err erro...
type boxPlatformLogWriterWrapper (line 163) | type boxPlatformLogWriterWrapper struct
method DisableColors (line 168) | func (w *boxPlatformLogWriterWrapper) DisableColors() bool { return tr...
method WriteMessage (line 170) | func (w *boxPlatformLogWriterWrapper) WriteMessage(level uint8, messag...
FILE: libcore/platform_java.go
type NB4AInterface (line 9) | type NB4AInterface interface
type BoxPlatformInterface (line 14) | type BoxPlatformInterface interface
FILE: libcore/procfs/procfs.go
function init (line 24) | func init() {
function ResolveSocketByProcSearch (line 33) | func ResolveSocketByProcSearch(network string, source, _ netip.AddrPort)...
function nativeEndianIP (line 91) | func nativeEndianIP(ip net.IP) []byte {
function init (line 103) | func init() {
FILE: libcore/stun.go
type StunResult (line 10) | type StunResult struct
function StunTest (line 15) | func StunTest(server string) *StunResult {
FILE: libcore/stun/attribute.go
type attribute (line 23) | type attribute struct
method xorAddr (line 68) | func (v *attribute) xorAddr(transID []byte) *Host {
method rawAddr (line 94) | func (v *attribute) rawAddr() *Host {
function newAttribute (line 29) | func newAttribute(types uint16, value []byte) *attribute {
function newFingerprintAttribute (line 37) | func newFingerprintAttribute(packet *packet) *attribute {
function newSoftwareAttribute (line 44) | func newSoftwareAttribute(name string) *attribute {
function newChangeReqAttribute (line 48) | func newChangeReqAttribute(changeIP bool, changePort bool) *attribute {
FILE: libcore/stun/client.go
type Client (line 25) | type Client struct
method SetVerbose (line 53) | func (c *Client) SetVerbose(v bool) {
method SetVVerbose (line 59) | func (c *Client) SetVVerbose(v bool) {
method SetServerHost (line 64) | func (c *Client) SetServerHost(host string, port int) {
method SetServerAddr (line 69) | func (c *Client) SetServerAddr(address string) {
method SetSoftwareName (line 75) | func (c *Client) SetSoftwareName(name string) {
method Discover (line 81) | func (c *Client) Discover() (NATType, *Host, error, bool) {
method BehaviorTest (line 102) | func (c *Client) BehaviorTest() (*NATBehavior, error) {
method Keepalive (line 125) | func (c *Client) Keepalive() (*Host, error) {
function NewClient (line 34) | func NewClient() *Client {
function NewClientWithConnection (line 43) | func NewClientWithConnection(conn net.PacketConn) *Client {
FILE: libcore/stun/const.go
constant DefaultServerAddr (line 19) | DefaultServerAddr = "stun.ekiga.net:3478"
constant DefaultSoftwareName (line 20) | DefaultSoftwareName = "StunClient"
constant magicCookie (line 24) | magicCookie = 0x2112A442
constant fingerprint (line 25) | fingerprint = 0x5354554e
type NATType (line 29) | type NATType
method String (line 96) | func (nat NATType) String() string {
type BehaviorType (line 32) | type BehaviorType
method String (line 103) | func (natBhType BehaviorType) String() string {
type NATBehavior (line 34) | type NATBehavior struct
method NormalType (line 110) | func (natBehavior NATBehavior) NormalType() string {
constant NATError (line 41) | NATError NATType = iota
constant NATUnknown (line 42) | NATUnknown
constant NATNone (line 43) | NATNone
constant NATBlocked (line 44) | NATBlocked
constant NATFull (line 45) | NATFull
constant NATSymmetric (line 46) | NATSymmetric
constant NATRestricted (line 47) | NATRestricted
constant NATPortRestricted (line 48) | NATPortRestricted
constant SymmetricUDPFirewall (line 49) | SymmetricUDPFirewall
constant NATSymetric (line 52) | NATSymetric = NATSymmetric
constant NATSymetricUDPFirewall (line 53) | NATSymetricUDPFirewall = SymmetricUDPFirewall
constant NATSymmetricUDPFirewall (line 54) | NATSymmetricUDPFirewall = SymmetricUDPFirewall
constant BehaviorTypeUnknown (line 58) | BehaviorTypeUnknown BehaviorType = iota
constant BehaviorTypeEndpoint (line 59) | BehaviorTypeEndpoint
constant BehaviorTypeAddr (line 60) | BehaviorTypeAddr
constant BehaviorTypeAddrAndPort (line 61) | BehaviorTypeAddrAndPort
function init (line 68) | func init() {
constant errorTryAlternate (line 118) | errorTryAlternate = 300
constant errorBadRequest (line 119) | errorBadRequest = 400
constant errorUnauthorized (line 120) | errorUnauthorized = 401
constant errorUnassigned402 (line 121) | errorUnassigned402 = 402
constant errorForbidden (line 122) | errorForbidden = 403
constant errorUnknownAttribute (line 123) | errorUnknownAttribute = 420
constant errorAllocationMismatch (line 124) | errorAllocationMismatch = 437
constant errorStaleNonce (line 125) | errorStaleNonce = 438
constant errorUnassigned439 (line 126) | errorUnassigned439 = 439
constant errorAddressFamilyNotSupported (line 127) | errorAddressFamilyNotSupported = 440
constant errorWrongCredentials (line 128) | errorWrongCredentials = 441
constant errorUnsupportedTransportProtocol (line 129) | errorUnsupportedTransportProtocol = 442
constant errorPeerAddressFamilyMismatch (line 130) | errorPeerAddressFamilyMismatch = 443
constant errorConnectionAlreadyExists (line 131) | errorConnectionAlreadyExists = 446
constant errorConnectionTimeoutOrFailure (line 132) | errorConnectionTimeoutOrFailure = 447
constant errorAllocationQuotaReached (line 133) | errorAllocationQuotaReached = 486
constant errorRoleConflict (line 134) | errorRoleConflict = 487
constant errorServerError (line 135) | errorServerError = 500
constant errorInsufficientCapacity (line 136) | errorInsufficientCapacity = 508
constant attributeFamilyIPv4 (line 139) | attributeFamilyIPv4 = 0x01
constant attributeFamilyIPV6 (line 140) | attributeFamilyIPV6 = 0x02
constant attributeMappedAddress (line 144) | attributeMappedAddress = 0x0001
constant attributeResponseAddress (line 145) | attributeResponseAddress = 0x0002
constant attributeChangeRequest (line 146) | attributeChangeRequest = 0x0003
constant attributeSourceAddress (line 147) | attributeSourceAddress = 0x0004
constant attributeChangedAddress (line 148) | attributeChangedAddress = 0x0005
constant attributeUsername (line 149) | attributeUsername = 0x0006
constant attributePassword (line 150) | attributePassword = 0x0007
constant attributeMessageIntegrity (line 151) | attributeMessageIntegrity = 0x0008
constant attributeErrorCode (line 152) | attributeErrorCode = 0x0009
constant attributeUnknownAttributes (line 153) | attributeUnknownAttributes = 0x000a
constant attributeReflectedFrom (line 154) | attributeReflectedFrom = 0x000b
constant attributeChannelNumber (line 155) | attributeChannelNumber = 0x000c
constant attributeLifetime (line 156) | attributeLifetime = 0x000d
constant attributeBandwidth (line 157) | attributeBandwidth = 0x0010
constant attributeXorPeerAddress (line 158) | attributeXorPeerAddress = 0x0012
constant attributeData (line 159) | attributeData = 0x0013
constant attributeRealm (line 160) | attributeRealm = 0x0014
constant attributeNonce (line 161) | attributeNonce = 0x0015
constant attributeXorRelayedAddress (line 162) | attributeXorRelayedAddress = 0x0016
constant attributeRequestedAddressFamily (line 163) | attributeRequestedAddressFamily = 0x0017
constant attributeEvenPort (line 164) | attributeEvenPort = 0x0018
constant attributeRequestedTransport (line 165) | attributeRequestedTransport = 0x0019
constant attributeDontFragment (line 166) | attributeDontFragment = 0x001a
constant attributeXorMappedAddress (line 167) | attributeXorMappedAddress = 0x0020
constant attributeTimerVal (line 168) | attributeTimerVal = 0x0021
constant attributeReservationToken (line 169) | attributeReservationToken = 0x0022
constant attributePriority (line 170) | attributePriority = 0x0024
constant attributeUseCandidate (line 171) | attributeUseCandidate = 0x0025
constant attributePadding (line 172) | attributePadding = 0x0026
constant attributeResponsePort (line 173) | attributeResponsePort = 0x0027
constant attributeConnectionID (line 174) | attributeConnectionID = 0x002a
constant attributeXorMappedAddressExp (line 175) | attributeXorMappedAddressExp = 0x8020
constant attributeSoftware (line 176) | attributeSoftware = 0x8022
constant attributeAlternateServer (line 177) | attributeAlternateServer = 0x8023
constant attributeCacheTimeout (line 178) | attributeCacheTimeout = 0x8027
constant attributeFingerprint (line 179) | attributeFingerprint = 0x8028
constant attributeIceControlled (line 180) | attributeIceControlled = 0x8029
constant attributeIceControlling (line 181) | attributeIceControlling = 0x802a
constant attributeResponseOrigin (line 182) | attributeResponseOrigin = 0x802b
constant attributeOtherAddress (line 183) | attributeOtherAddress = 0x802c
constant attributeEcnCheckStun (line 184) | attributeEcnCheckStun = 0x802d
constant attributeCiscoFlowdata (line 185) | attributeCiscoFlowdata = 0xc000
constant typeBindingRequest (line 189) | typeBindingRequest = 0x0001
constant typeBindingResponse (line 190) | typeBindingResponse = 0x0101
constant typeBindingErrorResponse (line 191) | typeBindingErrorResponse = 0x0111
constant typeSharedSecretRequest (line 192) | typeSharedSecretRequest = 0x0002
constant typeSharedSecretResponse (line 193) | typeSharedSecretResponse = 0x0102
constant typeSharedErrorResponse (line 194) | typeSharedErrorResponse = 0x0112
constant typeAllocate (line 195) | typeAllocate = 0x0003
constant typeAllocateResponse (line 196) | typeAllocateResponse = 0x0103
constant typeAllocateErrorResponse (line 197) | typeAllocateErrorResponse = 0x0113
constant typeRefresh (line 198) | typeRefresh = 0x0004
constant typeRefreshResponse (line 199) | typeRefreshResponse = 0x0104
constant typeRefreshErrorResponse (line 200) | typeRefreshErrorResponse = 0x0114
constant typeSend (line 201) | typeSend = 0x0006
constant typeSendResponse (line 202) | typeSendResponse = 0x0106
constant typeSendErrorResponse (line 203) | typeSendErrorResponse = 0x0116
constant typeData (line 204) | typeData = 0x0007
constant typeDataResponse (line 205) | typeDataResponse = 0x0107
constant typeDataErrorResponse (line 206) | typeDataErrorResponse = 0x0117
constant typeCreatePermisiion (line 207) | typeCreatePermisiion = 0x0008
constant typeCreatePermisiionResponse (line 208) | typeCreatePermisiionResponse = 0x0108
constant typeCreatePermisiionErrorResponse (line 209) | typeCreatePermisiionErrorResponse = 0x0118
constant typeChannelBinding (line 210) | typeChannelBinding = 0x0009
constant typeChannelBindingResponse (line 211) | typeChannelBindingResponse = 0x0109
constant typeChannelBindingErrorResponse (line 212) | typeChannelBindingErrorResponse = 0x0119
constant typeConnect (line 213) | typeConnect = 0x000a
constant typeConnectResponse (line 214) | typeConnectResponse = 0x010a
constant typeConnectErrorResponse (line 215) | typeConnectErrorResponse = 0x011a
constant typeConnectionBind (line 216) | typeConnectionBind = 0x000b
constant typeConnectionBindResponse (line 217) | typeConnectionBindResponse = 0x010b
constant typeConnectionBindErrorResponse (line 218) | typeConnectionBindErrorResponse = 0x011b
constant typeConnectionAttempt (line 219) | typeConnectionAttempt = 0x000c
constant typeConnectionAttemptResponse (line 220) | typeConnectionAttemptResponse = 0x010c
constant typeConnectionAttemptErrorResponse (line 221) | typeConnectionAttemptErrorResponse = 0x011c
FILE: libcore/stun/discover.go
method discover (line 66) | func (c *Client) discover(conn net.PacketConn, addr *net.UDPAddr) (_ NAT...
method behaviorTest (line 168) | func (c *Client) behaviorTest(conn net.PacketConn, addr *net.UDPAddr) (*...
FILE: libcore/stun/host.go
type Host (line 23) | type Host struct
method Family (line 46) | func (h *Host) Family() uint16 {
method IP (line 51) | func (h *Host) IP() string {
method Port (line 56) | func (h *Host) Port() uint16 {
method TransportAddr (line 61) | func (h *Host) TransportAddr() string {
method String (line 66) | func (h *Host) String() string {
function newHostFromStr (line 29) | func newHostFromStr(s string) *Host {
FILE: libcore/stun/log.go
type Logger (line 23) | type Logger struct
method SetDebug (line 36) | func (l *Logger) SetDebug(v bool) {
method SetInfo (line 41) | func (l *Logger) SetInfo(v bool) {
method Debug (line 46) | func (l *Logger) Debug(v ...interface{}) {
method Debugf (line 53) | func (l *Logger) Debugf(format string, v ...interface{}) {
method Debugln (line 60) | func (l *Logger) Debugln(v ...interface{}) {
method Info (line 67) | func (l *Logger) Info(v ...interface{}) {
method Infof (line 74) | func (l *Logger) Infof(format string, v ...interface{}) {
method Infoln (line 81) | func (l *Logger) Infoln(v ...interface{}) {
function NewLogger (line 30) | func NewLogger() *Logger {
FILE: libcore/stun/net.go
constant numRetransmit (line 26) | numRetransmit = 9
constant defaultTimeout (line 27) | defaultTimeout = 100
constant maxTimeout (line 28) | maxTimeout = 1600
constant maxPacketSize (line 29) | maxPacketSize = 1024
method sendBindingReq (line 32) | func (c *Client) sendBindingReq(conn net.PacketConn, addr net.Addr, chan...
method send (line 59) | func (c *Client) send(pkt *packet, conn net.PacketConn, addr net.Addr) (...
FILE: libcore/stun/packet.go
type packet (line 24) | type packet struct
method addAttribute (line 72) | func (v *packet) addAttribute(a attribute) {
method bytes (line 77) | func (v *packet) bytes() []byte {
method getSourceAddr (line 93) | func (v *packet) getSourceAddr() *Host {
method getMappedAddr (line 97) | func (v *packet) getMappedAddr() *Host {
method getChangedAddr (line 101) | func (v *packet) getChangedAddr() *Host {
method getOtherAddr (line 105) | func (v *packet) getOtherAddr() *Host {
method getRawAddr (line 109) | func (v *packet) getRawAddr(attribute uint16) *Host {
method getXorMappedAddr (line 118) | func (v *packet) getXorMappedAddr() *Host {
method getXorAddr (line 126) | func (v *packet) getXorAddr(attribute uint16) *Host {
function newPacket (line 31) | func newPacket() (*packet, error) {
function newPacketFromBytes (line 44) | func newPacketFromBytes(packetBytes []byte) (*packet, error) {
FILE: libcore/stun/response.go
type response (line 22) | type response struct
method String (line 65) | func (r *response) String() string {
function newResponse (line 31) | func newResponse(pkt *packet, conn net.PacketConn) *response {
FILE: libcore/stun/tests.go
method sendWithLog (line 22) | func (c *Client) sendWithLog(conn net.PacketConn, addr *net.UDPAddr, cha...
function addrCompare (line 39) | func addrCompare(host *Host, addr *net.UDPAddr, IPChange, portChange boo...
method test (line 45) | func (c *Client) test(conn net.PacketConn, addr *net.UDPAddr) (*response...
method testChangePort (line 49) | func (c *Client) testChangePort(conn net.PacketConn, addr *net.UDPAddr) ...
method testChangeBoth (line 53) | func (c *Client) testChangeBoth(conn net.PacketConn, addr *net.UDPAddr) ...
method test1 (line 57) | func (c *Client) test1(conn net.PacketConn, addr net.Addr) (*response, e...
method test2 (line 61) | func (c *Client) test2(conn net.PacketConn, addr net.Addr) (*response, e...
method test3 (line 65) | func (c *Client) test3(conn net.PacketConn, addr net.Addr) (*response, e...
FILE: libcore/stun/utils.go
function padding (line 22) | func padding(bytes []byte) []byte {
function align (line 29) | func align(n uint16) uint16 {
function isLocalAddress (line 34) | func isLocalAddress(local, localRemote string) bool {
Condensed preview — 524 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,000K chars).
[
{
"path": ".github/ISSUE_TEMPLATE/bug-report-en.md",
"chars": 459,
"preview": "---\nname: 'Bug Report'\nabout: 'Please troubleshoot server-side issues and upgrade to the latest client before raising a "
},
{
"path": ".github/ISSUE_TEMPLATE/bug-report-zh_cn.md",
"chars": 193,
"preview": "---\nname: '问题反馈'\nabout: '在提出问题前请先自行排除服务器端问题和升级到最新客户端。'\ntitle: 'BUG: '\nlabels: ''\nassignees: ''\n\n---\n\n## 描述问题\n\n预期行为:\n\n实际行"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request-en.md",
"chars": 188,
"preview": "---\nname: 'Feature Request'\nabout: 'Make suggestions for new features of the software'\ntitle: ''\nlabels: ''\nassignees: '"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request-zh_cn.md",
"chars": 99,
"preview": "---\nname: '功能请求'\nabout: '对软件的新功能提出建议。'\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## 描述建议\n\n## 建议的必要性\n"
},
{
"path": ".github/workflows/preview.yml",
"chars": 2267,
"preview": "name: Preview Build\non:\n workflow_dispatch:\n inputs:\njobs:\n libcore:\n name: Native Build (LibCore)\n runs-on: "
},
{
"path": ".github/workflows/release.yml",
"chars": 4594,
"preview": "name: Release Build\non:\n workflow_dispatch:\n inputs:\n tag:\n description: \"Release Tag\"\n required:"
},
{
"path": ".gitignore",
"chars": 271,
"preview": "*.iml\n.gradle\n.idea\n.vscode\n.DS_Store\nbuild/\n/captures\n.externalNativeBuild\n.cxx\nlocal.properties\n/app/libs/\n/app/src/ma"
},
{
"path": "AUTHORS",
"chars": 337,
"preview": "SagerNet was originally created in late 2021, by\nnekohasekai <contact-sagernet@sekai.icu>.\n\nHere is an inevitably incomp"
},
{
"path": "LICENSE",
"chars": 674,
"preview": "Copyright (C) 2021 by nekohasekai <contact-sagernet@sekai.icu>\n\nThis program is free software: you can redistribute it a"
},
{
"path": "README.md",
"chars": 2704,
"preview": "# NekoBox for Android\n\n[](https://android-arsen"
},
{
"path": "app/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "app/build.gradle.kts",
"chars": 3015,
"preview": "@file:Suppress(\"UnstableApiUsage\")\n\nplugins {\n id(\"com.android.application\")\n id(\"kotlin-android\")\n id(\"com.goo"
},
{
"path": "app/executableSo/.gitignore",
"chars": 5,
"preview": "*.so\n"
},
{
"path": "app/proguard-rules.pro",
"chars": 1987,
"preview": "-repackageclasses ''\n-allowaccessmodification\n\n-keep class io.nekohasekai.sagernet.** { *;}\n-keep class moe.matsuri.nb4a"
},
{
"path": "app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/1.json",
"chars": 9707,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 1,\n \"identityHash\": \"f66fd943df1d9e86d281a2e32c9fdd47\",\n \"e"
},
{
"path": "app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/2.json",
"chars": 10500,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 2,\n \"identityHash\": \"9ec160533656482a17cbd563e9e3e416\",\n \"e"
},
{
"path": "app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/3.json",
"chars": 10681,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 3,\n \"identityHash\": \"cff00d0142d9e53d2ca24a6a55cd213c\",\n \"e"
},
{
"path": "app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/4.json",
"chars": 10681,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 4,\n \"identityHash\": \"cff00d0142d9e53d2ca24a6a55cd213c\",\n \"e"
},
{
"path": "app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/5.json",
"chars": 10865,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 5,\n \"identityHash\": \"1dbf667053726c13d139a4d83c41f895\",\n \"e"
},
{
"path": "app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/6.json",
"chars": 11090,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 6,\n \"identityHash\": \"3d3db9106a89d6f20ef3fde6e81dbaa9\",\n \"e"
},
{
"path": "app/schemas/io.nekohasekai.sagernet.database.preference.PublicDatabase/1.json",
"chars": 1290,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 1,\n \"identityHash\": \"f1aab1fb633378621635c344dbc8ac7b\",\n \"e"
},
{
"path": "app/schemas/moe.matsuri.nb4a.TempDatabase/1.json",
"chars": 1290,
"preview": "{\n \"formatVersion\": 1,\n \"database\": {\n \"version\": 1,\n \"identityHash\": \"f1aab1fb633378621635c344dbc8ac7b\",\n \"e"
},
{
"path": "app/src/main/AndroidManifest.xml",
"chars": 14927,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:to"
},
{
"path": "app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetService.aidl",
"chars": 328,
"preview": "package io.nekohasekai.sagernet.aidl;\n\nimport io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback;\n\ninterface ISagerNe"
},
{
"path": "app/src/main/aidl/io/nekohasekai/sagernet/aidl/ISagerNetServiceCallback.aidl",
"chars": 443,
"preview": "package io.nekohasekai.sagernet.aidl;\n\nimport io.nekohasekai.sagernet.aidl.SpeedDisplayData;\nimport io.nekohasekai.sager"
},
{
"path": "app/src/main/aidl/io/nekohasekai/sagernet/aidl/SpeedDisplayData.aidl",
"chars": 68,
"preview": "package io.nekohasekai.sagernet.aidl;\n\nparcelable SpeedDisplayData;\n"
},
{
"path": "app/src/main/aidl/io/nekohasekai/sagernet/aidl/TrafficData.aidl",
"chars": 63,
"preview": "package io.nekohasekai.sagernet.aidl;\n\nparcelable TrafficData;\n"
},
{
"path": "app/src/main/assets/LICENSE",
"chars": 673,
"preview": "Copyright (C) 2021 by nekohasekai\n<contact-sagernet@sekai.icu>\n\nThis program is free software: you can\nredistribute it a"
},
{
"path": "app/src/main/assets/proxy_packagename.txt",
"chars": 9126,
"preview": "amanita_design.samorost3.gp\nandroid\nau.com.shiftyjelly.pocketcasts\nbbc.mobile.news.ww\nbe.mygod.vpnhotspot\nch.protonmail."
},
{
"path": "app/src/main/assets/yacd.version.txt",
"chars": 1,
"preview": "3"
},
{
"path": "app/src/main/java/com/github/shadowsocks/plugin/Utils.kt",
"chars": 163,
"preview": "@file:JvmName(\"Utils\")\n\npackage com.github.shadowsocks.plugin\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Par"
},
{
"path": "app/src/main/java/com/github/shadowsocks/plugin/fragment/AlertDialogFragment.kt",
"chars": 2684,
"preview": "package com.github.shadowsocks.plugin.fragment\n\nimport android.app.Activity\nimport android.content.DialogInterface\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/BootReceiver.kt",
"chars": 1624,
"preview": "package io.nekohasekai.sagernet\n\nimport android.content.BroadcastReceiver\nimport android.content.ComponentName\nimport an"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/Constants.kt",
"chars": 6719,
"preview": "package io.nekohasekai.sagernet\n\nconst val CONNECTION_TEST_URL = \"http://cp.cloudflare.com/\"\n\nobject Key {\n\n const va"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/QuickToggleShortcut.kt",
"chars": 4225,
"preview": "/*******************************************************************************\n * "
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/SagerNet.kt",
"chars": 7752,
"preview": "package io.nekohasekai.sagernet\n\nimport android.annotation.SuppressLint\nimport android.app.*\nimport android.content.Clip"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/aidl/SpeedDisplayData.kt",
"chars": 452,
"preview": "package io.nekohasekai.sagernet.aidl\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n@Parcelize\ndata c"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/aidl/TrafficData.kt",
"chars": 222,
"preview": "package io.nekohasekai.sagernet.aidl\n\nimport android.os.Parcelable\nimport kotlinx.parcelize.Parcelize\n\n@Parcelize\ndata c"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/AbstractInstance.kt",
"chars": 123,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport java.io.Closeable\n\ninterface AbstractInstance : Closeable {\n\n fun launch()"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt",
"chars": 14879,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.app.Service\nimport android.content.Context\nimport android.content.Int"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/Executable.kt",
"chars": 1382,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.system.ErrnoException\nimport android.system.Os\nimport android.system."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/GuardedProcessPool.kt",
"chars": 5206,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.os.Build\nimport android.os.SystemClock\nimport android.system.ErrnoExc"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/ProxyService.kt",
"chars": 1064,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.annotation.SuppressLint\nimport android.app.Service\nimport android.con"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/SagerConnection.kt",
"chars": 5801,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.c"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/ServiceNotification.kt",
"chars": 8144,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.app.PendingIntent\nimport android.app.Service\nimport android.content.B"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/SubscriptionUpdater.kt",
"chars": 3789,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.content.Context\nimport androidx.core.app.NotificationCompat\nimport an"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/TileService.kt",
"chars": 3173,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.graphics.drawable.Icon\nimport android.service.quicksettings.Tile\nimpo"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/VpnService.kt",
"chars": 7839,
"preview": "package io.nekohasekai.sagernet.bg\n\nimport android.Manifest\nimport android.annotation.SuppressLint\nimport android.app.Se"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt",
"chars": 8301,
"preview": "package io.nekohasekai.sagernet.bg.proto\n\nimport android.os.SystemClock\nimport io.nekohasekai.sagernet.SagerNet\nimport i"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/proto/ProxyInstance.kt",
"chars": 1743,
"preview": "package io.nekohasekai.sagernet.bg.proto\n\nimport io.nekohasekai.sagernet.BuildConfig\nimport io.nekohasekai.sagernet.bg.B"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt",
"chars": 1767,
"preview": "package io.nekohasekai.sagernet.bg.proto\n\nimport io.nekohasekai.sagernet.BuildConfig\nimport io.nekohasekai.sagernet.bg.G"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/proto/TrafficLooper.kt",
"chars": 6527,
"preview": "package io.nekohasekai.sagernet.bg.proto\n\nimport io.nekohasekai.sagernet.aidl.SpeedDisplayData\nimport io.nekohasekai.sag"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/proto/TrafficUpdater.kt",
"chars": 1990,
"preview": "package io.nekohasekai.sagernet.bg.proto\n\nclass TrafficUpdater(\n private val box: libcore.BoxInstance,\n val items:"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/bg/proto/UrlTest.kt",
"chars": 358,
"preview": "package io.nekohasekai.sagernet.bg.proto\n\nimport io.nekohasekai.sagernet.database.DataStore\nimport io.nekohasekai.sagern"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt",
"chars": 12871,
"preview": "package io.nekohasekai.sagernet.database\n\nimport android.os.Binder\nimport androidx.preference.PreferenceDataStore\nimport"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/GroupManager.kt",
"chars": 3468,
"preview": "package io.nekohasekai.sagernet.database\n\nimport io.nekohasekai.sagernet.GroupType\nimport io.nekohasekai.sagernet.bg.Sub"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/ParcelizeBridge.java",
"chars": 302,
"preview": "package io.nekohasekai.sagernet.database;\n\nimport android.os.Parcel;\n\n/**\n * see: https://youtrack.jetbrains.com/issue/K"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/ProfileManager.kt",
"chars": 7575,
"preview": "package io.nekohasekai.sagernet.database\n\nimport android.database.sqlite.SQLiteCantOpenDatabaseException\nimport io.nekoh"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/ProxyEntity.kt",
"chars": 18078,
"preview": "package io.nekohasekai.sagernet.database\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.r"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt",
"chars": 4083,
"preview": "package io.nekohasekai.sagernet.database\n\nimport androidx.room.*\nimport com.esotericsoftware.kryo.io.ByteBufferInput\nimp"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/RuleEntity.kt",
"chars": 3343,
"preview": "package io.nekohasekai.sagernet.database\n\nimport android.os.Parcelable\nimport androidx.room.*\nimport io.nekohasekai.sage"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/SagerDatabase.kt",
"chars": 1945,
"preview": "package io.nekohasekai.sagernet.database\n\nimport androidx.room.AutoMigration\nimport androidx.room.Database\nimport androi"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/StringCollectionConverter.kt",
"chars": 988,
"preview": "package io.nekohasekai.sagernet.database\n\nimport androidx.room.TypeConverter\n\nclass StringCollectionConverter {\n comp"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/SubscriptionBean.java",
"chars": 4066,
"preview": "package io.nekohasekai.sagernet.database;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.Byte"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/preference/EditTextPreferenceModifiers.kt",
"chars": 1429,
"preview": "package io.nekohasekai.sagernet.database.preference\n\nimport android.graphics.Typeface\nimport android.text.InputFilter\nim"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/preference/KeyValuePair.kt",
"chars": 4952,
"preview": "package io.nekohasekai.sagernet.database.preference\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport androi"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/preference/OnPreferenceDataStoreChangeListener.kt",
"chars": 229,
"preview": "package io.nekohasekai.sagernet.database.preference\n\nimport androidx.preference.PreferenceDataStore\n\ninterface OnPrefere"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/preference/PublicDatabase.kt",
"chars": 1133,
"preview": "package io.nekohasekai.sagernet.database.preference\n\nimport androidx.room.Database\nimport androidx.room.Room\nimport andr"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/database/preference/RoomPreferenceDataStore.kt",
"chars": 3363,
"preview": "package io.nekohasekai.sagernet.database.preference\n\nimport androidx.preference.PreferenceDataStore\n\n@Suppress(\"MemberVi"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/AbstractBean.java",
"chars": 3753,
"preview": "package io.nekohasekai.sagernet.fmt;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.ByteBuffe"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt",
"chars": 29192,
"preview": "package io.nekohasekai.sagernet.fmt\n\nimport android.widget.Toast\nimport io.nekohasekai.sagernet.*\nimport io.nekohasekai."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/KryoConverters.java",
"chars": 5785,
"preview": "package io.nekohasekai.sagernet.fmt;\n\nimport androidx.room.TypeConverter;\n\nimport com.esotericsoftware.kryo.KryoExceptio"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/PluginEntry.kt",
"chars": 1936,
"preview": "package io.nekohasekai.sagernet.fmt\n\nimport io.nekohasekai.sagernet.R\nimport io.nekohasekai.sagernet.SagerNet\n\nenum clas"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/Serializable.kt",
"chars": 850,
"preview": "package io.nekohasekai.sagernet.fmt\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.esotericsoftware.k"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/TypeMap.kt",
"chars": 999,
"preview": "package io.nekohasekai.sagernet.fmt\n\nimport io.nekohasekai.sagernet.database.ProxyEntity\n\nobject TypeMap : HashMap<Strin"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/UniversalFmt.kt",
"chars": 1287,
"preview": "package io.nekohasekai.sagernet.fmt\n\nimport io.nekohasekai.sagernet.database.ProxyEntity\nimport io.nekohasekai.sagernet."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/gson/GsonConverters.java",
"chars": 934,
"preview": "package io.nekohasekai.sagernet.fmt.gson;\n\nimport androidx.room.TypeConverter;\n\nimport java.util.Collection;\nimport java"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/http/HttpBean.java",
"chars": 1559,
"preview": "package io.nekohasekai.sagernet.fmt.http;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.Byte"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/http/HttpFmt.kt",
"chars": 1282,
"preview": "package io.nekohasekai.sagernet.fmt.http\n\nimport io.nekohasekai.sagernet.fmt.v2ray.isTLS\nimport io.nekohasekai.sagernet."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaBean.java",
"chars": 5652,
"preview": "package io.nekohasekai.sagernet.fmt.hysteria;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/hysteria/HysteriaFmt.kt",
"chars": 12517,
"preview": "package io.nekohasekai.sagernet.fmt.hysteria\n\nimport io.nekohasekai.sagernet.database.DataStore\nimport io.nekohasekai.sa"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/internal/ChainBean.java",
"chars": 1978,
"preview": "package io.nekohasekai.sagernet.fmt.internal;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/internal/InternalBean.java",
"chars": 464,
"preview": "package io.nekohasekai.sagernet.fmt.internal;\n\nimport io.nekohasekai.sagernet.fmt.AbstractBean;\n\npublic abstract class I"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/mieru/MieruBean.java",
"chars": 3295,
"preview": "/******************************************************************************\n * Copyright (C) 2022 by nekohasekai <co"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/mieru/MieruFmt.kt",
"chars": 2507,
"preview": "/******************************************************************************\n * Copyright (C) 2022 by nekohasekai <co"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/naive/NaiveBean.java",
"chars": 2806,
"preview": "package io.nekohasekai.sagernet.fmt.naive;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.Byt"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/naive/NaiveFmt.kt",
"chars": 3155,
"preview": "package io.nekohasekai.sagernet.fmt.naive\n\nimport io.nekohasekai.sagernet.database.DataStore\nimport io.nekohasekai.sager"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksBean.java",
"chars": 1990,
"preview": "package io.nekohasekai.sagernet.fmt.shadowsocks;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/shadowsocks/ShadowsocksFmt.kt",
"chars": 3975,
"preview": "package io.nekohasekai.sagernet.fmt.shadowsocks\n\nimport io.nekohasekai.sagernet.ktx.*\nimport moe.matsuri.nb4a.SingBoxOpt"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/socks/SOCKSBean.java",
"chars": 2889,
"preview": "package io.nekohasekai.sagernet.fmt.socks;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.Byt"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/socks/SOCKSFmt.kt",
"chars": 2391,
"preview": "package io.nekohasekai.sagernet.fmt.socks\n\nimport io.nekohasekai.sagernet.ktx.decodeBase64UrlSafe\nimport io.nekohasekai."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/ssh/SSHBean.java",
"chars": 2913,
"preview": "package io.nekohasekai.sagernet.fmt.ssh;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.ByteB"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/ssh/SSHFmt.kt",
"chars": 805,
"preview": "package io.nekohasekai.sagernet.fmt.ssh\n\nimport moe.matsuri.nb4a.SingBoxOptions\nimport moe.matsuri.nb4a.utils.listByLine"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/trojan/TrojanBean.java",
"chars": 1968,
"preview": "package io.nekohasekai.sagernet.fmt.trojan;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.By"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/trojan/TrojanFmt.kt",
"chars": 593,
"preview": "package io.nekohasekai.sagernet.fmt.trojan\n\nimport io.nekohasekai.sagernet.fmt.v2ray.parseDuckSoft\nimport okhttp3.HttpUr"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/trojan_go/TrojanGoBean.java",
"chars": 4226,
"preview": "package io.nekohasekai.sagernet.fmt.trojan_go;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/trojan_go/TrojanGoFmt.kt",
"chars": 5025,
"preview": "package io.nekohasekai.sagernet.fmt.trojan_go\n\nimport io.nekohasekai.sagernet.IPv6Mode\nimport io.nekohasekai.sagernet.da"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/tuic/TuicBean.java",
"chars": 3692,
"preview": "package io.nekohasekai.sagernet.fmt.tuic;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.Byte"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/tuic/TuicFmt.kt",
"chars": 3358,
"preview": "package io.nekohasekai.sagernet.fmt.tuic\n\nimport io.nekohasekai.sagernet.database.DataStore\nimport io.nekohasekai.sagern"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/StandardV2RayBean.java",
"chars": 7869,
"preview": "package io.nekohasekai.sagernet.fmt.v2ray;\n\nimport com.esotericsoftware.kryo.io.ByteBufferInput;\nimport com.esotericsoft"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/V2RayFmt.kt",
"chars": 20876,
"preview": "package io.nekohasekai.sagernet.fmt.v2ray\n\nimport android.text.TextUtils\nimport com.google.gson.Gson\nimport io.nekohasek"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/v2ray/VMessBean.java",
"chars": 1178,
"preview": "package io.nekohasekai.sagernet.fmt.v2ray;\n\nimport androidx.annotation.NonNull;\n\nimport org.jetbrains.annotations.NotNul"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/wireguard/WireGuardBean.java",
"chars": 2337,
"preview": "package io.nekohasekai.sagernet.fmt.wireguard;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/fmt/wireguard/WireGuardFmt.kt",
"chars": 1344,
"preview": "package io.nekohasekai.sagernet.fmt.wireguard\n\nimport moe.matsuri.nb4a.SingBoxOptions\nimport moe.matsuri.nb4a.utils.Util"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/group/GroupInterfaceAdapter.kt",
"chars": 3754,
"preview": "package io.nekohasekai.sagernet.group\n\nimport com.google.android.material.dialog.MaterialAlertDialogBuilder\nimport io.ne"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/group/GroupUpdater.kt",
"chars": 6157,
"preview": "package io.nekohasekai.sagernet.group\n\nimport io.nekohasekai.sagernet.*\nimport io.nekohasekai.sagernet.database.DataStor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt",
"chars": 37024,
"preview": "package io.nekohasekai.sagernet.group\n\nimport android.annotation.SuppressLint\nimport io.nekohasekai.sagernet.R\nimport io"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Asyncs.kt",
"chars": 1384,
"preview": "@file:Suppress(\"EXPERIMENTAL_API_USAGE\")\n\npackage io.nekohasekai.sagernet.ktx\n\nimport androidx.fragment.app.Fragment\nimp"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Browsers.kt",
"chars": 1016,
"preview": " package io.nekohasekai.sagernet.ktx\n\nimport android.content.Context\nimport android.net.Uri\nimport androidx.browser.cus"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Dialogs.kt",
"chars": 771,
"preview": "package io.nekohasekai.sagernet.ktx\n\nimport android.app.Activity\nimport android.content.Context\nimport androidx.appcompa"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Dimens.kt",
"chars": 307,
"preview": "package io.nekohasekai.sagernet.ktx\n\nimport android.content.res.Resources\nimport kotlin.math.ceil\n\nprivate val density ="
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Formats.kt",
"chars": 7555,
"preview": "package io.nekohasekai.sagernet.ktx\n\nimport com.google.gson.JsonParser\nimport io.nekohasekai.sagernet.fmt.AbstractBean\ni"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Kryos.kt",
"chars": 1489,
"preview": "package io.nekohasekai.sagernet.ktx\n\nimport android.os.Parcel\nimport android.os.Parcelable\nimport com.esotericsoftware.k"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Layouts.kt",
"chars": 2646,
"preview": "package io.nekohasekai.sagernet.ktx\n\nimport android.graphics.Rect\nimport androidx.recyclerview.widget.LinearLayoutManage"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Logs.kt",
"chars": 1779,
"preview": "package io.nekohasekai.sagernet.ktx\n\nimport libcore.Libcore\nimport java.io.InputStream\nimport java.io.OutputStream\n\nobje"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Nets.kt",
"chars": 1861,
"preview": "@file:Suppress(\"SpellCheckingInspection\")\n\npackage io.nekohasekai.sagernet.ktx\n\nimport io.nekohasekai.sagernet.BuildConf"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Preferences.kt",
"chars": 2082,
"preview": "package io.nekohasekai.sagernet.ktx\n\nimport androidx.preference.PreferenceDataStore\nimport kotlin.reflect.KProperty\n\nfun"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ktx/Utils.kt",
"chars": 11277,
"preview": "@file:SuppressLint(\"SoonBlockedPrivateApi\")\n\npackage io.nekohasekai.sagernet.ktx\n\nimport android.animation.Animator\nimpo"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/plugin/PluginManager.kt",
"chars": 3093,
"preview": "package io.nekohasekai.sagernet.plugin\n\nimport android.content.pm.ComponentInfo\nimport android.content.pm.ProviderInfo\ni"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt",
"chars": 13781,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.In"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/AppListActivity.kt",
"chars": 12383,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.Intent\nimport android.content.pm.ApplicationInfo\nimport andro"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/AppManagerActivity.kt",
"chars": 15864,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport android."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt",
"chars": 12052,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.os.Bundle\nimport android.provider.OpenableColumns\nimport android.text"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/BackupFragment.kt",
"chars": 12382,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Bundle\nimport"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/BlankActivity.kt",
"chars": 464,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AppCompatActivity\nimport moe."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt",
"chars": 70414,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport android."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/GroupFragment.kt",
"chars": 20580,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.text.format.Fo"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt",
"chars": 14532,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport android.co"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/LogcatFragment.kt",
"chars": 3777,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.annotation.SuppressLint\nimport android.graphics.Color\nimport android."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/MainActivity.kt",
"chars": 17058,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.Manifest.permission.POST_NOTIFICATIONS\nimport android.annotation.Supp"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/NamedFragment.kt",
"chars": 318,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport androidx.fragment.app.Fragment\n\nabstract class NamedFragment : Fragment {\n\n "
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/NetworkFragment.kt",
"chars": 710,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.View\nimpo"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/ProfileSelectActivity.kt",
"chars": 1019,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.Intent\nimport android.os.Bundle\nimport io.nekohasekai.sagerne"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/QuickDisableShortcut.kt",
"chars": 2759,
"preview": "/*******************************************************************************\n * "
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/QuickEnableShortcut.kt",
"chars": 2769,
"preview": "/*******************************************************************************\n * "
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/RouteFragment.kt",
"chars": 11256,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.MenuItem\n"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/RouteSettingsActivity.kt",
"chars": 13232,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.app.Activity\nimport android.content.DialogInterface\nimport android.co"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/ScannerActivity.kt",
"chars": 7844,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.Manifest\nimport android.content.Intent\nimport android.content.pm.Shor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/SettingsFragment.kt",
"chars": 691,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.core.view.ViewComp"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt",
"chars": 7754,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.Intent\nimport android.os.Build\nimport android.os.Bundle\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/StunActivity.kt",
"chars": 2206,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.os.Bundle\nimport androidx.appcompat.app.AlertDialog\nimport androidx.c"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/SwitchActivity.kt",
"chars": 1117,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.os.Bundle\nimport io.nekohasekai.sagernet.R\nimport io.nekohasekai.sage"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/ThemedActivity.kt",
"chars": 2445,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.content.res.Configuration\nimport android.os.Build\nimport android.os.B"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/ToolbarFragment.kt",
"chars": 945,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.os.Bundle\nimport android.view.KeyEvent\nimport android.view.View\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/ToolsFragment.kt",
"chars": 1274,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.Fragm"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/VpnRequestActivity.kt",
"chars": 2779,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.app.Activity\nimport android.app.KeyguardManager\nimport android.conten"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/WebviewFragment.kt",
"chars": 2884,
"preview": "package io.nekohasekai.sagernet.ui\n\nimport android.annotation.SuppressLint\nimport android.os.Bundle\nimport android.text."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/ChainSettingsActivity.kt",
"chars": 10673,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.annotation.SuppressLint\nimport android.app.Activity\nimport an"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/ConfigEditActivity.kt",
"chars": 6699,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.annotation.SuppressLint\nimport android.content.DialogInterfac"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/HttpSettingsActivity.kt",
"chars": 206,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport io.nekohasekai.sagernet.fmt.http.HttpBean\n\nclass HttpSettingsActivity"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/HysteriaSettingsActivity.kt",
"chars": 6202,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/MieruSettingsActivity.kt",
"chars": 3614,
"preview": "/******************************************************************************\n * "
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/NaiveSettingsActivity.kt",
"chars": 2593,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/ProfileSettingsActivity.kt",
"chars": 15644,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.annotation.SuppressLint\nimport android.content.DialogInterfac"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/SSHSettingsActivity.kt",
"chars": 3030,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/ShadowsocksSettingsActivity.kt",
"chars": 2462,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/SocksSettingsActivity.kt",
"chars": 2233,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/StandardV2RaySettingsActivity.kt",
"chars": 8116,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/TrojanGoSettingsActivity.kt",
"chars": 4544,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/TrojanSettingsActivity.kt",
"chars": 214,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport io.nekohasekai.sagernet.fmt.trojan.TrojanBean\n\nclass TrojanSettingsAc"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/TuicSettingsActivity.kt",
"chars": 2525,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/VMessSettingsActivity.kt",
"chars": 321,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport io.nekohasekai.sagernet.fmt.v2ray.VMessBean\n\nclass VMessSettingsActiv"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/ui/profile/WireGuardSettingsActivity.kt",
"chars": 2134,
"preview": "package io.nekohasekai.sagernet.ui.profile\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimpor"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/utils/Commandline.kt",
"chars": 5330,
"preview": "package io.nekohasekai.sagernet.utils\n\nimport java.util.*\n\n/**\n * Commandline objects help handling command lines specif"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/utils/CrashHandler.kt",
"chars": 6408,
"preview": "package io.nekohasekai.sagernet.utils\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport andro"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/utils/DefaultNetworkListener.kt",
"chars": 6550,
"preview": "package io.nekohasekai.sagernet.utils\n\nimport android.annotation.TargetApi\nimport android.net.ConnectivityManager\nimport"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/utils/PackageCache.kt",
"chars": 3106,
"preview": "package io.nekohasekai.sagernet.utils\n\nimport android.Manifest\nimport android.annotation.SuppressLint\nimport android.con"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/utils/Subnet.kt",
"chars": 3263,
"preview": "package io.nekohasekai.sagernet.utils\n\nimport io.nekohasekai.sagernet.ktx.parseNumericAddress\nimport java.net.InetAddres"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/utils/Theme.kt",
"chars": 4706,
"preview": "package io.nekohasekai.sagernet.utils\n\nimport android.content.Context\nimport android.content.res.Configuration\nimport an"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/AppListPreference.kt",
"chars": 1475,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/AutoCollapseTextView.kt",
"chars": 1203,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport and"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/FabProgressBehavior.kt",
"chars": 1048,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport android.v"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/GroupPreference.kt",
"chars": 919,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport io.nekoha"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/LinkOrContentPreference.kt",
"chars": 2292,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.content.Context\nimport android.net.Uri\nimport android.util.Attrib"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/OutboundPreference.kt",
"chars": 998,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport io.nekoha"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/QRCodeDialog.kt",
"chars": 3912,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.graphics.Bitmap\nimport android.graphics.Color\nimport android.os.B"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/ServiceButton.kt",
"chars": 5823,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.content.Context\nimport android.graphics.drawable.Drawable\nimport "
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/StatsBar.kt",
"chars": 5301,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.annotation.SuppressLint\nimport android.content.Context\nimport and"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/UndoSnackbarManager.kt",
"chars": 1714,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport com.google.android.material.snackbar.Snackbar\nimport io.nekohasekai.sager"
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/UserAgentPreference.kt",
"chars": 814,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.content.Context\nimport android.util.AttributeSet\nimport androidx."
},
{
"path": "app/src/main/java/io/nekohasekai/sagernet/widget/WindowInsetsListeners.kt",
"chars": 462,
"preview": "package io.nekohasekai.sagernet.widget\n\nimport android.view.View\nimport androidx.core.view.OnApplyWindowInsetsListener\ni"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/NativeInterface.kt",
"chars": 3675,
"preview": "package moe.matsuri.nb4a\n\nimport android.content.Context\nimport android.net.ConnectivityManager\nimport android.net.wifi."
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/Protocols.kt",
"chars": 1780,
"preview": "package moe.matsuri.nb4a\n\nimport android.content.Context\nimport io.nekohasekai.sagernet.R\nimport io.nekohasekai.sagernet"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/SingBoxOptions.java",
"chars": 99479,
"preview": "package moe.matsuri.nb4a;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.JsonE"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/SingBoxOptionsUtil.kt",
"chars": 6027,
"preview": "package moe.matsuri.nb4a\n\nimport io.nekohasekai.sagernet.database.DataStore\nimport moe.matsuri.nb4a.SingBoxOptions.RuleS"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/TempDatabase.kt",
"chars": 915,
"preview": "package moe.matsuri.nb4a\n\nimport androidx.room.Database\nimport androidx.room.Room\nimport androidx.room.RoomDatabase\nimpo"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/net/LocalResolverImpl.kt",
"chars": 5120,
"preview": "package moe.matsuri.nb4a.net\n\nimport android.net.DnsResolver\nimport android.os.Build\nimport android.os.CancellationSigna"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/plugin/Plugins.kt",
"chars": 4217,
"preview": "package moe.matsuri.nb4a.plugin\n\nimport android.content.Intent\nimport android.content.pm.PackageInfo\nimport android.cont"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/PreferenceBinding.kt",
"chars": 2985,
"preview": "package moe.matsuri.nb4a.proxy\n\nimport androidx.preference.Preference\nimport androidx.preference.PreferenceFragmentCompa"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/PreferenceBindingManager.kt",
"chars": 663,
"preview": "package moe.matsuri.nb4a.proxy\n\nimport androidx.preference.PreferenceFragmentCompat\n\n\nclass PreferenceBindingManager {\n "
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSBean.java",
"chars": 2605,
"preview": "package moe.matsuri.nb4a.proxy.anytls;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.ByteBuf"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSFmt.kt",
"chars": 2743,
"preview": "package moe.matsuri.nb4a.proxy.anytls\n\nimport io.nekohasekai.sagernet.ktx.blankAsNull\nimport io.nekohasekai.sagernet.ktx"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/anytls/AnyTLSSettingsActivity.kt",
"chars": 2117,
"preview": "package moe.matsuri.nb4a.proxy.anytls\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimport and"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigBean.java",
"chars": 2344,
"preview": "package moe.matsuri.nb4a.proxy.config;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.ByteBuf"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/config/ConfigSettingActivity.kt",
"chars": 1876,
"preview": "package moe.matsuri.nb4a.proxy.config\n\nimport android.os.Bundle\nimport androidx.preference.PreferenceDataStore\nimport an"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/neko/NekoBean.java",
"chars": 2383,
"preview": "package moe.matsuri.nb4a.proxy.neko;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.ByteBuffe"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/shadowtls/ShadowTLSBean.java",
"chars": 1622,
"preview": "package moe.matsuri.nb4a.proxy.shadowtls;\n\nimport androidx.annotation.NonNull;\n\nimport com.esotericsoftware.kryo.io.Byte"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/shadowtls/ShadowTLSFmt.kt",
"chars": 529,
"preview": "package moe.matsuri.nb4a.proxy.shadowtls\n\nimport io.nekohasekai.sagernet.fmt.v2ray.buildSingBoxOutboundTLS\nimport moe.ma"
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/proxy/shadowtls/ShadowTLSSettingsActivity.kt",
"chars": 2168,
"preview": "package moe.matsuri.nb4a.proxy.shadowtls\n\nimport android.os.Bundle\nimport androidx.preference.EditTextPreference\nimport "
},
{
"path": "app/src/main/java/moe/matsuri/nb4a/ui/ColorPickerPreference.kt",
"chars": 3714,
"preview": "package moe.matsuri.nb4a.ui\n\nimport android.content.Context\nimport android.content.res.Resources\nimport android.graphics"
}
]
// ... and 324 more files (download for full content)
About this extraction
This page contains the full source code of the MatsuriDayo/NekoBoxForAndroid GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 524 files (1.8 MB), approximately 475.5k tokens, and a symbol index with 683 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.