├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── top │ │ └── canyie │ │ └── settingsfirewall │ │ ├── ISettingsFirewall.aidl │ │ └── Replacement.aidl │ ├── assets │ └── xposed_init │ ├── java │ └── top │ │ └── canyie │ │ └── settingsfirewall │ │ ├── App.java │ │ ├── AppInfo.java │ │ ├── AppListAdapter.java │ │ ├── MainActivity.java │ │ ├── Replacement.java │ │ ├── SettingListAdapter.java │ │ ├── SettingsEditActivity.java │ │ ├── SettingsFirewallService.java │ │ └── SettingsProviderHook.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── layout │ ├── app_item.xml │ ├── edit_dialog.xml │ ├── main.xml │ ├── setting_item.xml │ └── settings.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── values-night │ └── themes.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values │ ├── arrays.xml │ ├── colors.xml │ ├── strings.xml │ └── themes.xml │ └── xml │ ├── backup_rules.xml │ └── data_extraction_rules.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | .cxx 10 | local.properties 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SettingsFirewall 2 | An Xposed module that blocks shitty apps from accessing your system settings (for example, check if development settings is enabled on the device, or check if there are running accessibility services). 3 | 4 | Please note that only accesses to system settings (e.g. [Settings APIs](https://developer.android.com/reference/android/provider/Settings) or `/system/bin/settings get`) can be intercepted by this module. 5 | Accesses to system properties (e.g. `android.os.SystemProperties APIs`, `__system_property_get` or `getprop`) or other system APIs cannot be blocked. 6 | 7 | ### Usage 8 | Requirements: Android 4.3+ devices with a root Xposed framework installed. 9 | This module requires to hook system components, so rootless Xposed frameworks (e.g. LSPatch, VirtualXposed or TaiChi-Ying) cannot be supported. 10 | 11 | For LSPosed users, select only "System Framework" and reboot. 12 | 13 | For [Dreamland](https://github.com/canyie/Dreamland) users, select "Settings Provider" (`com.android.providers.settings`) and reboot. 14 | 15 | ### FAQ 16 | Q: Why is this module using the old Holo / Android Design? 17 | 18 | A: Thanks to all Material Design 3 (Material You) missionaries, 19 | I had to uninstall many apps and rollback my Android system to an old one 20 | to avoid being attacked by your new "amazing" design. Happy now? 21 | 22 | ### License 23 | This project is under the MIT license with the following additional restrictions: 24 | 25 | - You are **FORBIDDEN** to do anything that would make Android 4.3 users unable to use this app 26 | (e.g. changing `minSdkVersion` to anything higher than `18`) 27 | or use code from this project in a project that does not support Android 4.3. 28 | - You are **FORBIDDEN** to change the UI design style to Material Design 3 (Material You) 29 | or use code from this project in a project that uses Material Design 3 (Material You). 30 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | namespace 'top.canyie.settingsfirewall' 7 | compileSdk 34 8 | 9 | defaultConfig { 10 | applicationId "top.canyie.settingsfirewall" 11 | minSdk 18 12 | targetSdk 34 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | compileOptions { 24 | sourceCompatibility JavaVersion.VERSION_17 25 | targetCompatibility JavaVersion.VERSION_17 26 | } 27 | buildFeatures { 28 | aidl true 29 | buildConfig true 30 | } 31 | } 32 | 33 | dependencies { 34 | compileOnly 'de.robv.android.xposed:api:82' 35 | implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3' 36 | } 37 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/aidl/top/canyie/settingsfirewall/ISettingsFirewall.aidl: -------------------------------------------------------------------------------- 1 | // ISettingsFirewall.aidl 2 | package top.canyie.settingsfirewall; 3 | 4 | import top.canyie.settingsfirewall.Replacement; 5 | 6 | interface ISettingsFirewall { 7 | int[] getTargets() = 1; 8 | void setTarget(int uid, boolean enabled) = 2; 9 | Replacement[] getReplacements(int uid) = 3; 10 | void setReplacement(int uid, String setting, String value, int flags) = 4; 11 | void deleteReplacement(int uid, String setting) = 5; 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/aidl/top/canyie/settingsfirewall/Replacement.aidl: -------------------------------------------------------------------------------- 1 | // Replacement.aidl 2 | package top.canyie.settingsfirewall; 3 | 4 | parcelable Replacement; 5 | -------------------------------------------------------------------------------- /app/src/main/assets/xposed_init: -------------------------------------------------------------------------------- 1 | top.canyie.settingsfirewall.SettingsProviderHook 2 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/App.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import android.content.pm.PackageManager; 6 | import android.graphics.drawable.Drawable; 7 | import android.net.Uri; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.os.Process; 11 | import android.os.RemoteException; 12 | import android.provider.Settings; 13 | import android.text.TextUtils; 14 | import android.util.Log; 15 | 16 | import org.lsposed.hiddenapibypass.HiddenApiBypass; 17 | 18 | import java.lang.reflect.Field; 19 | import java.lang.reflect.Modifier; 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.Collections; 23 | import java.util.HashSet; 24 | import java.util.List; 25 | import java.util.Set; 26 | 27 | /** 28 | * @author canyie 29 | */ 30 | public class App extends Application { 31 | public static final int MY_UID = Process.myUid(); 32 | private static ISettingsFirewall service; 33 | 34 | @Override protected void attachBaseContext(Context base) { 35 | super.attachBaseContext(base); 36 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) 37 | HiddenApiBypass.addHiddenApiExemptions(""); 38 | } 39 | 40 | public static ISettingsFirewall getService(Context context) { 41 | if (service == null) { 42 | Uri uri = Uri.parse("content://" + Settings.AUTHORITY); 43 | try { 44 | Bundle result = context.getContentResolver().call(uri, SettingsProviderHook.METHOD, 45 | null, null); 46 | if (result != null) 47 | service = ISettingsFirewall.Stub.asInterface(result.getBinder(Settings.NameValueTable.VALUE)); 48 | } catch (Exception ignored) { 49 | } 50 | } 51 | return service; 52 | } 53 | 54 | public static List getSortedList(Context context, ISettingsFirewall service) { 55 | PackageManager pm = context.getPackageManager(); 56 | var apps = pm.getInstalledPackages(0); 57 | int[] enabledUids; 58 | try { 59 | enabledUids = service.getTargets(); 60 | } catch (RemoteException e) { 61 | throw new RuntimeException(e); 62 | } 63 | Arrays.sort(enabledUids); 64 | List list = new ArrayList<>(); 65 | Set addedSharedUserIds = new HashSet<>(); 66 | Drawable androidIcon = null; 67 | for (var app : apps) { 68 | var appInfo = app.applicationInfo; 69 | int uid = appInfo.uid; 70 | // Skip system core process and ourselves 71 | if (uid < Process.SHELL_UID) continue; 72 | if (uid == App.MY_UID) continue; 73 | AppInfo info; 74 | if (TextUtils.isEmpty(app.sharedUserId)) { 75 | info = new AppInfo(); 76 | info.name = appInfo.loadLabel(pm).toString(); 77 | info.icon = appInfo.loadIcon(pm); 78 | } else { 79 | var sharedUserId = app.sharedUserId; 80 | if (!addedSharedUserIds.add(sharedUserId)) continue; 81 | info = new AppInfo(); 82 | info.name = "[SharedUserID] " + sharedUserId; 83 | if (androidIcon == null) 84 | androidIcon = context.getResources().getDrawable(android.R.mipmap.sym_def_app_icon); 85 | info.icon = androidIcon; 86 | info.isSharedUid = true; 87 | } 88 | info.enabled = Arrays.binarySearch(enabledUids, info.uid = uid) >= 0; 89 | list.add(info); 90 | } 91 | Collections.sort(list, AppInfo.COMPARATOR); 92 | return list; 93 | } 94 | 95 | public static List getSettings(ISettingsFirewall service, int uid) { 96 | Replacement[] replacements; 97 | try { 98 | replacements = service.getReplacements(uid); 99 | } catch (RemoteException e) { 100 | // Should never happen: the remote service runs in Settings Provider which is in system_server 101 | throw new RuntimeException(e); 102 | } 103 | List out = new ArrayList<>(); 104 | addSettings(Settings.System.class, out, Replacement.FLAG_SYSTEM, replacements, 105 | "MOVED_TO_SECURE", "MOVED_TO_GLOBAL", "MOVED_TO_SECURE_THEN_GLOBAL"); 106 | addSettings(Settings.Secure.class, out, Replacement.FLAG_SECURE, replacements, 107 | "MOVED_TO_GLOBAL"); 108 | addSettings(Settings.Global.class, out, Replacement.FLAG_GLOBAL, replacements); 109 | Collections.sort(out, Replacement.COMPARATOR); 110 | return out; 111 | } 112 | private static void addSettings(Class cls, 113 | List out, int flag, Replacement[] replacements, 114 | String... ignore) { 115 | Set[] ignoreSets = new Set[ignore.length]; 116 | for (int i = 0;i < ignore.length;i++) { 117 | Set set; 118 | try { 119 | Field field = cls.getDeclaredField(ignore[i]); 120 | field.setAccessible(true); 121 | set = (Set) field.get(null); 122 | } catch (Exception e) { 123 | Log.w("SettingsFirewall", "Unable to access " + cls + "." + ignore[i], e); 124 | set = Collections.emptySet(); 125 | } 126 | ignoreSets[i] = set; 127 | } 128 | Field[] fields = cls.getDeclaredFields(); 129 | outer: for (Field field : fields) { 130 | int modifiers = field.getModifiers(); 131 | if (!(Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers))) continue; 132 | if (field.getType() != String.class) continue; 133 | field.setAccessible(true); 134 | String key; 135 | try { 136 | key = (String) field.get(null); 137 | } catch (Exception e) { 138 | // Should never happen 139 | throw new RuntimeException(e); 140 | } 141 | if (TextUtils.isEmpty(key)) continue; 142 | for (Set set : ignoreSets) 143 | if (set.contains(key)) 144 | continue outer; 145 | if (replacements != null) { 146 | for (Replacement existing : replacements) { 147 | if (key.equals(existing.key)) { 148 | out.add(existing); 149 | continue outer; 150 | } 151 | } 152 | } 153 | out.add(new Replacement(key, null, flag)); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/AppInfo.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import java.util.Comparator; 5 | 6 | /** 7 | * @author canyie 8 | */ 9 | public class AppInfo { 10 | public int uid; 11 | public String name; 12 | public Drawable icon; 13 | public boolean enabled; 14 | public boolean isSharedUid; 15 | 16 | public static final Comparator COMPARATOR = (a, b) -> { 17 | if (a.enabled != b.enabled) 18 | return a.enabled ? -1 : 1; 19 | if (a.isSharedUid != b.isSharedUid) 20 | return a.isSharedUid ? -1 : 1; 21 | return a.name.compareTo(b.name); 22 | }; 23 | 24 | @Override public boolean equals(Object obj) { 25 | return obj instanceof AppInfo that && uid == that.uid; 26 | } 27 | 28 | @Override public int hashCode() { 29 | return uid; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/AppListAdapter.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ArrayAdapter; 7 | import android.widget.CheckBox; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author canyie 15 | */ 16 | public class AppListAdapter extends ArrayAdapter { 17 | private final MainActivity activity; 18 | public AppListAdapter(MainActivity activity, List list) { 19 | super(activity, 0, list); 20 | this.activity = activity; 21 | } 22 | 23 | @Override public View getView(int position, View convertView, ViewGroup parent) { 24 | var appInfo = getItem(position); 25 | assert appInfo != null; 26 | ViewHolder viewHolder; 27 | if (convertView == null) { 28 | convertView = LayoutInflater.from(activity).inflate(R.layout.app_item, parent, false); 29 | viewHolder = new ViewHolder(); 30 | viewHolder.name = convertView.findViewById(R.id.app_name); 31 | viewHolder.icon = convertView.findViewById(R.id.app_icon); 32 | viewHolder.checkBox = convertView.findViewById(R.id.checkbox); 33 | convertView.setTag(viewHolder); 34 | } else { 35 | viewHolder = (ViewHolder) convertView.getTag(); 36 | } 37 | convertView.setOnClickListener(v -> activity.onItemClicked(appInfo)); 38 | viewHolder.name.setText(appInfo.name); 39 | viewHolder.icon.setImageDrawable(appInfo.icon); 40 | viewHolder.checkBox.setOnCheckedChangeListener(null); 41 | viewHolder.checkBox.setChecked(appInfo.enabled); 42 | viewHolder.checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> 43 | activity.onItemChecked(appInfo, isChecked)); 44 | return convertView; 45 | } 46 | 47 | private static class ViewHolder { 48 | TextView name; 49 | ImageView icon; 50 | CheckBox checkBox; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/MainActivity.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.os.RemoteException; 7 | import android.view.View; 8 | import android.widget.ListView; 9 | 10 | /** 11 | * @author canyie 12 | */ 13 | public class MainActivity extends Activity { 14 | private ISettingsFirewall service; 15 | 16 | @Override protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | try { 19 | setContentView(R.layout.main); 20 | findViewById(android.R.id.home).setVisibility(View.GONE); 21 | service = App.getService(this); 22 | findViewById(R.id.progress_bar).setVisibility(View.GONE); 23 | if (service == null) { 24 | findViewById(R.id.not_activated_msg).setVisibility(View.VISIBLE); 25 | return; 26 | } 27 | ListView listView = findViewById(R.id.list); 28 | AppListAdapter adapter = new AppListAdapter(this, App.getSortedList(this, service)); 29 | listView.setAdapter(adapter); 30 | listView.setVisibility(View.VISIBLE); 31 | } catch (Exception e) { 32 | throw new RuntimeException(e); 33 | } 34 | } 35 | 36 | public void onItemChecked(AppInfo appInfo, boolean checked) { 37 | try { 38 | appInfo.enabled = checked; 39 | service.setTarget(appInfo.uid, checked); 40 | } catch (RemoteException e) { 41 | throw new RuntimeException(e); 42 | } 43 | } 44 | 45 | public void onItemClicked(AppInfo appInfo) { 46 | startActivity(new Intent(this, SettingsEditActivity.class) 47 | .putExtra(SettingsEditActivity.KEY_UID, appInfo.uid) 48 | .putExtra(SettingsEditActivity.KEY_NAME, appInfo.name)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/Replacement.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import java.io.Serial; 7 | import java.io.Serializable; 8 | import java.util.Comparator; 9 | 10 | /** 11 | * @author canyie 12 | */ 13 | public final class Replacement implements Serializable, Parcelable { 14 | public static final int FLAG_SYSTEM = 1 << 0; 15 | public static final int FLAG_SECURE = 1 << 1; 16 | public static final int FLAG_GLOBAL = 1 << 2; 17 | @Serial private static final long serialVersionUID = 1145141919810L; 18 | public final String key; 19 | public String value; 20 | public int flags; 21 | 22 | public static final Creator CREATOR = new Creator<>() { 23 | @Override public Replacement createFromParcel(Parcel in) { 24 | return new Replacement(in.readString(), in.readString(), in.readInt()); 25 | } 26 | 27 | @Override public Replacement[] newArray(int size) { 28 | return new Replacement[size]; 29 | } 30 | }; 31 | 32 | public static final Comparator COMPARATOR = (a, b) -> { 33 | boolean aReplaced = a.value != null; 34 | boolean bReplaced = b.value != null; 35 | if (aReplaced != bReplaced) 36 | return aReplaced ? -1 : 1; 37 | return a.key.compareTo(b.key); 38 | }; 39 | 40 | public Replacement(String name, String value, int flags) { 41 | this.key = name; 42 | this.value = value; 43 | this.flags = flags; 44 | } 45 | 46 | @Override public int describeContents() { 47 | return 0; 48 | } 49 | 50 | @Override public void writeToParcel(Parcel dest, int flags) { 51 | dest.writeString(key); 52 | dest.writeString(value); 53 | dest.writeInt(this.flags); 54 | } 55 | 56 | @Override public boolean equals(Object obj) { 57 | return obj instanceof Replacement that && key.equals(that.key) && flags == that.flags; 58 | } 59 | 60 | @Override public int hashCode() { 61 | return key.hashCode() ^ flags; 62 | } 63 | 64 | @Override public String toString() { 65 | return "Replacement{key=" + key + ", value=" + value + ", flags=" + flags + "}"; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/SettingListAdapter.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.view.LayoutInflater; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.ArrayAdapter; 7 | import android.widget.TextView; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author canyie 13 | */ 14 | public class SettingListAdapter extends ArrayAdapter { 15 | private final SettingsEditActivity activity; 16 | public SettingListAdapter(SettingsEditActivity activity, List list) { 17 | super(activity, 0, list); 18 | this.activity = activity; 19 | } 20 | 21 | @Override public View getView(int position, View convertView, ViewGroup parent) { 22 | var replacement = getItem(position); 23 | assert replacement != null; 24 | ViewHolder viewHolder; 25 | if (convertView == null) { 26 | convertView = LayoutInflater.from(getContext()).inflate(R.layout.setting_item, parent, false); 27 | viewHolder = new ViewHolder(); 28 | viewHolder.key = convertView.findViewById(R.id.key); 29 | viewHolder.replacement = convertView.findViewById(R.id.replacement); 30 | convertView.setTag(viewHolder); 31 | } else { 32 | viewHolder = (ViewHolder) convertView.getTag(); 33 | } 34 | convertView.setOnClickListener(v -> activity.onItemClicked(replacement)); 35 | viewHolder.key.setText(replacement.key); 36 | if (replacement.value != null) { 37 | viewHolder.replacement.setVisibility(View.VISIBLE); 38 | viewHolder.replacement.setText(getContext().getString(R.string.replaced_with, replacement.value)); 39 | } else { 40 | viewHolder.replacement.setVisibility(View.GONE); 41 | } 42 | return convertView; 43 | } 44 | 45 | private static class ViewHolder { 46 | TextView key; 47 | TextView replacement; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/SettingsEditActivity.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.app.AlertDialog; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.os.RemoteException; 9 | import android.view.LayoutInflater; 10 | import android.widget.EditText; 11 | import android.widget.ListView; 12 | 13 | /** 14 | * @author canyie 15 | */ 16 | public class SettingsEditActivity extends Activity { 17 | public static final String KEY_NAME = "name"; 18 | public static final String KEY_UID = "uid"; 19 | private int uid; 20 | private ISettingsFirewall service; 21 | private SettingListAdapter adapter; 22 | private LayoutInflater layoutInflater; 23 | 24 | @Override protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | Intent intent = getIntent(); 27 | if (intent == null || (uid = intent.getIntExtra(KEY_UID, -1)) == -1) { 28 | finish(); 29 | return; 30 | } 31 | setContentView(R.layout.settings); 32 | layoutInflater = LayoutInflater.from(this); 33 | var actionBar = getActionBar(); 34 | actionBar.setDisplayShowHomeEnabled(false); 35 | actionBar.setTitle(R.string.edit_settings_replacement); 36 | actionBar.setSubtitle(intent.getStringExtra(KEY_NAME)); 37 | service = App.getService(this); 38 | adapter = new SettingListAdapter(this, App.getSettings(service, uid)); 39 | ListView listView = findViewById(R.id.list); 40 | listView.setAdapter(adapter); 41 | } 42 | 43 | public void onItemClicked(Replacement replacement) { 44 | var layout = layoutInflater.inflate(R.layout.edit_dialog, null); 45 | @SuppressLint({"MissingInflatedId", "LocalSuppress"}) 46 | EditText editText = layout.findViewById(R.id.edit); 47 | if (replacement.value != null) 48 | editText.setText(replacement.value); 49 | new AlertDialog.Builder(this) 50 | .setTitle(getString(R.string.editing, replacement.key)) 51 | .setView(layout) 52 | .setNegativeButton(android.R.string.cancel, null) 53 | .setPositiveButton(R.string.save, (dialog, which) -> { 54 | replacement.value = editText.getText().toString(); 55 | try { 56 | service.setReplacement(uid, replacement.key, replacement.value, replacement.flags); 57 | } catch (RemoteException e) { 58 | throw new RuntimeException(e); 59 | } 60 | adapter.notifyDataSetChanged(); 61 | }) 62 | .setNeutralButton(R.string.delete, (dialog, which) -> { 63 | replacement.value = null; 64 | try { 65 | service.deleteReplacement(uid, replacement.key); 66 | } catch (RemoteException e) { 67 | throw new RuntimeException(e); 68 | } 69 | adapter.notifyDataSetChanged(); 70 | }) 71 | .setCancelable(false) 72 | .show(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/SettingsFirewallService.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.os.Build; 6 | import android.os.Bundle; 7 | import android.provider.Settings; 8 | import android.util.SparseArray; 9 | 10 | import java.io.File; 11 | import java.io.FileInputStream; 12 | import java.io.FileOutputStream; 13 | import java.io.IOException; 14 | import java.io.ObjectInputStream; 15 | import java.io.ObjectOutputStream; 16 | import java.util.ArrayList; 17 | import java.util.HashSet; 18 | import java.util.List; 19 | import java.util.Set; 20 | import java.util.concurrent.locks.Lock; 21 | import java.util.concurrent.locks.ReentrantReadWriteLock; 22 | 23 | import de.robv.android.xposed.XposedBridge; 24 | 25 | /** 26 | * @author canyie 27 | */ 28 | public class SettingsFirewallService extends ISettingsFirewall.Stub { 29 | public static final SettingsFirewallService INSTANCE = new SettingsFirewallService(); 30 | public static final Bundle BUNDLE; 31 | 32 | private static final String KEY_TARGET_UIDS = "firewall_targets"; 33 | private static final String FILENAME = "settings-firewall"; 34 | private static File policyDir; 35 | private static SharedPreferences sharedPreferences; 36 | private static final Set targetUids = new HashSet<>(); 37 | private static final SparseArray> policyCache = new SparseArray<>(); 38 | private static final Lock readLock, writeLock; 39 | 40 | static { 41 | BUNDLE = new Bundle(1); 42 | BUNDLE.putBinder(Settings.NameValueTable.VALUE, INSTANCE.asBinder()); 43 | var lock = new ReentrantReadWriteLock(); 44 | readLock = lock.readLock(); 45 | writeLock = lock.writeLock(); 46 | } 47 | 48 | private SettingsFirewallService() { 49 | } 50 | 51 | public static void init(Context context) { 52 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) 53 | context = context.createDeviceProtectedStorageContext(); 54 | sharedPreferences = context.getSharedPreferences(FILENAME, Context.MODE_PRIVATE); 55 | var uidSet = sharedPreferences.getStringSet(KEY_TARGET_UIDS, null); 56 | if (uidSet != null) { 57 | for (String uid : uidSet) { 58 | try { 59 | targetUids.add(Integer.valueOf(uid)); 60 | } catch (NumberFormatException e) { 61 | XposedBridge.log("[SettingsFirewall] Found invalid target uid " + uid); 62 | } 63 | } 64 | } 65 | policyDir = context.getDir(FILENAME, Context.MODE_PRIVATE); 66 | XposedBridge.log("[SettingsFirewall] Load uid policy from " + policyDir); 67 | File[] files = policyDir.listFiles(); 68 | if (files == null) return; 69 | for (File file : files) { 70 | int uid; 71 | String filename = file.getName(); 72 | try { 73 | uid = Integer.parseInt(filename); 74 | } catch (NumberFormatException e) { 75 | XposedBridge.log("[SettingsFirewall] Found invalid file " + file); 76 | continue; 77 | } 78 | try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(file))) { 79 | Object o = in.readObject(); 80 | policyCache.put(uid, (List) o); 81 | } catch (Exception e) { 82 | // Maybe we should delete the file, since the file may be corrupted? 83 | XposedBridge.log("[SettingsFirewall] Error reading " + file); 84 | XposedBridge.log(e); 85 | } 86 | } 87 | } 88 | 89 | public static String getReplacement(int uid, String setting, int flag) { 90 | readLock.lock(); 91 | try { 92 | if (!targetUids.contains(uid)) return null; 93 | List replacements = policyCache.get(uid); 94 | if (replacements != null) 95 | for (Replacement replacement : replacements) 96 | if (setting.equals(replacement.key) && (replacement.flags & flag) != 0) 97 | return replacement.value; 98 | } finally { 99 | readLock.unlock(); 100 | } 101 | return null; 102 | } 103 | 104 | @Override public int[] getTargets() { 105 | readLock.lock(); 106 | try { 107 | int N = targetUids.size(); 108 | int[] a = new int[N]; 109 | int i = 0; 110 | for (Integer uid : targetUids) 111 | a[i++] = uid; 112 | return a; 113 | } finally { 114 | readLock.unlock(); 115 | } 116 | } 117 | 118 | @Override public void setTarget(int uid, boolean enabled) { 119 | writeLock.lock(); 120 | try { 121 | if (enabled) 122 | targetUids.add(uid); 123 | else 124 | targetUids.remove(uid); 125 | saveTargets(targetUids); 126 | } finally { 127 | writeLock.unlock(); 128 | } 129 | } 130 | 131 | @Override public Replacement[] getReplacements(int uid) { 132 | readLock.lock(); 133 | try { 134 | var list = policyCache.get(uid); 135 | // Deep-copy the list before releasing the lock, to avoid the content being changed 136 | // in a time window between returning(unlocking) and writing its content into parcel 137 | return list != null ? list.toArray(new Replacement[list.size()]) : null; 138 | } finally { 139 | readLock.unlock(); 140 | } 141 | } 142 | 143 | @Override public void setReplacement(int uid, String setting, String value, int flags) { 144 | writeLock.lock(); 145 | try { 146 | List replacements = policyCache.get(uid); 147 | if (replacements == null) { 148 | policyCache.put(uid, replacements = new ArrayList<>()); 149 | } 150 | boolean add = true; 151 | for (Replacement replacement : replacements) { 152 | if (setting.equals(replacement.key)) { 153 | replacement.value = value; 154 | replacement.flags = flags; 155 | add = false; 156 | } 157 | } 158 | if (add) 159 | replacements.add(new Replacement(setting, value, flags)); 160 | saveUidRules(uid, replacements); 161 | } finally { 162 | writeLock.unlock(); 163 | } 164 | } 165 | 166 | @Override public void deleteReplacement(int uid, String setting) { 167 | writeLock.lock(); 168 | try { 169 | List replacements = policyCache.get(uid); 170 | if (replacements == null) return; 171 | for (var iterator = replacements.iterator();iterator.hasNext();) { 172 | var replacement = iterator.next(); 173 | if (setting.equals(replacement.key)) { 174 | iterator.remove(); 175 | } 176 | } 177 | saveUidRules(uid, replacements); 178 | } finally { 179 | writeLock.unlock(); 180 | } 181 | } 182 | 183 | private static void saveTargets(Set uids) { 184 | Set stringSet = new HashSet<>(uids.size(), 2); 185 | for (Integer uid : uids) { 186 | stringSet.add(uid.toString()); 187 | } 188 | sharedPreferences.edit().putStringSet(KEY_TARGET_UIDS, stringSet).commit(); 189 | } 190 | 191 | private static void saveUidRules(int uid, List replacements) { 192 | try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File( 193 | policyDir, Integer.toString(uid))))) { 194 | out.writeObject(replacements); 195 | } catch (IOException e) { 196 | XposedBridge.log("[SettingsFirewall] Error saving rules of uid " + uid); 197 | XposedBridge.log(e); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /app/src/main/java/top/canyie/settingsfirewall/SettingsProviderHook.java: -------------------------------------------------------------------------------- 1 | package top.canyie.settingsfirewall; 2 | 3 | import android.content.ContentProvider; 4 | import android.content.Context; 5 | import android.os.Binder; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.provider.Settings; 9 | 10 | import de.robv.android.xposed.IXposedHookLoadPackage; 11 | import de.robv.android.xposed.XC_MethodHook; 12 | import de.robv.android.xposed.XposedHelpers; 13 | import de.robv.android.xposed.callbacks.XC_LoadPackage; 14 | 15 | /** 16 | * @author canyie 17 | */ 18 | public class SettingsProviderHook extends XC_MethodHook implements IXposedHookLoadPackage { 19 | public static final String METHOD = "GET_SettingsFirewall"; 20 | private volatile Context context; 21 | 22 | @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { 23 | if (!"com.android.providers.settings".equals(lpparam.packageName)) return; 24 | XposedHelpers.findAndHookMethod( 25 | "com.android.providers.settings.SettingsProvider", 26 | lpparam.classLoader, 27 | "call", 28 | String.class, 29 | String.class, 30 | Bundle.class, 31 | this 32 | ); 33 | } 34 | 35 | @Override protected void beforeHookedMethod(MethodHookParam param) { 36 | ContentProvider contentProvider = (ContentProvider) param.thisObject; 37 | String method = (String) param.args[0]; 38 | String name = (String) param.args[1]; 39 | // Bundle args = (Bundle) param.args[2]; 40 | if (context == null) { 41 | synchronized (this) { 42 | if (context == null) { 43 | context = contentProvider.getContext(); 44 | SettingsFirewallService.init(context); 45 | } 46 | } 47 | } 48 | int callingUid = Binder.getCallingUid(); 49 | int flag; 50 | switch (method) { 51 | case METHOD: { 52 | // Verify the calling package is actually our module. If not, don't send anything. 53 | // We catch all possible exception to make sure unauthorized apps can't fool us. 54 | // For example, if the caller tries to lie us that it is another package, 55 | // getCallingPackage will throw an exception but we avoid delivering the exception 56 | // back to the caller because it is a side channel and can be detected 57 | try { 58 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT 59 | && !BuildConfig.APPLICATION_ID.equals(contentProvider.getCallingPackage())) 60 | return; 61 | param.setResult(SettingsFirewallService.BUNDLE); 62 | } catch (Exception ignored) { 63 | } 64 | return; 65 | } 66 | case "GET_global": 67 | flag = Replacement.FLAG_GLOBAL; 68 | break; 69 | case "GET_secure": 70 | flag = Replacement.FLAG_SECURE; 71 | break; 72 | case "GET_system": 73 | flag = Replacement.FLAG_SYSTEM; 74 | break; 75 | default: 76 | return; 77 | } 78 | 79 | String replacement = SettingsFirewallService.getReplacement(callingUid, name, flag); 80 | if (replacement != null) { 81 | Bundle result = new Bundle(1); 82 | result.putString(Settings.NameValueTable.VALUE, replacement); 83 | param.setResult(result); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/app_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 14 | 22 | 23 | 30 | 36 | -------------------------------------------------------------------------------- /app/src/main/res/layout/edit_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 11 | 17 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/layout/setting_item.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 18 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/values-zh-rCN/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 设置防睛眼 4 | 模块未激活。\n请在 Xposed 中激活本模块。\n作用域:\nLSPosed 用户:选择“系统框架”;\n梦境框架用户:仅选择 “设置存储”。 5 | 编辑设置项覆盖 6 | 已替换为 %s 7 | 正在编辑:%s 8 | 保存 9 | 删除 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | android 5 | com.android.providers.settings 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SettingsFirewall 3 | Module not activated.\n Please enable this module in Xposed. \n Scope:\n For LSPosed users, select \"System Framework\"\n For Dreamland users, select only \"Settings Provider\". 4 | Edit settings replacement 5 | Replaced with %s 6 | Editing: %s 7 | Save 8 | Delete 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | 20 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '8.2.0' apply false 4 | id 'com.android.library' version '8.2.0' apply false 5 | } 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # Enables namespacing of each library's R class so that its R class includes only the 15 | # resources declared in the library itself and none from the library's dependencies, 16 | # thereby reducing the size of the R class for that library 17 | android.nonTransitiveRClass=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/canyie/SettingsFirewall/df1135e80d1b89e1867d8625028542c8646ebb59/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat May 20 10:42:56 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { url 'https://api.xposed.info' } 14 | } 15 | } 16 | rootProject.name = "SettingsFirewall" 17 | include ':app' 18 | --------------------------------------------------------------------------------