├── .gitignore
├── .gitmodules
├── .travis.yml
├── LICENSE
├── README.md
├── app
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── org
│ │ └── ethack
│ │ └── orwall
│ │ └── ApplicationTest.java
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── org
│ │ ├── ethack
│ │ └── orwall
│ │ │ ├── BackgroundProcess.java
│ │ │ ├── BootBroadcast.java
│ │ │ ├── NetworkReceiver.java
│ │ │ ├── PreferencesActivity.java
│ │ │ ├── TabbedMain.java
│ │ │ ├── UninstallBroadcast.java
│ │ │ ├── WizardActivity.java
│ │ │ ├── adapter
│ │ │ ├── AppListAdapter.java
│ │ │ └── TabsPagerAdapter.java
│ │ │ ├── database
│ │ │ └── natDBHelper.java
│ │ │ ├── fragments
│ │ │ ├── AppFragment.java
│ │ │ ├── HomeFragment.java
│ │ │ └── WizardFragment.java
│ │ │ └── lib
│ │ │ ├── AppPreferenceList.java
│ │ │ ├── AppRule.java
│ │ │ ├── AppRuleComparator.java
│ │ │ ├── CheckSum.java
│ │ │ ├── Constants.java
│ │ │ ├── InstallScripts.java
│ │ │ ├── Iptables.java
│ │ │ ├── NatRules.java
│ │ │ ├── NetworkHelper.java
│ │ │ ├── PackageComparator.java
│ │ │ ├── PackageInfoData.java
│ │ │ ├── Preferences.java
│ │ │ └── Util.java
│ │ └── sufficientlysecure
│ │ └── rootcommands
│ │ ├── Mount.java
│ │ ├── Remounter.java
│ │ ├── RootCommands.java
│ │ ├── Shell.java
│ │ ├── SystemCommands.java
│ │ ├── Toolbox.java
│ │ ├── command
│ │ ├── Command.java
│ │ ├── ExecutableCommand.java
│ │ ├── SimpleCommand.java
│ │ └── SimpleExecutableCommand.java
│ │ └── util
│ │ ├── BrokenBusyboxException.java
│ │ ├── Log.java
│ │ ├── RootAccessDeniedException.java
│ │ ├── UnsupportedArchitectureException.java
│ │ └── Utils.java
│ └── res
│ ├── create_ico.sh
│ ├── drawable-hdpi
│ ├── android_unknown_app.png
│ ├── ic_action_settings.png
│ └── v2.png
│ ├── drawable-mdpi
│ ├── android_unknown_app.png
│ ├── ic_action_settings.png
│ └── v2.png
│ ├── drawable-xhdpi
│ ├── android_unknown_app.png
│ ├── ic_action_settings.png
│ └── v2.png
│ ├── drawable-xxhdpi
│ ├── android_unknown_app.png
│ ├── ic_action_settings.png
│ └── v2.png
│ ├── drawable-xxxhdpi
│ └── android_unknown_app.png
│ ├── layout
│ ├── about.xml
│ ├── activity_tabbed_main.xml
│ ├── activity_wizard.xml
│ ├── advanced_connection.xml
│ ├── app_row.xml
│ ├── fragment_tabbed_apps.xml
│ ├── fragment_tabbed_home.xml
│ └── fragment_wizard.xml
│ ├── raw
│ ├── activate_portal.sh
│ ├── deactivate_portal.sh
│ └── userinit.sh
│ ├── values-de
│ └── strings.xml
│ ├── values-es
│ └── strings.xml
│ ├── values-fr
│ └── strings.xml
│ ├── values-it
│ └── strings.xml
│ ├── values
│ ├── integers.xml
│ ├── no_translation.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ ├── fragment_apps_prefs.xml
│ ├── fragment_network_prefs.xml
│ ├── fragment_proxy_ports.xml
│ ├── network_preference.xml
│ ├── other_preferences.xml
│ └── preferences_header.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── orWall.iml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea
4 | .DS_Store
5 | /build
6 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/.gitmodules
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | jdk:
3 | - oraclejdk8
4 | android:
5 | components:
6 | - tools
7 | - platform-tools
8 | - build-tools-24.0.1
9 | - android-24
10 |
11 | script:
12 | - ./gradlew build
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.org/EthACKdotOrg/orWall)
2 |
3 | # orWall
4 |
5 | Because real life is a Dystopia
6 |
7 |
8 | ## Website
9 | https://orwall.org/
10 |
11 | ## What's this?
12 | orWall will force selected applications through Orbot while preventing unchecked applications to have network access.
13 | In order to do so, it will call the iptables binary. This binary, present on your Android device, requires superuser access (aka root). It's the application that manages the firewall on Linux and, by extension, on Android.
14 |
15 | In short, orWall will add special iptables rules in order to redirect traffic for applications through Tor; it will also add required rules in order to block traffic for other apps.
16 | The redirection is based on the application user id. Each android application runs as a dedicated user, and iptables has support for traffic filtering based on the process owner, meaning it's really easy and pretty safe to do this kind of thing on an Android device.
17 |
18 | The application works in two stages: first, an init-script will block all incoming and outgoing traffic. This should prevent leaks, knowing Android sends stuff before you can even access the device.
19 | Second stage comes once the device is fully booted: orWall itself takes the lead on the firewall, and add required rules in order to allow Orbot traffic, and redirect selected application to Orbot TransPort.
20 |
21 | ## Where can we find the APK?
22 | orWall is published on [f-droid](https://f-droid.org/repository/browse/?fdid=org.ethack.orwall), and we provide a GPG signed APK in the release tab.
23 |
24 | Beware, we found out people are messing around and push the APK on Google Play, as a paid app, without mentioning sources nor author. That's not the official one. We don't know if the app code is the same we provide.
25 |
26 | If you find such an app, please contact us.
27 |
28 | ### Coming soon
29 | - Support for other Onion Router applications (i2p)
30 | - Support for application dedicated stream (for Orbot)
31 |
32 | ### External libraries
33 | - [super-command](https://github.com/dschuermann/superuser-commands) (Apache2) for root accesses
34 |
35 | ### Support us
36 | - Bitcoin: 1Kriu9owRhEsFkj8Lc6Wr5xTv8YTNphhXn
37 | - Litecoin: LXjW5tKRHbrbxwTZmitj4JeBqcm4xpqvJ2
38 |
39 | ### Follow us on Twitter
40 | https://twitter.com/orWallApp
41 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | generateDebugSources
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion '24.0.1'
6 |
7 | defaultConfig {
8 | applicationId "org.ethack.orwall"
9 | minSdkVersion 16
10 | targetSdkVersion 24
11 | versionCode 40
12 | versionName "1.2.0"
13 | }
14 | buildTypes {
15 | release {
16 | minifyEnabled false
17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
18 | }
19 | }
20 | lintOptions {
21 | abortOnError false
22 | }
23 | }
24 |
25 | dependencies {
26 | compile fileTree(dir: 'libs', include: ['*.jar'])
27 | testCompile 'junit:junit:4.12'
28 | compile 'com.android.support:appcompat-v7:24.0.0'
29 |
30 | // You must install or update the Support Repository through the SDK manager to use this dependency.
31 | //compile 'com.android.support:support-v4:19.+'
32 | }
33 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /home/cedric/android-studio/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/ethack/orwall/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
15 |
16 |
17 |
18 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
58 |
59 |
62 |
63 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/BackgroundProcess.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import android.app.IntentService;
4 | import android.content.Intent;
5 |
6 | import org.ethack.orwall.lib.Constants;
7 | import org.ethack.orwall.lib.Iptables;
8 | import org.ethack.orwall.lib.Util;
9 | import org.sufficientlysecure.rootcommands.util.Log;
10 |
11 | /**
12 | * Allows to run background commands in order to avoid any blocking stuff in main thread.
13 | */
14 | public class BackgroundProcess extends IntentService {
15 |
16 | private Iptables iptables;
17 |
18 | public BackgroundProcess() {
19 | super("BackgroundProcess");
20 | }
21 |
22 | @Override
23 | protected void onHandleIntent(Intent workIntent) {
24 | this.iptables = new Iptables(this);
25 |
26 | String action = workIntent.getStringExtra(Constants.ACTION);
27 |
28 | if (action != null) {
29 | if (action.equals(Constants.ACTION_PORTAL)) {
30 | boolean activate = workIntent.getBooleanExtra(Constants.PARAM_ACTIVATE, false);
31 | managePortal(activate);
32 |
33 | } else if (action.equals(Constants.ACTION_ADD_RULE)) {
34 | long appUID = workIntent.getLongExtra(Constants.PARAM_APPUID, 0);
35 | String appName = workIntent.getStringExtra(Constants.PARAM_APPNAME);
36 | String onionType = workIntent.getStringExtra(Constants.PARAM_ONIONTYPE);
37 | Boolean localHost = workIntent.getBooleanExtra(Constants.PARAM_LOCALHOST, false);
38 | Boolean localNetwork = workIntent.getBooleanExtra(Constants.PARAM_LOCALNETWORK, false);
39 | addRule(appUID, appName, onionType, localHost, localNetwork);
40 |
41 | } else if (action.equals(Constants.ACTION_RM_RULE)) {
42 | long appUID = workIntent.getLongExtra(Constants.PARAM_APPUID, 0);
43 | String appName = workIntent.getStringExtra(Constants.PARAM_APPNAME);
44 | String onionType = workIntent.getStringExtra(Constants.PARAM_ONIONTYPE);
45 | Boolean localHost = workIntent.getBooleanExtra(Constants.PARAM_LOCALHOST, false);
46 | Boolean localNetwork = workIntent.getBooleanExtra(Constants.PARAM_LOCALNETWORK, false);
47 | rmRule(appUID, appName, onionType, localHost, localNetwork);
48 |
49 | } else if (action.equals(Constants.ACTION_DISABLE_ORWALL)) {
50 | iptables.deactivate();
51 | iptables.deactivateV6();
52 |
53 | } else if (action.equals(Constants.ACTION_ENABLE_ORWALL)) {
54 | iptables.boot();
55 | } else {
56 | Log.e("BackgroundProcess", "Just got an unknown action!");
57 | }
58 | } else {
59 | Log.e("BackgroundProcess", "Just got an undefined action!");
60 | }
61 | }
62 |
63 | private void managePortal(boolean activate) {
64 | Util.enableCaptiveDetection(activate, this);
65 | }
66 |
67 | private void addRule(Long appUID, String appName, String onionType, Boolean localHost, Boolean localNetwork) {
68 |
69 | if (onionType.equals(Constants.DB_ONION_TYPE_TOR)) {
70 | iptables.natApp(this, appUID, 'A', appName);
71 | } else
72 | if (onionType.equals(Constants.DB_ONION_TYPE_BYPASS)) {
73 | iptables.bypass(appUID, appName, true);
74 | }
75 |
76 | if (localHost) {
77 | iptables.localHost(appUID, appName, true);
78 | }
79 |
80 | if (localNetwork) {
81 | iptables.localNetwork(appUID, appName, true);
82 | }
83 | }
84 |
85 | private void rmRule(Long appUID, String appName, String onionType, Boolean localHost, Boolean localNetwork) {
86 | if (onionType.equals(Constants.DB_ONION_TYPE_TOR)) {
87 | iptables.natApp(this, appUID, 'D', appName);
88 | } else
89 | if (onionType.equals(Constants.DB_ONION_TYPE_BYPASS)) {
90 | iptables.bypass(appUID, appName, false);
91 | }
92 |
93 | if (localHost) {
94 | iptables.localHost(appUID, appName, false);
95 | }
96 |
97 | if (localNetwork) {
98 | iptables.localNetwork(appUID, appName, false);
99 | }
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/BootBroadcast.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import org.ethack.orwall.lib.Iptables;
8 | import org.ethack.orwall.lib.Preferences;
9 |
10 | /**
11 | * Do think at startup.
12 | */
13 | public class BootBroadcast extends BroadcastReceiver {
14 |
15 | public BootBroadcast() {
16 | }
17 |
18 | @Override
19 | public void onReceive(final Context context, final Intent intent) {
20 | Iptables iptables = new Iptables(context);
21 |
22 | // Enforce init-script if sharedpreference says it
23 | // We want to do it the earlier.
24 | // Also, we want to get a fresh status regarding the init-script support: this can be
25 | // a reboot after a ROM upgrade or change.
26 | boolean enforceInit = Preferences.isEnforceInitScript(context);
27 | if (Iptables.initSupported() && enforceInit) {
28 | Iptables.installInitScript(context);
29 | }
30 | // Apply boot-up rules in order to enable traffic for orbot and other things.
31 |
32 | if (Preferences.isOrwallEnabled(context)){
33 | iptables.boot();
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/NetworkReceiver.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Set;
5 | import java.util.HashSet;
6 |
7 | import android.content.BroadcastReceiver;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.util.Log;
11 |
12 | import org.ethack.orwall.lib.Iptables;
13 | import org.ethack.orwall.lib.NetworkHelper;
14 | import org.ethack.orwall.lib.Preferences;
15 |
16 | public class NetworkReceiver extends BroadcastReceiver {
17 | private static String TAG = "NetworkReceiver";
18 |
19 | public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
20 | public static final String EXTRA_ACTIVE_TETHER = "activeArray";
21 |
22 | public NetworkReceiver() {
23 | }
24 |
25 | @Override
26 | public void onReceive(Context context, Intent intent) {
27 | if (!Preferences.isOrwallEnabled(context)){
28 | return;
29 | }
30 |
31 | String action = intent.getAction();
32 |
33 | Log.d(TAG, "Got a Network Change event: " + action);
34 |
35 | Iptables iptables = new Iptables(context);
36 |
37 | if (action.equals(ACTION_TETHER_STATE_CHANGED)){
38 | // try the faster way
39 | Set set = new HashSet<>(0);
40 | ArrayList active = intent.getStringArrayListExtra(EXTRA_ACTIVE_TETHER);
41 | if (active != null){
42 | for(String intf: active) set.add(intf);
43 | } else {
44 | // hum, try the old fashioned way
45 | NetworkHelper.getTetheredInterfaces(context, set);
46 | }
47 |
48 | Set oldIntfs = Preferences.getTetherInterfaces(context);
49 |
50 | if (!set.equals(oldIntfs))
51 | iptables.tetherUpdate(context, oldIntfs, set);
52 | }
53 | else
54 | if (action.equals("android.net.wifi.WIFI_STATE_CHANGED") || action.equals("android.net.conn.CONNECTIVITY_CHANGE")) {
55 | Log.d(TAG, "Will do some LAN stuff");
56 |
57 | iptables.LANPolicy();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/PreferencesActivity.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.os.Bundle;
7 | import android.preference.PreferenceActivity;
8 | import android.preference.PreferenceFragment;
9 | import android.preference.PreferenceManager;
10 |
11 | import org.ethack.orwall.lib.Constants;
12 | import org.ethack.orwall.lib.Iptables;
13 | import org.ethack.orwall.lib.Preferences;
14 |
15 | import java.util.Arrays;
16 | import java.util.List;
17 |
18 |
19 | public class PreferencesActivity extends PreferenceActivity {
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | }
24 |
25 | @Override
26 | public void onBuildHeaders(List target) {
27 | loadHeadersFromResource(R.xml.preferences_header, target);
28 | }
29 |
30 | @Override
31 | protected boolean isValidFragment(String fragmentName) {
32 | String prepend = "org.ethack.orwall.PreferencesActivity$";
33 | String[] fragments = {
34 | prepend + "SpecialApps",
35 | prepend + "NetworkPrefs",
36 | prepend + "ProxyPorts",
37 | };
38 |
39 | return Arrays.asList(fragments).contains(fragmentName);
40 | }
41 |
42 | @Override
43 | public void onDestroy() {
44 | super.onDestroy();
45 | }
46 |
47 | public static class SpecialApps extends PreferenceFragment {
48 | private SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
49 | @Override
50 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
51 |
52 | }
53 | };
54 |
55 | @Override
56 | public void onCreate(Bundle savedInstanceState) {
57 | super.onCreate(savedInstanceState);
58 | addPreferencesFromResource(R.xml.fragment_apps_prefs);
59 | }
60 |
61 | @Override
62 | public void onResume() {
63 | super.onResume();
64 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
65 | }
66 |
67 | @Override
68 | public void onPause() {
69 | super.onPause();
70 | getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener);
71 | }
72 | }
73 |
74 | public static class NetworkPrefs extends PreferenceFragment {
75 | private SharedPreferences.OnSharedPreferenceChangeListener listener = new SharedPreferences.OnSharedPreferenceChangeListener() {
76 | @Override
77 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
78 |
79 | if (!sharedPreferences.getBoolean(Preferences.PREF_KEY_ORWALL_ENABLED, true)) return;
80 |
81 | Iptables iptables = new Iptables(getActivity());
82 |
83 | switch (s) {
84 | case Preferences.PREF_KEY_ADB_ENABLED:
85 | iptables.enableADB(sharedPreferences.getBoolean(s, false));
86 | break;
87 | case Preferences.PREF_KEY_SSH_ENABLED:
88 | iptables.enableSSH(sharedPreferences.getBoolean(s, false));
89 | break;
90 | case "enable_captive_portal":
91 | Context context = getActivity();
92 | Intent bgpProcess = new Intent(context, BackgroundProcess.class);
93 | bgpProcess.putExtra(Constants.PARAM_ACTIVATE, sharedPreferences.getBoolean(s, false));
94 | bgpProcess.putExtra(Constants.ACTION, Constants.ACTION_PORTAL);
95 | context.startService(bgpProcess);
96 | break;
97 | }
98 | }
99 | };
100 |
101 | @Override
102 | public void onCreate(Bundle savedInstanceState) {
103 | super.onCreate(savedInstanceState);
104 | PreferenceManager.setDefaultValues(getActivity(), R.xml.other_preferences, true);
105 | addPreferencesFromResource(R.xml.fragment_network_prefs);
106 | }
107 |
108 | @Override
109 | public void onResume() {
110 | super.onResume();
111 | getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(listener);
112 | }
113 |
114 | @Override
115 | public void onPause() {
116 | super.onPause();
117 | getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(listener);
118 | }
119 | }
120 |
121 | public static class ProxyPorts extends PreferenceFragment {
122 | @Override
123 | public void onCreate(Bundle savedInstanceState) {
124 | super.onCreate(savedInstanceState);
125 | addPreferencesFromResource(R.xml.fragment_proxy_ports);
126 | }
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/TabbedMain.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import android.app.ActionBar;
4 | import android.app.ActionBar.Tab;
5 | import android.app.FragmentTransaction;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.support.v4.app.FragmentActivity;
9 | import android.support.v4.view.ViewPager;
10 | import android.view.Menu;
11 | import android.view.MenuItem;
12 |
13 | import org.ethack.orwall.adapter.TabsPagerAdapter;
14 | import org.ethack.orwall.lib.Iptables;
15 | import org.ethack.orwall.lib.NatRules;
16 | import org.ethack.orwall.lib.Preferences;
17 | import org.sufficientlysecure.rootcommands.util.Log;
18 |
19 | import java.util.Set;
20 |
21 | /**
22 | * New main layout: using a tabbed layout allows to get a cleaner view
23 | * and a more friendly experience for end-users.
24 | */
25 | public class TabbedMain extends FragmentActivity implements ActionBar.TabListener {
26 |
27 | // Debug tag
28 | private String TAG = "TabbedMain";
29 |
30 | // Private variables we may need across multiple methods
31 | private ViewPager viewPager;
32 | private TabsPagerAdapter mAdapter;
33 | private ActionBar actionBar;
34 | // TODO: use R content for tab names if needed.
35 | private String[] tabs = {"Home", "Apps"/*, "Logs"*/};
36 |
37 | @Override
38 | public void onTabReselected(Tab tab, FragmentTransaction ft) {
39 | }
40 |
41 | @Override
42 | public void onTabSelected(Tab tab, FragmentTransaction ft) {
43 | // on tab selected
44 | // show respected fragment view
45 | Log.d(TAG, String.valueOf(tab.getPosition()));
46 | viewPager.setCurrentItem(tab.getPosition());
47 | }
48 |
49 | @Override
50 | public void onTabUnselected(Tab tab, FragmentTransaction ft) {
51 | }
52 |
53 | @Override
54 | protected void onCreate(Bundle savedInstanceState) {
55 | super.onCreate(savedInstanceState);
56 | setContentView(R.layout.activity_tabbed_main);
57 |
58 | // Import old settings to SQLite, and remove them from SharedPreferences
59 | NatRules natRules = new NatRules(this);
60 | Set oldRules = getSharedPreferences(Preferences.PREFERENCES, MODE_PRIVATE).getStringSet("nat_rules", null);
61 | if (natRules.getRuleCount() == 0 && oldRules != null) {
62 | natRules.importFromSharedPrefs(oldRules);
63 | getSharedPreferences(Preferences.PREFERENCES, MODE_PRIVATE).edit().remove("nat_rules").apply();
64 | }
65 |
66 | // Is it the first application run?
67 | if (Preferences.isFirstRun(this)) {
68 | // Initialize orWall iptables rules - #72 should be better after that
69 | Iptables iptables = new Iptables(this);
70 | iptables.boot();
71 | // Start Wizard
72 | Intent wizard = new Intent(this, WizardActivity.class);
73 | startActivity(wizard);
74 | }
75 |
76 | viewPager = (ViewPager) findViewById(R.id.pager);
77 | actionBar = getActionBar();
78 | mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
79 |
80 | viewPager.setAdapter(mAdapter);
81 | actionBar.setHomeButtonEnabled(false);
82 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
83 |
84 | // create the tab header
85 | for (String tab : tabs) {
86 | actionBar.addTab(actionBar.newTab().setText(tab).setTabListener(this));
87 | }
88 |
89 | // changer in order to take care of tab switching
90 | viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
91 | @Override
92 | public void onPageSelected(int position) {
93 | actionBar.setSelectedNavigationItem(position);
94 | }
95 |
96 | @Override
97 | public void onPageScrolled(int arg0, float arg1, int arg2) {
98 | }
99 |
100 | @Override
101 | public void onPageScrollStateChanged(int arg0) {
102 | }
103 | });
104 | }
105 |
106 |
107 | /**
108 | * No more menu
109 | *
110 | * @param menu
111 | * @return
112 | */
113 | @Override
114 | public boolean onCreateOptionsMenu(Menu menu) {
115 | // We don't need menu anymore now. "Settings" entry is on home page, as well as quick actions
116 | return true;
117 | }
118 |
119 | /**
120 | * We do not provide a menu.
121 | *
122 | * @param item
123 | * @return
124 | */
125 | @Override
126 | public boolean onOptionsItemSelected(MenuItem item) {
127 | return true;
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/UninstallBroadcast.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.util.Log;
8 | import org.ethack.orwall.lib.AppRule;
9 | import org.ethack.orwall.lib.NatRules;
10 |
11 | public class UninstallBroadcast extends BroadcastReceiver {
12 | private final static String TAG = "UninstallBroadcast";
13 |
14 | public UninstallBroadcast() {
15 | }
16 |
17 | @Override
18 | public void onReceive(Context context, Intent intent) {
19 | Uri data = intent.getData();
20 |
21 | if (!data.getScheme().equals("package")) {
22 | Log.d(TAG, "Intent scheme was not 'package'");
23 | return;
24 | }
25 |
26 | boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
27 |
28 | if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) && !replacing) {
29 | final long uid = intent.getIntExtra(Intent.EXTRA_UID, -123);
30 | final String appName = intent.getData().getSchemeSpecificPart();
31 | Log.d("UninstallBroadcast", "AppName: " + appName + ", AppUID: " + uid);
32 |
33 | // is the app present in rules?
34 | NatRules natRules = new NatRules(context);
35 |
36 | AppRule rule = natRules.getAppRule(uid);
37 | if (rule.isStored()) {
38 |
39 | // First: remove rule from firewall if any
40 | rule.uninstall(context);
41 |
42 | // Second: remove app from NatRules if present
43 | natRules.removeAppFromRules(uid);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/WizardActivity.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 | import android.support.v4.app.FragmentActivity;
6 | import android.support.v4.app.FragmentManager;
7 | import android.support.v4.app.FragmentStatePagerAdapter;
8 | import android.support.v4.view.PagerAdapter;
9 | import android.support.v4.view.ViewPager;
10 |
11 | import org.ethack.orwall.fragments.WizardFragment;
12 | import org.ethack.orwall.lib.Preferences;
13 |
14 | /**
15 | * Simple wizard activity.
16 | * It will displays orWall capabilities, explain some stuff
17 | *
18 | * Aim: provide main information in a smooth though complete way to the User, so that
19 | * he knows what to do.
20 | *
21 | * Taken from example in here:
22 | * https://developer.android.com/training/animation/screen-slide.html
23 | */
24 | public class WizardActivity extends FragmentActivity {
25 |
26 | // Number of pages (wizard steps)
27 | private static final int NUM_PAGES = 3;
28 |
29 | // Pager widget
30 | private ViewPager viewPager;
31 |
32 | // Pager adapter
33 | private PagerAdapter pagerAdapter;
34 |
35 | @Override
36 | protected void onCreate(Bundle savedInstanceState) {
37 | super.onCreate(savedInstanceState);
38 | setContentView(R.layout.activity_wizard);
39 |
40 | viewPager = (ViewPager) findViewById(R.id.wizard_pager);
41 | pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
42 | viewPager.setAdapter(pagerAdapter);
43 | viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
44 | @Override
45 | public void onPageSelected(int position) {
46 | invalidateOptionsMenu();
47 | }
48 | });
49 | }
50 |
51 | @Override
52 | public void onBackPressed() {
53 | if (viewPager.getCurrentItem() == 0) {
54 | // If the user is currently looking at the first step, allow the system to handle the
55 | // Back button. This calls finish() on this activity and pops the back stack.
56 | Preferences.setFirstRun(this, false);
57 | super.onBackPressed();
58 | } else {
59 | // Otherwise, select the previous step.
60 | viewPager.setCurrentItem(viewPager.getCurrentItem() - 1);
61 | }
62 | }
63 |
64 | /**
65 | * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
66 | * sequence.
67 | */
68 | private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
69 | public ScreenSlidePagerAdapter(FragmentManager fm) {
70 | super(fm);
71 | }
72 |
73 | @Override
74 | public Fragment getItem(int position) {
75 | return WizardFragment.create(position);
76 | }
77 |
78 | @Override
79 | public int getCount() {
80 | return NUM_PAGES;
81 | }
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/adapter/TabsPagerAdapter.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.adapter;
2 |
3 | import android.support.v4.app.Fragment;
4 | import android.support.v4.app.FragmentManager;
5 | import android.support.v4.app.FragmentPagerAdapter;
6 |
7 | import org.ethack.orwall.fragments.AppFragment;
8 | import org.ethack.orwall.fragments.HomeFragment;
9 |
10 | /**
11 | * A simple wrapper for tab management.
12 | */
13 | public class TabsPagerAdapter extends FragmentPagerAdapter {
14 |
15 | public TabsPagerAdapter(FragmentManager fragmentManager) {
16 | super(fragmentManager);
17 | }
18 |
19 | @Override
20 | public Fragment getItem(int index) {
21 | switch (index) {
22 | case 0:
23 | return new HomeFragment();
24 | case 1:
25 | return new AppFragment();
26 | }
27 | return null;
28 | }
29 |
30 | @Override
31 | public int getCount() {
32 | return 2;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/database/natDBHelper.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.database;
2 |
3 | import android.content.Context;
4 | import android.database.sqlite.SQLiteDatabase;
5 | import android.database.sqlite.SQLiteOpenHelper;
6 |
7 |
8 | /**
9 | * Simple DB helper in order to manage SQLite for NAT rules.
10 | * This also prepare the way for more features.
11 | */
12 | public class natDBHelper extends SQLiteOpenHelper {
13 |
14 | public static final String NAT_TABLE_NAME = "rules";
15 | public static final String COLUMN_APPUID = "appUID";
16 | public static final String COLUMN_APPNAME = "appName";
17 | public static final String COLUMN_ONIONTYPE = "onionType";
18 | public static final String COLUMN_LOCALHOST = "localhost";
19 | public static final String COLUMN_LOCALNETWORK = "localnetwork";
20 |
21 | /*
22 | @Deprecated
23 | private static final String COLUMN_ONIONPORT = "onionPort";
24 | @Deprecated
25 | private static final String DB_PORT_TYPE_FENCED = "Fenced";
26 | @Deprecated
27 | private static final String COLUMN_PORTTYPE = "portType";
28 |
29 | private static final String NAT_TABLE_CREATE_V1 =
30 | String.format(
31 | "CREATE TABLE %s (" +
32 | "%s INTEGER PRIMARY KEY," +
33 | "%s TEXT NOT NULL," +
34 | "%s TEXT NOT NULL DEFAULT \"%s\"," +
35 | "%s INTEGER NOT NULL DEFAULT '%d'," +
36 | "%s TEXT NOT NULL DEFAULT \"TransProxy\")",
37 | NAT_TABLE_NAME,
38 | COLUMN_APPUID,
39 | COLUMN_APPNAME,
40 | COLUMN_ONIONTYPE, Constants.DB_ONION_TYPE_TOR,
41 | COLUMN_ONIONPORT, Constants.ORBOT_TRANSPROXY,
42 | COLUMN_PORTTYPE
43 | );
44 | */
45 | private static final String NAT_TABLE_CREATE_V2 =
46 | String.format(
47 | "CREATE TABLE %s (" +
48 | "%s INTEGER PRIMARY KEY," +
49 | "%s TEXT NOT NULL," +
50 | "%s TEXT," +
51 | "%s INTEGER," +
52 | "%s INTEGER)",
53 | NAT_TABLE_NAME,
54 | COLUMN_APPUID,
55 | COLUMN_APPNAME,
56 | COLUMN_ONIONTYPE,
57 | COLUMN_LOCALHOST,
58 | COLUMN_LOCALNETWORK
59 | );
60 |
61 | private static final int DATABASE_VERSION = 2;
62 | private static final String DB_NAME = "nat.s3db";
63 |
64 | public natDBHelper(Context context) {
65 | super(context, DB_NAME, null, DATABASE_VERSION);
66 | }
67 |
68 | @Override
69 | public void onCreate(SQLiteDatabase db) {
70 | db.execSQL(NAT_TABLE_CREATE_V2);
71 | }
72 |
73 | @Override
74 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
75 | if (oldVersion == newVersion){
76 | return;
77 | }
78 |
79 | db.beginTransaction();
80 | try{
81 | for(int version = oldVersion; version < newVersion; version++){
82 | switch (version){
83 | // VERSION 1 -----> 2
84 | case 1:
85 | db.execSQL(String.format("ALTER TABLE %s RENAME TO %s_backup;", NAT_TABLE_NAME, NAT_TABLE_NAME));
86 | db.execSQL(NAT_TABLE_CREATE_V2);
87 | db.execSQL(String.format(
88 | "INSERT INTO %s(%s, %s, %s, %s, %s) SELECT %s, %s, %s, 0, 0 FROM %s_backup;",
89 | NAT_TABLE_NAME, COLUMN_APPUID, COLUMN_APPNAME, COLUMN_ONIONTYPE, COLUMN_LOCALHOST, COLUMN_LOCALNETWORK,
90 | COLUMN_APPUID, COLUMN_APPNAME, COLUMN_ONIONTYPE, NAT_TABLE_NAME));
91 | db.execSQL(String.format("DROP TABLE %s_backup;", NAT_TABLE_NAME));
92 | }
93 | }
94 |
95 | db.setTransactionSuccessful();
96 | } finally{
97 | db.endTransaction();
98 | }
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/fragments/AppFragment.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.fragments;
2 |
3 | import android.content.pm.PackageInfo;
4 | import android.content.pm.PackageManager;
5 | import android.os.Bundle;
6 | import android.support.v4.app.Fragment;
7 | import android.support.v4.util.LongSparseArray;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.ListView;
12 |
13 | import org.ethack.orwall.R;
14 | import org.ethack.orwall.adapter.AppListAdapter;
15 | import org.ethack.orwall.lib.AppRule;
16 | import org.ethack.orwall.lib.AppRuleComparator;
17 | import org.ethack.orwall.lib.Constants;
18 | import org.ethack.orwall.lib.Iptables;
19 | import org.ethack.orwall.lib.NatRules;
20 | import org.ethack.orwall.lib.PackageInfoData;
21 | import org.ethack.orwall.lib.Preferences;
22 | import org.sufficientlysecure.rootcommands.RootCommands;
23 |
24 | import java.util.ArrayList;
25 | import java.util.Collections;
26 | import java.util.List;
27 | import java.util.Map;
28 |
29 | /**
30 | * Manage "apps" tab fragment.
31 | *
32 | * @link org.ethack.orwall.TabbedMain
33 | */
34 | public class AppFragment extends Fragment {
35 |
36 | @Override
37 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
38 |
39 | View view;
40 |
41 | view = inflater.inflate(R.layout.fragment_tabbed_apps, container, false);
42 | Iptables iptables = new Iptables(getActivity());
43 | // Do we have root access ?
44 | if (RootCommands.rootAccessGiven()) {
45 | view.findViewById(R.id.warn_root).setVisibility(View.GONE);
46 | } else {
47 | view.findViewById(R.id.warn_root).setVisibility(View.VISIBLE);
48 | }
49 | // Hopefully there IS iptables on this device…
50 | if (Iptables.iptablesExists()) {
51 | view.findViewById(R.id.warn_iptables).setVisibility(View.GONE);
52 | } else {
53 | view.findViewById(R.id.warn_iptables).setVisibility(View.VISIBLE);
54 | }
55 | if (Iptables.initSupported() && !iptables.isInitialized()) {
56 | view.findViewById(R.id.warn_init).setVisibility(View.VISIBLE);
57 | }
58 |
59 | ListView listView = (ListView) view.findViewById(R.id.id_enabled_apps);
60 |
61 | // Toggle hint layer
62 | boolean hide_hint = Preferences.isHidePressHint(getActivity());
63 |
64 | if (hide_hint) {
65 | view.findViewById(R.id.hint_press).setVisibility(View.GONE);
66 | } else {
67 | view.findViewById(R.id.id_hide_hint).setOnClickListener(new View.OnClickListener() {
68 | @Override
69 | public void onClick(View view) {
70 | ((View) view.getParent()).setVisibility(View.GONE);
71 | Preferences.setHidePressHint(getActivity(), true);
72 | }
73 | });
74 | }
75 |
76 | // get enabled apps
77 | NatRules natRules = new NatRules(this.getActivity());
78 | List enabledApps = natRules.getAllRules();
79 | LongSparseArray rulesIndex = new LongSparseArray<>();
80 | for (AppRule app: enabledApps) rulesIndex.put(app.getAppUID(), app);
81 |
82 | // get disabled apps (filtered with enabled)
83 | List disabledApps = listDisabledApps(rulesIndex);
84 | // Get special, disabled apps
85 | List specialDisabled = listSpecialApps(rulesIndex);
86 |
87 | // Merge both disabled apps
88 | disabledApps.addAll(specialDisabled);
89 |
90 | // Sort collection using a dedicated method
91 | Collections.sort(enabledApps, new AppRuleComparator(getActivity().getPackageManager()));
92 | Collections.sort(disabledApps, new AppRuleComparator(getActivity().getPackageManager()));
93 |
94 | // merge both collections so that enabled apps are above disabled
95 | enabledApps.addAll(disabledApps);
96 |
97 | listView.setAdapter(new AppListAdapter(this.getActivity(), enabledApps));
98 |
99 | return view;
100 | }
101 |
102 | /**
103 | * List all disabled application. Meaning: installed app requiring Internet, but NOT in NatRules.
104 | * It also filters out special apps like orbot and i2p.
105 | *
106 | * @return List of AppRule
107 | */
108 | private List listDisabledApps(LongSparseArray index) {
109 | PackageManager packageManager = this.getActivity().getPackageManager();
110 | List pkgList = new ArrayList<>();
111 |
112 | List pkgInstalled = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
113 |
114 | for (PackageInfo pkgInfo : pkgInstalled) {
115 | if (needInternet(pkgInfo) && !isReservedApp(pkgInfo)) {
116 | if (index.indexOfKey((long) pkgInfo.applicationInfo.uid) < 0) {
117 | AppRule app = new AppRule(false, pkgInfo.packageName, (long) pkgInfo.applicationInfo.uid, Constants.DB_ONION_TYPE_NONE, false, false);
118 | app.setAppName(packageManager.getApplicationLabel(pkgInfo.applicationInfo).toString());
119 | pkgList.add(app);
120 | }
121 | }
122 | }
123 | return pkgList;
124 | }
125 |
126 | private List listSpecialApps(LongSparseArray index) {
127 | List pkgList = new ArrayList<>();
128 | Map specialApps = PackageInfoData.specialApps();
129 |
130 | for (PackageInfoData pkgInfo: specialApps.values()) {
131 | if (index.indexOfKey(pkgInfo.getUid()) < 0) {
132 | AppRule app = new AppRule(false, pkgInfo.getPkgName(), pkgInfo.getUid(), Constants.DB_ONION_TYPE_NONE, false, false);
133 | app.setAppName(pkgInfo.getName());
134 | pkgList.add(app);
135 | }
136 | }
137 |
138 | return pkgList;
139 | }
140 |
141 | /**
142 | * Checks if application requires Internet
143 | *
144 | * @param pkg PackageInfo object
145 | * @return true if package requires internet
146 | */
147 | private boolean needInternet(PackageInfo pkg) {
148 | String[] permissions = (pkg.requestedPermissions);
149 | if (permissions != null) {
150 | for (String perm : permissions) {
151 | if (perm.equals("android.permission.INTERNET")) {
152 | return true;
153 | }
154 | }
155 | }
156 | return false;
157 | }
158 |
159 | /**
160 | * Check if app name is a reserved one, like orbot or i2p
161 | *
162 | * @param pkg PackageInfo object
163 | * @return true if package name matches one of the reserved names
164 | */
165 | private boolean isReservedApp(PackageInfo pkg) {
166 | return (
167 | pkg.packageName.equals(Constants.ORBOT_APP_NAME) ||
168 | pkg.packageName.equals("org.ethack.orwall")
169 | );
170 | }
171 |
172 | }
173 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/fragments/WizardFragment.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.fragments;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.Fragment;
5 | import android.view.LayoutInflater;
6 | import android.view.View;
7 | import android.view.ViewGroup;
8 | import android.widget.CompoundButton;
9 | import android.widget.Switch;
10 | import android.widget.TextView;
11 |
12 | import org.ethack.orwall.R;
13 | import org.ethack.orwall.lib.InstallScripts;
14 | import org.ethack.orwall.lib.Iptables;
15 | import org.ethack.orwall.lib.Preferences;
16 | import org.ethack.orwall.lib.Util;
17 | import org.sufficientlysecure.rootcommands.RootCommands;
18 |
19 | import java.util.Locale;
20 |
21 | /**
22 | * A simple {@link Fragment} subclass.
23 | * Will display a simple Wizard explaining User what orWall can do.
24 | */
25 | public class WizardFragment extends Fragment {
26 |
27 | /**
28 | * The argument key for the page number this fragment represents.
29 | */
30 | public static final String ARG_PAGE = "page";
31 |
32 | /**
33 | * The fragment's page number, which is set to the argument value for {@link #ARG_PAGE}.
34 | */
35 | private int mPageNumber;
36 |
37 |
38 | public WizardFragment() {
39 | }
40 |
41 | /**
42 | * Factory method for this fragment class. Constructs a new fragment for the given page number.
43 | */
44 | public static WizardFragment create(int position) {
45 | WizardFragment fragment = new WizardFragment();
46 | Bundle args = new Bundle();
47 | args.putInt(ARG_PAGE, position);
48 | fragment.setArguments(args);
49 | return fragment;
50 | }
51 |
52 | @Override
53 | public void onCreate(Bundle savedInstanceState) {
54 | super.onCreate(savedInstanceState);
55 | mPageNumber = getArguments().getInt(ARG_PAGE);
56 | }
57 |
58 |
59 | @Override
60 | public View onCreateView(LayoutInflater inflater, ViewGroup container,
61 | Bundle savedInstanceState) {
62 | ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_wizard, container, false);
63 |
64 | int[] titles = {
65 | R.string.wizard_title_one,
66 | R.string.wizard_title_two,
67 | R.string.wizard_title_three,
68 | };
69 |
70 | String title = getString(R.string.wizard_title_one);
71 | String step = "Step 1: ";
72 | if (mPageNumber < titles.length) {
73 | title = getString(titles[mPageNumber]);
74 | step = String.format(Locale.US, "Step %d: ", mPageNumber + 1);
75 | }
76 | ((TextView) rootView.findViewById(R.id.wizard_step_title))
77 | .setText(step + title);
78 |
79 | int[] fragments = {
80 | R.string.wizard_first,
81 | R.string.wizard_second,
82 | R.string.wizard_third,
83 | };
84 |
85 | String fragment = getString(fragments[0]);
86 | if (mPageNumber < fragments.length) {
87 | fragment = getString(fragments[mPageNumber]);
88 | }
89 | ((TextView) rootView.findViewById(R.id.wizard_fragment_content)).setText(fragment);
90 |
91 | rootView.findViewById(R.id.wizard_close).setOnClickListener(new View.OnClickListener() {
92 | @Override
93 | public void onClick(View view) {
94 | Preferences.setFirstRun(getActivity(), false);
95 | getActivity().finish();
96 | }
97 | });
98 |
99 | // Add some stuff on the very first Wizard page
100 | if (mPageNumber == 0) {
101 | ViewGroup main_content = (ViewGroup) rootView.findViewById(R.id.id_main_content);
102 | final Iptables iptables = new Iptables(getActivity());
103 | // Extract scripts
104 | InstallScripts installScripts = new InstallScripts(getActivity());
105 | installScripts.run();
106 |
107 | // init-script installation
108 | // install init as default behavior
109 | Iptables.installInitScript(getActivity());
110 | boolean enforceInit = Preferences.isEnforceInitScript(getActivity());
111 | boolean initSupported = Iptables.initSupported();
112 |
113 | Switch initScript = new Switch(getActivity());
114 | initScript.setChecked( (enforceInit && initSupported) );
115 | initScript.setText(getString(R.string.wizard_init_script_text));
116 | initScript.setEnabled(initSupported);
117 |
118 | initScript.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
119 | @Override
120 | public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
121 | boolean checked = compoundButton.isChecked();
122 | if (checked) {
123 | Iptables.installInitScript(getActivity());
124 | } else {
125 | Iptables.removeIniScript(getActivity());
126 | }
127 | }
128 | });
129 |
130 | main_content.addView(initScript);
131 |
132 | // Root status
133 | Switch rootStatus = new Switch(getActivity());
134 | rootStatus.setChecked(RootCommands.rootAccessGiven());
135 | rootStatus.setEnabled(false);
136 | rootStatus.setText(getString(R.string.wizard_init_root_text));
137 | main_content.addView(rootStatus);
138 |
139 | // Does iptables exist?
140 | Switch iptablesStatus = new Switch(getActivity());
141 | iptablesStatus.setChecked(Iptables.iptablesExists());
142 | iptablesStatus.setEnabled(false);
143 | iptablesStatus.setText(getString(R.string.wizard_init_iptables_text));
144 | main_content.addView(iptablesStatus);
145 |
146 | // Does current kernel support IPTables comments?
147 | Switch iptablesComments = new Switch(getActivity());
148 | iptablesComments.setChecked(iptables.getSupportComment());
149 | iptablesComments.setEnabled(false);
150 | iptablesComments.setText(getString(R.string.wizard_init_ipt_comments_text));
151 | main_content.addView(iptablesComments);
152 |
153 | // Is orbot installed?
154 | Switch orbotStatus = new Switch(getActivity());
155 | orbotStatus.setChecked(Util.isOrbotInstalled(getActivity()));
156 | orbotStatus.setEnabled(false);
157 | orbotStatus.setText(getString(R.string.wizard_orbot_status_text));
158 | main_content.addView(orbotStatus);
159 |
160 | }
161 |
162 | return rootView;
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/AppPreferenceList.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageInfo;
5 | import android.content.pm.PackageManager;
6 | import android.preference.ListPreference;
7 | import android.preference.PreferenceManager;
8 | import android.util.AttributeSet;
9 | import android.view.View;
10 | import android.widget.ArrayAdapter;
11 | import android.widget.ListAdapter;
12 | import android.widget.ListView;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Collection;
16 | import java.util.Collections;
17 | import java.util.List;
18 |
19 | /**
20 | * Created by cedric on 7/26/14.
21 | */
22 | public class AppPreferenceList extends ListPreference {
23 |
24 | private final PackageManager packageManager;
25 |
26 | public AppPreferenceList(Context context, AttributeSet attributeSet) {
27 | super(context, attributeSet);
28 | this.packageManager = context.getPackageManager();
29 | }
30 |
31 | public AppPreferenceList(Context context) {
32 | super(context);
33 | this.packageManager = context.getPackageManager();
34 | }
35 |
36 | @Override
37 | protected View onCreateDialogView() {
38 | PreferenceManager preferenceManager = getPreferenceManager();
39 | String chosen_app = preferenceManager.getSharedPreferences().getString(this.getKey(), "0");
40 | ListView view = new ListView(getContext());
41 | view.setAdapter(adapter());
42 | setEntries(entries());
43 | setEntryValues(entryValues());
44 | setValue(chosen_app);
45 | setPersistent(true);
46 | setDefaultValue(chosen_app);
47 | return view;
48 | }
49 |
50 | private ListAdapter adapter() {
51 | return new ArrayAdapter(getContext(), android.R.layout.select_dialog_singlechoice);
52 | }
53 |
54 | private CharSequence[] entries() {
55 | List pkgList = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
56 | Collections.sort(pkgList, new PackageComparator(packageManager));
57 |
58 | Collection list = new ArrayList<>();
59 |
60 | for (PackageInfo pkg : pkgList) {
61 | if (isInternet(pkg))
62 | list.add(packageManager.getApplicationLabel(pkg.applicationInfo));
63 | }
64 |
65 | return list.toArray(new CharSequence[list.size()]);
66 | }
67 |
68 | private CharSequence[] entryValues() {
69 | List pkgList = packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS);
70 | Collections.sort(pkgList, new PackageComparator(packageManager));
71 |
72 | Collection list = new ArrayList<>();
73 |
74 | for (PackageInfo pkg : pkgList) {
75 | if (isInternet(pkg))
76 | list.add(Long.toString(pkg.applicationInfo.uid));
77 | }
78 | return list.toArray(new CharSequence[list.size()]);
79 | }
80 |
81 | private boolean isInternet(PackageInfo pkg) {
82 | String[] permissions = (pkg.requestedPermissions);
83 | if (permissions != null) {
84 | for (String perm : permissions) {
85 | if (perm.equals("android.permission.INTERNET")) {
86 | return true;
87 | }
88 | }
89 | }
90 | return false;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/AppRule.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | import org.ethack.orwall.BackgroundProcess;
7 |
8 | import java.util.ArrayList;
9 |
10 | /**
11 | * Data structure: application NAT rule.
12 | */
13 | public class AppRule {
14 | private Boolean stored;
15 | private String pkgName;
16 | private Long appUID;
17 | private String onionType;
18 | private Boolean localHost;
19 | private Boolean localNetwork;
20 |
21 | // Variables dedicated for ListView
22 | // We need them for persistence across scroll
23 | private String label;
24 | private String appName;
25 |
26 | public AppRule(Boolean stored, String pkgName, Long appUID, String onionType, Boolean localHost, Boolean localNetwork) {
27 | this.stored = stored;
28 | this.pkgName = pkgName;
29 | this.appUID = appUID;
30 | this.onionType = onionType;
31 | this.localHost = localHost;
32 | this.localNetwork = localNetwork;
33 | // set to a null value - used in AppListAdapter
34 | this.label = null;
35 | this.appName = null;
36 | }
37 |
38 | public AppRule() {
39 | // Empty constructor in order to use setters.
40 | this.stored = false;
41 | this.pkgName = null;
42 | this.appUID = null;
43 | this.onionType = Constants.DB_ONION_TYPE_NONE;
44 | this.localHost = false;
45 | this.localNetwork = false;
46 | // set to a null value - used in AppListAdapter
47 | this.label = null;
48 | this.appName = null;
49 | }
50 |
51 | public Boolean isStored(){
52 | return this.stored;
53 | }
54 |
55 | public void setStored(Boolean stored) {
56 | this.stored = stored;
57 | }
58 |
59 | public Boolean isEmpty(){
60 | return !this.localHost && !this.localNetwork && this.onionType.equals(Constants.DB_ONION_TYPE_NONE);
61 | }
62 |
63 | public String getPkgName() {
64 | return this.pkgName;
65 | }
66 |
67 | public void setPkgName(String pkgName) {
68 | this.pkgName = pkgName;
69 | }
70 |
71 | public String getOnionType() {
72 | return this.onionType;
73 | }
74 |
75 | public String getDisplay(){
76 | String ret = this.appName;
77 | ArrayList flags = new ArrayList<>();
78 | switch (this.onionType) {
79 | case Constants.DB_ONION_TYPE_NONE:
80 | break;
81 | case Constants.DB_ONION_TYPE_BYPASS:
82 | flags.add("Bypass");
83 | break;
84 | case Constants.DB_ONION_TYPE_TOR:
85 | flags.add("Tor");
86 | break;
87 | }
88 | if (this.localHost) {
89 | flags.add("Localhost");
90 | }
91 | if (this.localNetwork) {
92 | flags.add("LocalNetwork");
93 | }
94 |
95 | if (!flags.isEmpty()){
96 | ret += " (" + flags.get(0);
97 | for(int i = 1; i < flags.size(); i++){
98 | ret += " - " + flags.get(i);
99 | }
100 | ret += ")";
101 | }
102 | return ret;
103 | }
104 |
105 | public void setOnionType(String onionType) {
106 | this.onionType = onionType;
107 | }
108 |
109 | public Boolean getLocalHost() {
110 | return this.localHost;
111 | }
112 |
113 | public void setLocalHost(Boolean localHost) {
114 | this.localHost = localHost;
115 | }
116 |
117 | public Boolean getLocalNetwork() {
118 | return this.localNetwork;
119 | }
120 |
121 | public void setLocalNetwork(Boolean localNetwork) {
122 | this.localNetwork = localNetwork;
123 | }
124 |
125 | public Long getAppUID() {
126 | return this.appUID;
127 | }
128 |
129 | public void setAppUID(Long appUID) {
130 | this.appUID = appUID;
131 | }
132 |
133 | public String getLabel() {
134 | return this.label;
135 | }
136 |
137 | public void setLabel(String label) {
138 | this.label = label;
139 | }
140 |
141 | public String getAppName() {
142 | return this.appName;
143 | }
144 |
145 | public void setAppName(String appName) {
146 | this.appName = appName;
147 | }
148 |
149 | private Intent newBackground(Context context, Intent intent){
150 | Intent bg = (intent == null? new Intent(context, BackgroundProcess.class): intent);
151 | bg.putExtra(Constants.PARAM_APPUID, getAppUID());
152 | bg.putExtra(Constants.PARAM_APPNAME, getPkgName());
153 | bg.putExtra(Constants.PARAM_ONIONTYPE, getOnionType());
154 | bg.putExtra(Constants.PARAM_LOCALHOST, getLocalHost());
155 | bg.putExtra(Constants.PARAM_LOCALNETWORK, getLocalNetwork());
156 | return bg;
157 | }
158 |
159 | public void install(Context context, Intent intent){
160 | Intent bg = newBackground(context, intent);
161 | bg.putExtra(Constants.ACTION, Constants.ACTION_ADD_RULE);
162 | context.startService(bg);
163 | }
164 |
165 | public void uninstall(Context context, Intent intent){
166 | Intent bg = newBackground(context, intent);
167 | bg.putExtra(Constants.ACTION, Constants.ACTION_RM_RULE);
168 | context.startService(bg);
169 | }
170 |
171 | public void install(Context context){
172 | install(context, null);
173 | }
174 |
175 | public void uninstall(Context context){
176 | uninstall(context, null);
177 | }
178 |
179 |
180 | }
181 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/AppRuleComparator.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.pm.PackageInfo;
4 | import android.content.pm.PackageManager;
5 | import android.util.Log;
6 |
7 | import java.util.Comparator;
8 |
9 | /**
10 | * Comparator: allows to sort appRule collection using application name.
11 | */
12 | public class AppRuleComparator implements Comparator {
13 | private static final String TAG = "AppRuleComparator";
14 |
15 | private PackageManager packageManager;
16 |
17 | public AppRuleComparator(PackageManager packageManager) {
18 | this.packageManager = packageManager;
19 | }
20 |
21 | private String getLabel(AppRule appRule){
22 | if (appRule.getAppName() == null){
23 | if (appRule.getPkgName().startsWith(Constants.SPECIAL_APPS_PREFIX)) {
24 | appRule.setAppName(PackageInfoData.specialApps().get(appRule.getPkgName()).getName());
25 | } else {
26 | try {
27 | PackageInfo pkgInfo1 = packageManager.getPackageInfo(appRule.getPkgName(), PackageManager.GET_PERMISSIONS);
28 | appRule.setAppName(packageManager.getApplicationLabel(pkgInfo1.applicationInfo).toString());
29 | } catch (PackageManager.NameNotFoundException e) {
30 | Log.e(TAG, e.getMessage());
31 | }
32 | }
33 | }
34 | return appRule.getAppName();
35 | }
36 |
37 | @Override
38 | public int compare(AppRule appRule1, AppRule appRule2) {
39 | String label1 = getLabel(appRule1);
40 | String label2 = getLabel(appRule2);
41 |
42 | if (label1 != null && label2 != null) {
43 | return label1.compareTo(label2);
44 | } else {
45 | return 0;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/CheckSum.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.util.Log;
4 |
5 | import java.io.FileInputStream;
6 | import java.io.FileNotFoundException;
7 | import java.io.IOException;
8 | import java.security.MessageDigest;
9 | import java.security.NoSuchAlgorithmException;
10 |
11 | /**
12 | * Allows to checksum files.
13 | * Used for init-script installation.
14 | */
15 | public class CheckSum {
16 |
17 | private String method;
18 | private String file;
19 |
20 | /**
21 | * Class builder — default method is MD5
22 | *
23 | * @param file String, path to the file
24 | */
25 | public CheckSum(String file) {
26 | this.file = file;
27 | this.method = "MD5";
28 | }
29 |
30 | /**
31 | * Create the hash
32 | *
33 | * @return String, file hash
34 | */
35 | public String hash() {
36 | MessageDigest md;
37 | try {
38 | md = MessageDigest.getInstance(method);
39 | } catch (NoSuchAlgorithmException e) {
40 | Log.e("Hash", "No such algorithm: " + method);
41 | return Constants.E_NO_SUCH_ALGO;
42 | }
43 | FileInputStream fis;
44 | try {
45 | fis = new FileInputStream(file);
46 | } catch (FileNotFoundException e) {
47 | Log.e("Hash", "No such file: " + file);
48 | return Constants.E_NO_SUCH_FILE;
49 | }
50 |
51 | byte[] dataBytes = new byte[1024];
52 | int nread;
53 |
54 | try {
55 | while ((nread = fis.read(dataBytes)) != -1) {
56 | md.update(dataBytes, 0, nread);
57 | }
58 | } catch (IOException e) {
59 | return "E_IOEXCEPTION";
60 | }
61 |
62 | byte[] mdbytes = md.digest();
63 |
64 | StringBuffer sb = new StringBuffer();
65 | for (byte mdb : mdbytes) {
66 | sb.append(Integer.toHexString(0xFF & mdb));
67 | }
68 | Log.d("Checksum", sb.toString());
69 | return sb.toString();
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/Constants.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | /**
4 | * Constants used across the code
5 | */
6 | public class Constants {
7 | public final static String SPECIAL_APPS_PREFIX = "orwall.special.";
8 | public final static String IPTABLES = "/system/bin/iptables";
9 | public final static String IP6TABLES = "/system/bin/ip6tables";
10 |
11 | public final static String ACTION = "org.ethack.orwall.backgroundProcess.action";
12 | public final static String ACTION_PORTAL = "org.ethack.orwall.backgroundProcess.action.portal";
13 | public final static String PARAM_ACTIVATE = "org.ethack.orwall.captive.activate";
14 |
15 | public final static String ACTION_ADD_RULE = "org.ethack.orwall.backgroundProcess.action.addRule";
16 | public final static String ACTION_RM_RULE = "org.ethack.orwall.backgroundProcess.action.rmRule";
17 | public final static String PARAM_APPUID = "org.ethack.orwall.backgroundProcess.action.rule.appUid";
18 | public final static String PARAM_APPNAME = "org.ethack.orwall.backgroundProcess.action.rule.appName";
19 | public final static String PARAM_LOCALHOST = "org.ethack.orwall.backgroundProcess.action.rule.localHost";
20 | public final static String PARAM_LOCALNETWORK = "org.ethack.orwall.backgroundProcess.action.rule.localNetwork";
21 | public final static String PARAM_ONIONTYPE = "org.ethack.orwall.backgroundProcess.action.rule.onionType";
22 |
23 | public final static String ACTION_DISABLE_ORWALL = "org.ethack.orwall.backgroundProcess.action.disable_orwall";
24 | public final static String ACTION_ENABLE_ORWALL = "org.ethack.orwall.backgroundProcess.action.enable_orwall";
25 |
26 | public final static String E_NO_SUCH_FILE = "E_NO_SUCH_FILE";
27 | public final static String E_NO_SUCH_ALGO = "E_NO_SUCH_ALGO";
28 |
29 | public final static String DB_ONION_TYPE_NONE = "None";
30 | public final static String DB_ONION_TYPE_TOR = "Tor";
31 | public final static String DB_ONION_TYPE_BYPASS = "Bypass";
32 |
33 | public final static String ORBOT_APP_NAME = "org.torproject.android";
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/InstallScripts.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 |
6 | import org.ethack.orwall.R;
7 |
8 | import java.io.File;
9 | import java.io.FileOutputStream;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 |
13 | /**
14 | * Created by cedric on 7/25/14.
15 | */
16 | public class InstallScripts extends Thread {
17 | private final Context context;
18 |
19 | public InstallScripts(Context context) {
20 | this.context = context;
21 | }
22 |
23 | /**
24 | * Thanks to AFWall :)
25 | *
26 | * @param ctx
27 | * @param resId
28 | * @param filename
29 | * @return
30 | */
31 | private static boolean installBinary(Context ctx, int resId, String filename) {
32 | try {
33 | File f = new File(ctx.getDir("bin", 0), filename);
34 | if (f.exists()) {
35 | f.delete();
36 | }
37 | copyRawFile(ctx, resId, f, "0755");
38 | return true;
39 | } catch (Exception e) {
40 | Log.e("InstallScripts", "installBinary failed: " + e.getLocalizedMessage());
41 | return false;
42 | }
43 | }
44 |
45 | /**
46 | * Copies a raw resource file, given its ID to the given location
47 | *
48 | * @param ctx context
49 | * @param resid resource id
50 | * @param file destination file
51 | * @param mode file permissions (E.g.: "755")
52 | * @throws java.io.IOException on error
53 | * @throws InterruptedException when interrupted
54 | *
55 | * Thanks AFWall source code
56 | */
57 | private static void copyRawFile(Context ctx, int resid, File file, String mode) throws IOException, InterruptedException {
58 | final String abspath = file.getAbsolutePath();
59 | final FileOutputStream out = new FileOutputStream(file);
60 | final InputStream is = ctx.getResources().openRawResource(resid);
61 | byte buf[] = new byte[1024];
62 | int len;
63 | while ((len = is.read(buf)) > 0) {
64 | out.write(buf, 0, len);
65 | }
66 | out.close();
67 | is.close();
68 | // Change the permissions
69 | Runtime.getRuntime().exec("chmod " + mode + " " + abspath).waitFor();
70 | }
71 |
72 | @Override
73 | public void run() {
74 | if (!installBinary(context, R.raw.activate_portal, "activate_portal.sh")) {
75 | Log.e("Init", "Unable to install activate_portal script");
76 | }
77 | if (!installBinary(context, R.raw.deactivate_portal, "deactivate_portal.sh")) {
78 | Log.e("Init", "Unable to install deactivate_portal script");
79 | }
80 | if (!installBinary(context, R.raw.userinit, "userinit.sh")) {
81 | Log.e("Init", "We're fucked… unable to extract userinit.sh script");
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/NatRules.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.ContentValues;
4 | import android.content.Context;
5 | import android.content.pm.PackageManager;
6 | import android.database.Cursor;
7 | import android.database.sqlite.SQLiteConstraintException;
8 | import android.database.sqlite.SQLiteDatabase;
9 |
10 | import org.ethack.orwall.database.natDBHelper;
11 | import org.sufficientlysecure.rootcommands.util.Log;
12 |
13 | import java.util.ArrayList;
14 | import java.util.HashMap;
15 | import java.util.Set;
16 |
17 | /**
18 | * Helper: manage apps in SQLite, in order to prevent concurrent accesses to the DB.
19 | */
20 | public class NatRules {
21 | private final static String TAG = "NatRules";
22 | private natDBHelper dbHelper;
23 | private Context context;
24 |
25 | public NatRules(Context context) {
26 | this.dbHelper = new natDBHelper(context);
27 | this.context = context;
28 | }
29 |
30 | public boolean removeAppFromRules(Long appUID) {
31 | String filter = natDBHelper.COLUMN_APPUID + "=?";
32 | String[] filterArgs = {String.valueOf(appUID)};
33 |
34 | SQLiteDatabase db = this.dbHelper.getWritableDatabase();
35 | int result = db.delete(natDBHelper.NAT_TABLE_NAME, filter, filterArgs);
36 | db.close();
37 | return (result == 1);
38 | }
39 |
40 | public boolean addAppToRules(Long appUID, String appName, String onionType, Boolean localHost, Boolean localNetwork) {
41 |
42 | ContentValues contentValues = new ContentValues();
43 | contentValues.put(natDBHelper.COLUMN_APPNAME, appName);
44 | contentValues.put(natDBHelper.COLUMN_APPUID, String.valueOf(appUID));
45 | contentValues.put(natDBHelper.COLUMN_ONIONTYPE, onionType);
46 | contentValues.put(natDBHelper.COLUMN_LOCALHOST, localHost);
47 | contentValues.put(natDBHelper.COLUMN_LOCALNETWORK, localNetwork);
48 |
49 | SQLiteDatabase db = this.dbHelper.getWritableDatabase();
50 | long result = db.insert(natDBHelper.NAT_TABLE_NAME, null, contentValues);
51 | db.close();
52 | return (result > 0);
53 | }
54 |
55 | public boolean addAppToRules(AppRule appRule) {
56 | return addAppToRules(
57 | appRule.getAppUID(),
58 | appRule.getPkgName(),
59 | appRule.getOnionType(),
60 | appRule.getLocalHost(),
61 | appRule.getLocalNetwork()
62 | );
63 | }
64 |
65 | public ArrayList getAllRules() {
66 | ArrayList list = new ArrayList<>();
67 |
68 | SQLiteDatabase db = this.dbHelper.getReadableDatabase();
69 | String[] selection = {
70 | natDBHelper.COLUMN_APPNAME,
71 | natDBHelper.COLUMN_APPUID,
72 | natDBHelper.COLUMN_ONIONTYPE,
73 | natDBHelper.COLUMN_LOCALHOST,
74 | natDBHelper.COLUMN_LOCALNETWORK
75 | };
76 | Cursor cursor = db.query(natDBHelper.NAT_TABLE_NAME, selection, null, null, null, null, null);
77 |
78 | if (!cursor.moveToFirst()) {
79 | Log.e(TAG, "getAllRules size is null!");
80 | return list;
81 | }
82 |
83 | AppRule appRule;
84 |
85 | do {
86 | appRule = new AppRule(
87 | true,
88 | cursor.getString(0),
89 | cursor.getLong(1),
90 | cursor.getString(2),
91 | cursor.getLong(3) == 1,
92 | cursor.getLong(4) == 1
93 | );
94 | list.add(appRule);
95 | } while (cursor.moveToNext());
96 |
97 | cursor.close();
98 | db.close();
99 | Log.d(TAG, "getAllRules size: " + String.valueOf(list.size()));
100 | return list;
101 | }
102 |
103 | public int getRuleCount() {
104 | SQLiteDatabase db = this.dbHelper.getReadableDatabase();
105 | Cursor cursor = db.query(natDBHelper.NAT_TABLE_NAME, null, null, null, null, null, null);
106 | cursor.moveToFirst();
107 |
108 | int total = cursor.getCount();
109 | cursor.close();
110 | db.close();
111 | return total;
112 | }
113 |
114 | public void importFromSharedPrefs(Set oldRules) {
115 | PackageManager packageManager = this.context.getPackageManager();
116 | for (Object rule : oldRules.toArray()) {
117 | HashMap r = (HashMap) rule;
118 | Long uid = (Long) r.values().toArray()[0];
119 | String name = (String) r.keySet().toArray()[0];
120 | // ensure we migrate only existing applications
121 | try {
122 | packageManager.getApplicationInfo(name, PackageManager.GET_META_DATA);
123 | addAppToRules(uid, name, Constants.DB_ONION_TYPE_TOR, false, false);
124 | } catch (PackageManager.NameNotFoundException e) {
125 | }
126 | }
127 | }
128 |
129 | public boolean update(AppRule appRule) {
130 | ContentValues contentValues = new ContentValues();
131 | contentValues.put(natDBHelper.COLUMN_APPNAME, appRule.getPkgName());
132 | contentValues.put(natDBHelper.COLUMN_APPUID, String.valueOf(appRule.getAppUID()));
133 | contentValues.put(natDBHelper.COLUMN_ONIONTYPE, appRule.getOnionType());
134 | contentValues.put(natDBHelper.COLUMN_LOCALHOST, appRule.getLocalHost()?1:0);
135 | contentValues.put(natDBHelper.COLUMN_LOCALNETWORK, appRule.getLocalNetwork()?1:0);
136 |
137 | String filter = natDBHelper.COLUMN_APPUID + "=?";
138 | String[] filterArgs = {String.valueOf(appRule.getAppUID())};
139 | SQLiteDatabase db = this.dbHelper.getWritableDatabase();
140 |
141 | int nb_row = 0;
142 | try {
143 | nb_row = db.update(natDBHelper.NAT_TABLE_NAME, contentValues, filter, filterArgs);
144 | } catch (SQLiteConstraintException e) {
145 | Log.e(TAG, "Constraint exception");
146 | Log.e(TAG, e.getMessage());
147 | }
148 | db.close();
149 |
150 | return (nb_row == 1);
151 | }
152 |
153 | public AppRule getAppRule(Long appUID) {
154 | SQLiteDatabase db = this.dbHelper.getReadableDatabase();
155 |
156 | String[] filterArgs = {
157 | String.valueOf(appUID)
158 | };
159 | String[] selection = {
160 | natDBHelper.COLUMN_APPNAME,
161 | natDBHelper.COLUMN_APPUID,
162 | natDBHelper.COLUMN_ONIONTYPE,
163 | natDBHelper.COLUMN_LOCALHOST,
164 | natDBHelper.COLUMN_LOCALNETWORK
165 | };
166 |
167 | Cursor cursor = db.query(
168 | natDBHelper.NAT_TABLE_NAME,
169 | selection,
170 | natDBHelper.COLUMN_APPUID + "=?",
171 | filterArgs,
172 | null,
173 | null,
174 | null
175 | );
176 |
177 | AppRule appRule;
178 | if (cursor.moveToFirst()) {
179 | appRule = new AppRule(
180 | true,
181 | cursor.getString(0),
182 | cursor.getLong(1),
183 | cursor.getString(2),
184 | cursor.getLong(3) == 1,
185 | cursor.getLong(4) == 1
186 | );
187 | } else {
188 | appRule = new AppRule();
189 | Log.e(TAG, "Unable to get rules for " + String.valueOf(appUID));
190 | }
191 | cursor.close();
192 | db.close();
193 |
194 | return appRule;
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/NetworkHelper.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.Context;
4 | import android.net.DhcpInfo;
5 | import android.net.ConnectivityManager;
6 | import android.net.wifi.WifiManager;
7 | import android.util.Log;
8 |
9 | import java.lang.reflect.InvocationTargetException;
10 | import java.lang.reflect.Method;
11 | import java.net.Inet4Address;
12 | import java.net.InetAddress;
13 | import java.net.InterfaceAddress;
14 | import java.net.NetworkInterface;
15 | import java.net.SocketException;
16 | import java.nio.ByteOrder;
17 | import java.util.Collections;
18 | import java.util.Iterator;
19 | import java.util.Locale;
20 | import java.util.Set;
21 |
22 | /**
23 | * Small helper in order to get some network information
24 | */
25 | public class NetworkHelper {
26 |
27 | private static String TAG = "NetworkHelper";
28 |
29 | /**
30 | * Tries to detect if we're sharing the connection or not.
31 | * It's not that easy, as it seems there is no simple API to call for that :(.
32 | *
33 | * @param context Context in order to get ConnectivityManager
34 | * @return boolean (true if connection is shared)
35 | */
36 | public static boolean isTether(Context context) {
37 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
38 |
39 | Method[] methods = connectivityManager.getClass().getDeclaredMethods();
40 | String[] tethered = {};
41 | for (Method method : methods) {
42 | if (method.getName().equals("getTetheredIfaces")) {
43 | try {
44 | tethered = (String[]) method.invoke(connectivityManager);
45 | } catch (IllegalArgumentException e) {
46 | Log.e(TAG, e.getMessage());
47 | } catch (IllegalAccessException e) {
48 | Log.e(TAG, e.getMessage());
49 | } catch (InvocationTargetException e) {
50 | Log.e(TAG, e.getMessage());
51 | }
52 | }
53 | }
54 | return (tethered.length != 0);
55 | }
56 |
57 | private static int netmaskToCIDR(int netmask){
58 | switch (netmask){
59 | case 0x80000000: return 1;
60 | case 0xC0000000: return 2;
61 | case 0xE0000000: return 3;
62 | case 0xF0000000: return 4;
63 | case 0xF8000000: return 5;
64 | case 0xFC000000: return 6;
65 | case 0xFE000000: return 7;
66 | case 0xFF000000: return 8;
67 | case 0xFF800000: return 9;
68 | case 0xFFC00000: return 10;
69 | case 0xFFE00000: return 11;
70 | case 0xFFF00000: return 12;
71 | case 0xFFF80000: return 13;
72 | case 0xFFFC0000: return 14;
73 | case 0xFFFE0000: return 15;
74 | case 0xFFFF0000: return 16;
75 | case 0xFFFF8000: return 17;
76 | case 0xFFFFC000: return 18;
77 | case 0xFFFFE000: return 19;
78 | case 0xFFFFF000: return 20;
79 | case 0xFFFFF800: return 21;
80 | case 0xFFFFFC00: return 22;
81 | case 0xFFFFFE00: return 23;
82 | case 0xFFFFFF00: return 24;
83 | case 0xFFFFFF80: return 25;
84 | case 0xFFFFFFC0: return 26;
85 | case 0xFFFFFFE0: return 27;
86 | case 0xFFFFFFF0: return 28;
87 | case 0xFFFFFFF8: return 29;
88 | case 0xFFFFFFFC: return 30;
89 | case 0xFFFFFFFE: return 31;
90 | case 0xFFFFFFFF: return 32;
91 | default:
92 | return 0;
93 | }
94 | }
95 |
96 | private static String getNetwork(DhcpInfo dhcp){
97 | int ip = dhcp.ipAddress;
98 | int mask = dhcp.netmask;
99 | if (ip == 0 || mask == 0) return null;
100 |
101 | if (ByteOrder.nativeOrder().equals(ByteOrder.LITTLE_ENDIAN)) {
102 | ip = Integer.reverseBytes(ip);
103 | mask = Integer.reverseBytes(mask);
104 | }
105 |
106 | ip &= mask;
107 | mask = netmaskToCIDR(mask);
108 | if (mask == 0) return null;
109 |
110 | int a = (ip >> 24) & 0xFF;
111 | int b = (ip >> 16) & 0xFF;
112 | int c = (ip >> 8) & 0xFF;
113 | int d = ip & 0xFF;
114 |
115 | return String.format(Locale.US, "%d.%d.%d.%d/%d", a, b, c, d, mask);
116 | }
117 |
118 | /**
119 | * Provide a simple way to get subnet
120 | *
121 | * @param context Context in order to get WifiManager
122 | * @return subnet as a String
123 | */
124 |
125 | public static String getSubnet(Context context) {
126 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
127 | return getNetwork(wifiManager.getDhcpInfo());
128 | }
129 |
130 | public static void getTetheredInterfaces(Context context, Set set){
131 | ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
132 | for(Method method: cm.getClass().getDeclaredMethods()){
133 | if(method.getName().equals("getTetheredIfaces")){
134 | try {
135 | String[] intfs = (String[]) method.invoke(cm);
136 | if (intfs != null)
137 | Collections.addAll(set, intfs);
138 | break;
139 | } catch (Exception e) {
140 | return;
141 | }
142 | }
143 | }
144 | }
145 |
146 | public static String getMask(String intf){
147 | try {
148 | NetworkInterface ntwrk = NetworkInterface.getByName(intf);
149 | Iterator addrList = ntwrk.getInterfaceAddresses().iterator();
150 | while (addrList.hasNext()) {
151 | InterfaceAddress addr = addrList.next();
152 | InetAddress ip = addr.getAddress();
153 | if (ip instanceof Inet4Address) {
154 | String mask = ip.getHostAddress() + "/" +
155 | addr.getNetworkPrefixLength();
156 | return mask;
157 | }
158 | }
159 | } catch (SocketException e) {
160 | e.printStackTrace();
161 | }
162 | return null;
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/PackageComparator.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.pm.PackageInfo;
4 | import android.content.pm.PackageManager;
5 |
6 | import java.util.Comparator;
7 |
8 | /**
9 | * Package comparator in order to sort a collection of application.
10 | */
11 | public class PackageComparator implements Comparator {
12 | private PackageManager packageManager;
13 |
14 | public PackageComparator(PackageManager packageManager) {
15 | this.packageManager = packageManager;
16 | }
17 |
18 | @Override
19 | public int compare(PackageInfo packageInfo, PackageInfo packageInfo2) {
20 | String label1 = packageManager.getApplicationLabel(packageInfo.applicationInfo).toString();
21 | String label2 = packageManager.getApplicationLabel(packageInfo2.applicationInfo).toString();
22 | return label1.compareTo(label2);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/PackageInfoData.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * Small structure holding special application info
8 | * Taken from AFWall+:
9 | * https://github.com/ukanth/afwallplus/blob/fd4843824e6e6678c8b50204b68a01f6fb8ed3d0/src/dev/ukanth/ufirewall/Api.java#L1569
10 | */
11 | public class PackageInfoData {
12 | // Linux user ID
13 | private long uid;
14 | // Application name related to UID
15 | private String name;
16 | private String pkgName;
17 |
18 | public PackageInfoData(long uid, String name, String pkgName) {
19 | this.uid = uid;
20 | this.name = name;
21 | this.pkgName = pkgName;
22 | }
23 |
24 | public PackageInfoData(String user, String name, String pkgName) {
25 | this(android.os.Process.getUidForName(user), name, pkgName);
26 | }
27 |
28 | public void setUid(long uid) {
29 | this.uid = uid;
30 | }
31 | public long getUid() {
32 | return this.uid;
33 | }
34 |
35 | public void setName(String name) {
36 | this.name = name;
37 | }
38 | public String getName() {
39 | return this.name;
40 | }
41 |
42 | public void setPkgName(String pkgName) {
43 | this.pkgName = pkgName;
44 | }
45 | public String getPkgName() {
46 | return this.pkgName;
47 | }
48 |
49 | /**
50 | * It seems some system things don't show up in packagemanager.
51 | * This code comes from AFWall+, as they already hit this small problem:
52 | * https://github.com/ukanth/afwallplus/blob/fd4843824e6e6678c8b50204b68a01f6fb8ed3d0/src/dev/ukanth/ufirewall/Api.java#L2313-L2327
53 | */
54 | public static Map specialApps() {
55 | String prefix = Constants.SPECIAL_APPS_PREFIX;
56 | Map specialApps = new HashMap<>();
57 | specialApps.put(prefix+"media", new PackageInfoData("media", "Media Server", prefix+"media"));
58 | specialApps.put(prefix+"vpn", new PackageInfoData("vpn", "VPN Service", prefix+"vpn"));
59 | specialApps.put(prefix+"shell", new PackageInfoData("shell", "Linux Shell", prefix+"shell"));
60 | specialApps.put(prefix+"adb", new PackageInfoData("adb", "Android Debug Bridge (ADB)", prefix+"adb"));
61 |
62 | return specialApps;
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/Preferences.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.support.annotation.Nullable;
6 |
7 | import java.util.Set;
8 |
9 | public final class Preferences {
10 | public final static String PREFERENCES = "org.ethack.orwall_preferences";
11 | public final static String PREF_KEY_FIRST_RUN = "first_run";
12 | public final static String PREF_KEY_SIP_APP = "sip_app";
13 | public final static String PREF_KEY_SIP_ENABLED = "sip_enabled";
14 | public final static String PREF_KEY_SPEC_BROWSER = "browser_app";
15 | public final static String PREF_KEY_BROWSER_ENABLED = "browser_enabled";
16 | //public final static String PREF_KEY_IS_TETHER_ENABLED = "is_tether_enabled";
17 | public final static String PREF_KEY_TETHER_INTFS = "tether_interfaces";
18 | public final static String PREF_TRANS_PORT = "proxy_transport";
19 | public final static String PREF_DNS_PORT = "proxy_dns";
20 | public final static String PREF_KEY_ADB_ENABLED = "enable_adb";
21 | public final static String PREF_KEY_SSH_ENABLED = "enable_ssh";
22 | public final static String PREF_KEY_ENFORCE_INIT = "enforce_init_script";
23 | //public final static String PREF_KEY_DISABLE_INIT = "deactivate_init_script";
24 | public final static String PREF_KEY_BROWSER_GRACETIME = "browser_gracetime";
25 | //public final static String PREF_KEY_IPT_SUPPORTS_COMMENTS = "ipt_comments";
26 | public final static String PREF_KEY_ORWALL_ENABLED = "orwall_enabled";
27 | public final static String PREF_KEY_CURRENT_SUBNET = "current_subnet";
28 | public final static String PREF_KEY_HIDE_PRESS_HINT = "hide_press_hint";
29 | public final static String PREF_KEY_TETHER_NETWORK = "tether_net_";
30 |
31 | public static long ORBOT_TRANSPROXY = 9040;
32 | public static long ORBOT_DNS_PROXY = 5400;
33 |
34 | public final static long BROWSER_GRACETIME = 5;
35 |
36 | private static boolean getBoolean(Context context, String key, boolean def){
37 | return context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).getBoolean(key, def);
38 | }
39 |
40 | private static void putBoolean(Context context, String key, boolean value){
41 | context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).edit().putBoolean(key, value).apply();
42 | }
43 |
44 | private static String getString(Context context, String key, String def){
45 | return context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).getString(key, def);
46 | }
47 |
48 | private static void setString(Context context, String key, String value){
49 | context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).edit().putString(key, value).apply();
50 | }
51 |
52 | private static Set getStringSet(Context context, String key, Set def){
53 | return context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).getStringSet(key, def);
54 | }
55 |
56 | private static void setStringSet(Context context, String key, Set value){
57 | context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE).edit().putStringSet(key, value).apply();
58 | }
59 |
60 | public static boolean isFirstRun(Context context){
61 | return getBoolean(context, PREF_KEY_FIRST_RUN, true);
62 | }
63 |
64 | public static void setFirstRun(Context context, boolean value){
65 | putBoolean(context, PREF_KEY_FIRST_RUN, value);
66 | }
67 |
68 | public static String getSIPApp(Context context){
69 | return getString(context, PREF_KEY_SIP_APP, "0");
70 | }
71 |
72 | public static boolean isSIPEnabled(Context context){
73 | return getBoolean(context, PREF_KEY_SIP_ENABLED, false);
74 | }
75 |
76 | public static void setSIPEnabled(Context context, boolean value){
77 | putBoolean(context, PREF_KEY_SIP_ENABLED, value);
78 | }
79 |
80 | public static String getBrowserApp(Context context){
81 | return getString(context, PREF_KEY_SPEC_BROWSER, "0");
82 | }
83 |
84 | public static boolean isBrowserEnabled(Context context){
85 | return getBoolean(context, PREF_KEY_BROWSER_ENABLED, false);
86 | }
87 |
88 | public static void setBrowserEnabled(Context context, boolean value){
89 | putBoolean(context, PREF_KEY_BROWSER_ENABLED, value);
90 | }
91 |
92 | public static Set getTetherInterfaces(Context context){
93 | return getStringSet(context, PREF_KEY_TETHER_INTFS, null);
94 | }
95 |
96 | public static void setTetherInterfaces(Context context, @Nullable Set value){
97 | setStringSet(context, PREF_KEY_TETHER_INTFS, value);
98 | }
99 |
100 | public static void cleanIptablesPreferences(Context context){
101 | SharedPreferences sharedPreferences = context.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
102 | sharedPreferences.edit().remove(PREF_KEY_CURRENT_SUBNET).apply();
103 | sharedPreferences.edit().remove(PREF_KEY_TETHER_INTFS).apply();
104 | }
105 |
106 | public static String getTransPort(Context context){
107 | return getString(context, PREF_TRANS_PORT, String.valueOf(ORBOT_TRANSPROXY));
108 | }
109 |
110 | public static String getDNSPort(Context context){
111 | return getString(context, PREF_DNS_PORT, String.valueOf(ORBOT_DNS_PROXY));
112 | }
113 |
114 | public static boolean isADBEnabled(Context context){
115 | return getBoolean(context, PREF_KEY_ADB_ENABLED, false);
116 | }
117 |
118 | public static boolean isSSHEnabled(Context context){
119 | return getBoolean(context, PREF_KEY_SSH_ENABLED, false);
120 | }
121 |
122 | public static boolean isEnforceInitScript(Context context){
123 | return getBoolean(context, PREF_KEY_ENFORCE_INIT, true);
124 | }
125 |
126 | public static void setEnforceInitScript(Context context, boolean value){
127 | putBoolean(context, PREF_KEY_ENFORCE_INIT, value);
128 | }
129 |
130 | public static String getBrowserGraceTime(Context context){
131 | return getString(context, PREF_KEY_BROWSER_GRACETIME, String.valueOf(BROWSER_GRACETIME));
132 | }
133 |
134 | public static boolean isOrwallEnabled(Context context){
135 | return getBoolean(context, PREF_KEY_ORWALL_ENABLED, true);
136 | }
137 |
138 | public static void setOrwallEnabled(Context context, boolean value){
139 | putBoolean(context, PREF_KEY_ORWALL_ENABLED, value);
140 | }
141 |
142 | public static String getCurrentSubnet(Context context){
143 | return getString(context, PREF_KEY_CURRENT_SUBNET, null);
144 | }
145 |
146 | public static void setCurrentSubnet(Context context, String value){
147 | setString(context, PREF_KEY_CURRENT_SUBNET, value);
148 | }
149 |
150 | public static boolean isHidePressHint(Context context){
151 | return getBoolean(context, PREF_KEY_HIDE_PRESS_HINT, false);
152 | }
153 |
154 | public static void setHidePressHint(Context context, boolean value){
155 | putBoolean(context, PREF_KEY_HIDE_PRESS_HINT, value);
156 | }
157 |
158 | public static String getTetherNetwork(Context context, String intf){
159 | return getString(context, PREF_KEY_TETHER_NETWORK + intf, null);
160 | }
161 |
162 | public static void setTetherNetwork(Context context, String intf, String network){
163 | setString(context, PREF_KEY_TETHER_NETWORK + intf, network);
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/app/src/main/java/org/ethack/orwall/lib/Util.java:
--------------------------------------------------------------------------------
1 | package org.ethack.orwall.lib;
2 |
3 | import android.content.Context;
4 | import android.content.pm.PackageManager;
5 | import android.os.Build;
6 | import android.util.Log;
7 |
8 | import org.sufficientlysecure.rootcommands.Shell;
9 | import org.sufficientlysecure.rootcommands.command.SimpleCommand;
10 |
11 | import java.io.File;
12 | import java.io.IOException;
13 | import java.util.concurrent.TimeoutException;
14 |
15 | public class Util {
16 | public static boolean isOrbotInstalled(Context context) {
17 | try {
18 | PackageManager pm = context.getPackageManager();
19 | pm.getPackageInfo(Constants.ORBOT_APP_NAME, PackageManager.GET_ACTIVITIES);
20 | return true;
21 | } catch (PackageManager.NameNotFoundException e) {
22 | return false;
23 | }
24 | }
25 |
26 | public static int getOrbotUID(Context context){
27 | try {
28 | PackageManager pm = context.getPackageManager();
29 | return pm.getApplicationInfo(Constants.ORBOT_APP_NAME, 0).uid;
30 | } catch (PackageManager.NameNotFoundException e) {
31 | return 0;
32 | }
33 | }
34 |
35 | /**
36 | * Apply or remove rules for captive portal detection.
37 | * Captive portal detection works with DNS and redirection detection.
38 | * Once the device is connected, Android will probe the network in order to get a page, located on Google servers.
39 | * If it can connect to it, this means we're not in a captive network; otherwise, it will prompt for network login.
40 | * @param status boolean, true if we want to enable this probe.
41 | * @param context application context
42 | */
43 | public static void enableCaptiveDetection(boolean status, Context context) {
44 | // TODO: find a way to disable it on android <4.4
45 | // TODO: we may want to get some setting writer directly through the API.
46 | // This seems to be done with a System app only. orWall may become a system app.
47 | if (Build.VERSION.SDK_INT > 18) {
48 |
49 | String CMD;
50 | if (status) {
51 | CMD = new File(context.getDir("bin", 0), "activate_portal.sh").getAbsolutePath();
52 | } else {
53 | CMD = new File(context.getDir("bin", 0), "deactivate_portal.sh").getAbsolutePath();
54 | }
55 | Shell shell = null;
56 | try {
57 | shell = Shell.startRootShell();
58 | } catch (IOException e) {
59 | Log.e("Shell", "Unable to get shell");
60 | }
61 |
62 | if (shell != null) {
63 | SimpleCommand command = new SimpleCommand(CMD);
64 | try {
65 | shell.add(command).waitForFinish();
66 | } catch (IOException e) {
67 | Log.e("Shell", "IO Error");
68 | } catch (TimeoutException e) {
69 | Log.e("Shell", "Timeout");
70 | } finally {
71 | try {
72 | shell.close();
73 | } catch (IOException e) {
74 |
75 | }
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/Mount.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools)
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package org.sufficientlysecure.rootcommands;
19 |
20 | import java.io.File;
21 | import java.util.Arrays;
22 | import java.util.HashSet;
23 | import java.util.Set;
24 |
25 | public class Mount {
26 | protected final File mDevice;
27 | protected final File mMountPoint;
28 | protected final String mType;
29 | protected final Set mFlags;
30 |
31 | Mount(File device, File path, String type, String flagsStr) {
32 | mDevice = device;
33 | mMountPoint = path;
34 | mType = type;
35 | mFlags = new HashSet(Arrays.asList(flagsStr.split(",")));
36 | }
37 |
38 | public File getDevice() {
39 | return mDevice;
40 | }
41 |
42 | public File getMountPoint() {
43 | return mMountPoint;
44 | }
45 |
46 | public String getType() {
47 | return mType;
48 | }
49 |
50 | public Set getFlags() {
51 | return mFlags;
52 | }
53 |
54 | @Override
55 | public String toString() {
56 | return String.format("%s on %s type %s %s", mDevice, mMountPoint, mType, mFlags);
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/Remounter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools)
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package org.sufficientlysecure.rootcommands;
19 |
20 | import java.io.File;
21 | import java.io.FileReader;
22 | import java.io.IOException;
23 | import java.io.LineNumberReader;
24 | import java.util.ArrayList;
25 | import java.util.Locale;
26 |
27 | import org.sufficientlysecure.rootcommands.command.SimpleCommand;
28 | import org.sufficientlysecure.rootcommands.util.Log;
29 |
30 | //no modifier, this means it is package-private. Only our internal classes can use this.
31 | class Remounter {
32 |
33 | private Shell shell;
34 |
35 | public Remounter(Shell shell) {
36 | super();
37 | this.shell = shell;
38 | }
39 |
40 | /**
41 | * This will take a path, which can contain the file name as well, and attempt to remount the
42 | * underlying partition.
43 | *
44 | * For example, passing in the following string:
45 | * "/system/bin/some/directory/that/really/would/never/exist" will result in /system ultimately
46 | * being remounted. However, keep in mind that the longer the path you supply, the more work
47 | * this has to do, and the slower it will run.
48 | *
49 | * @param file
50 | * file path
51 | * @param mountType
52 | * mount type: pass in RO (Read only) or RW (Read Write)
53 | * @return a boolean
which indicates whether or not the partition has been
54 | * remounted as specified.
55 | */
56 | protected boolean remount(String file, String mountType) {
57 |
58 | // if the path has a trailing slash get rid of it.
59 | if (file.endsWith("/") && !file.equals("/")) {
60 | file = file.substring(0, file.lastIndexOf("/"));
61 | }
62 | // Make sure that what we are trying to remount is in the mount list.
63 | boolean foundMount = false;
64 | while (!foundMount) {
65 | try {
66 | for (Mount mount : getMounts()) {
67 | Log.d(RootCommands.TAG, mount.getMountPoint().toString());
68 |
69 | if (file.equals(mount.getMountPoint().toString())) {
70 | foundMount = true;
71 | break;
72 | }
73 | }
74 | } catch (Exception e) {
75 | Log.e(RootCommands.TAG, "Exception", e);
76 | return false;
77 | }
78 | if (!foundMount) {
79 | try {
80 | file = (new File(file).getParent()).toString();
81 | } catch (Exception e) {
82 | Log.e(RootCommands.TAG, "Exception", e);
83 | return false;
84 | }
85 | }
86 | }
87 | Mount mountPoint = findMountPointRecursive(file);
88 |
89 | Log.d(RootCommands.TAG, "Remounting " + mountPoint.getMountPoint().getAbsolutePath()
90 | + " as " + mountType.toLowerCase(Locale.US));
91 | final boolean isMountMode = mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US));
92 |
93 | if (!isMountMode) {
94 | // grab an instance of the internal class
95 | try {
96 | SimpleCommand command = new SimpleCommand("busybox mount -o remount,"
97 | + mountType.toLowerCase(Locale.US) + " " + mountPoint.getDevice().getAbsolutePath()
98 | + " " + mountPoint.getMountPoint().getAbsolutePath(),
99 | "toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " "
100 | + mountPoint.getDevice().getAbsolutePath() + " "
101 | + mountPoint.getMountPoint().getAbsolutePath(), "mount -o remount,"
102 | + mountType.toLowerCase(Locale.US) + " "
103 | + mountPoint.getDevice().getAbsolutePath() + " "
104 | + mountPoint.getMountPoint().getAbsolutePath(),
105 | "/system/bin/toolbox mount -o remount," + mountType.toLowerCase(Locale.US) + " "
106 | + mountPoint.getDevice().getAbsolutePath() + " "
107 | + mountPoint.getMountPoint().getAbsolutePath());
108 |
109 | // execute on shell
110 | shell.add(command).waitForFinish();
111 |
112 | } catch (Exception e) {
113 | }
114 |
115 | mountPoint = findMountPointRecursive(file);
116 | }
117 |
118 | if (mountPoint != null) {
119 | Log.d(RootCommands.TAG, mountPoint.getFlags() + " AND " + mountType.toLowerCase(Locale.US));
120 | if (mountPoint.getFlags().contains(mountType.toLowerCase(Locale.US))) {
121 | Log.d(RootCommands.TAG, mountPoint.getFlags().toString());
122 | return true;
123 | } else {
124 | Log.d(RootCommands.TAG, mountPoint.getFlags().toString());
125 | }
126 | } else {
127 | Log.d(RootCommands.TAG, "mountPoint is null");
128 | }
129 | return false;
130 | }
131 |
132 | private Mount findMountPointRecursive(String file) {
133 | try {
134 | ArrayList mounts = getMounts();
135 | for (File path = new File(file); path != null;) {
136 | for (Mount mount : mounts) {
137 | if (mount.getMountPoint().equals(path)) {
138 | return mount;
139 | }
140 | }
141 | }
142 | return null;
143 | } catch (IOException e) {
144 | throw new RuntimeException(e);
145 | } catch (Exception e) {
146 | Log.e(RootCommands.TAG, "Exception", e);
147 | }
148 | return null;
149 | }
150 |
151 | /**
152 | * This will return an ArrayList of the class Mount. The class mount contains the following
153 | * property's: device mountPoint type flags
154 | *
155 | * These will provide you with any information you need to work with the mount points.
156 | *
157 | * @return ArrayList
an ArrayList of the class Mount.
158 | * @throws Exception
159 | * if we cannot return the mount points.
160 | */
161 | protected static ArrayList getMounts() throws Exception {
162 |
163 | final String tempFile = "/data/local/RootToolsMounts";
164 |
165 | // copy /proc/mounts to tempfile. Directly reading it does not work on 4.3
166 | Shell shell = Shell.startRootShell();
167 | Toolbox tb = new Toolbox(shell);
168 | tb.copyFile("/proc/mounts", tempFile, false, false);
169 | tb.setFilePermissions(tempFile, "777");
170 | shell.close();
171 |
172 | LineNumberReader lnr = null;
173 | lnr = new LineNumberReader(new FileReader(tempFile));
174 | String line;
175 | ArrayList mounts = new ArrayList();
176 | while ((line = lnr.readLine()) != null) {
177 |
178 | Log.d(RootCommands.TAG, line);
179 |
180 | String[] fields = line.split(" ");
181 | mounts.add(new Mount(new File(fields[0]), // device
182 | new File(fields[1]), // mountPoint
183 | fields[2], // fstype
184 | fields[3] // flags
185 | ));
186 | }
187 | lnr.close();
188 |
189 | return mounts;
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/RootCommands.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands;
18 |
19 | import org.sufficientlysecure.rootcommands.util.Log;
20 |
21 | public class RootCommands {
22 | public static boolean DEBUG = false;
23 | public static int DEFAULT_TIMEOUT = 10000;
24 |
25 | public static final String TAG = "RootCommands";
26 |
27 | /**
28 | * General method to check if user has su binary and accepts root access for this program!
29 | *
30 | * @return true if everything worked
31 | */
32 | public static boolean rootAccessGiven() {
33 | boolean rootAccess = false;
34 |
35 | try {
36 | Shell rootShell = Shell.startRootShell();
37 |
38 | Toolbox tb = new Toolbox(rootShell);
39 | if (tb.isRootAccessGiven()) {
40 | rootAccess = true;
41 | }
42 |
43 | rootShell.close();
44 | } catch (Exception e) {
45 | Log.e(TAG, "Problem while checking for root access!", e);
46 | }
47 |
48 | return rootAccess;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/Shell.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks, Jeremy Lakeman (RootTools)
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package org.sufficientlysecure.rootcommands;
19 |
20 | import java.io.BufferedReader;
21 | import java.io.Closeable;
22 | import java.io.DataOutputStream;
23 | import java.io.IOException;
24 | import java.io.InputStreamReader;
25 | import java.util.ArrayList;
26 | import java.util.List;
27 |
28 | import org.sufficientlysecure.rootcommands.command.Command;
29 | import org.sufficientlysecure.rootcommands.util.Log;
30 | import org.sufficientlysecure.rootcommands.util.RootAccessDeniedException;
31 | import org.sufficientlysecure.rootcommands.util.Utils;
32 |
33 | public class Shell implements Closeable {
34 | private final Process shellProcess;
35 | private final BufferedReader stdOutErr;
36 | private final DataOutputStream outputStream;
37 | private final List commands = new ArrayList();
38 | private boolean close = false;
39 |
40 | private static final String LD_LIBRARY_PATH = System.getenv("LD_LIBRARY_PATH");
41 | private static final String token = "F*D^W@#FGF";
42 |
43 | /**
44 | * Start root shell
45 | *
46 | * @param customEnv
47 | * @param baseDirectory
48 | * @return
49 | * @throws IOException
50 | */
51 | public static Shell startRootShell(ArrayList customEnv, String baseDirectory)
52 | throws IOException, RootAccessDeniedException {
53 | Log.d(RootCommands.TAG, "Starting Root Shell!");
54 |
55 | // On some versions of Android (ICS) LD_LIBRARY_PATH is unset when using su
56 | // We need to pass LD_LIBRARY_PATH over su for some commands to work correctly.
57 | if (customEnv == null) {
58 | customEnv = new ArrayList();
59 | }
60 | customEnv.add("LD_LIBRARY_PATH=" + LD_LIBRARY_PATH);
61 |
62 | Shell shell = new Shell(Utils.getSuPath(), customEnv, baseDirectory);
63 |
64 | return shell;
65 | }
66 |
67 | /**
68 | * Start root shell without custom environment and base directory
69 | *
70 | * @return
71 | * @throws IOException
72 | */
73 | public static Shell startRootShell() throws IOException, RootAccessDeniedException {
74 | return startRootShell(null, null);
75 | }
76 |
77 | /**
78 | * Start default sh shell
79 | *
80 | * @param customEnv
81 | * @param baseDirectory
82 | * @return
83 | * @throws IOException
84 | */
85 | public static Shell startShell(ArrayList customEnv, String baseDirectory)
86 | throws IOException {
87 | Log.d(RootCommands.TAG, "Starting Shell!");
88 | Shell shell = new Shell("sh", customEnv, baseDirectory);
89 | return shell;
90 | }
91 |
92 | /**
93 | * Start default sh shell without custom environment and base directory
94 | *
95 | * @return
96 | * @throws IOException
97 | */
98 | public static Shell startShell() throws IOException {
99 | return startShell(null, null);
100 | }
101 |
102 | /**
103 | * Start custom shell defined by shellPath
104 | *
105 | * @param shellPath
106 | * @param customEnv
107 | * @param baseDirectory
108 | * @return
109 | * @throws IOException
110 | */
111 | public static Shell startCustomShell(String shellPath, ArrayList customEnv,
112 | String baseDirectory) throws IOException {
113 | Log.d(RootCommands.TAG, "Starting Custom Shell!");
114 | Shell shell = new Shell(shellPath, customEnv, baseDirectory);
115 |
116 | return shell;
117 | }
118 |
119 | /**
120 | * Start custom shell without custom environment and base directory
121 | *
122 | * @param shellPath
123 | * @return
124 | * @throws IOException
125 | */
126 | public static Shell startCustomShell(String shellPath) throws IOException {
127 | return startCustomShell(shellPath, null, null);
128 | }
129 |
130 | private Shell(String shell, ArrayList customEnv, String baseDirectory)
131 | throws IOException, RootAccessDeniedException {
132 | Log.d(RootCommands.TAG, "Starting shell: " + shell);
133 |
134 | // start shell process!
135 | shellProcess = Utils.runWithEnv(shell, customEnv, baseDirectory);
136 |
137 | // StdErr is redirected to StdOut, defined in Command.getCommand()
138 | stdOutErr = new BufferedReader(new InputStreamReader(shellProcess.getInputStream()));
139 | outputStream = new DataOutputStream(shellProcess.getOutputStream());
140 |
141 | outputStream.write("echo Started\n".getBytes());
142 | outputStream.flush();
143 |
144 | while (true) {
145 | String line = stdOutErr.readLine();
146 | if (line == null)
147 | throw new RootAccessDeniedException(
148 | "stdout line is null! Access was denied or this executeable is not a shell!");
149 | if ("".equals(line))
150 | continue;
151 | if ("Started".equals(line))
152 | break;
153 |
154 | destroyShellProcess();
155 | throw new IOException("Unable to start shell, unexpected output \"" + line + "\"");
156 | }
157 |
158 | new Thread(inputRunnable, "Shell Input").start();
159 | new Thread(outputRunnable, "Shell Output").start();
160 | }
161 |
162 | private Runnable inputRunnable = new Runnable() {
163 | public void run() {
164 | try {
165 | writeCommands();
166 | } catch (IOException e) {
167 | Log.e(RootCommands.TAG, "IO Exception", e);
168 | }
169 | }
170 | };
171 |
172 | private Runnable outputRunnable = new Runnable() {
173 | public void run() {
174 | try {
175 | readOutput();
176 | } catch (IOException e) {
177 | Log.e(RootCommands.TAG, "IOException", e);
178 | } catch (InterruptedException e) {
179 | Log.e(RootCommands.TAG, "InterruptedException", e);
180 | }
181 | }
182 | };
183 |
184 | /**
185 | * Destroy shell process considering that the process could already be terminated
186 | */
187 | private void destroyShellProcess() {
188 | try {
189 | // Yes, this really is the way to check if the process is
190 | // still running.
191 | shellProcess.exitValue();
192 | } catch (IllegalThreadStateException e) {
193 | // Only call destroy() if the process is still running;
194 | // Calling it for a terminated process will not crash, but
195 | // (starting with at least ICS/4.0) spam the log with INFO
196 | // messages ala "Failed to destroy process" and "kill
197 | // failed: ESRCH (No such process)".
198 | shellProcess.destroy();
199 | }
200 |
201 | Log.d(RootCommands.TAG, "Shell destroyed");
202 | }
203 |
204 | /**
205 | * Writes queued commands one after another into the opened shell. After an execution a token is
206 | * written to seperate command output on read
207 | *
208 | * @throws IOException
209 | */
210 | private void writeCommands() throws IOException {
211 | try {
212 | int commandIndex = 0;
213 | while (true) {
214 | DataOutputStream out;
215 | synchronized (commands) {
216 | while (!close && commandIndex >= commands.size()) {
217 | commands.wait();
218 | }
219 | out = this.outputStream;
220 | }
221 | if (commandIndex < commands.size()) {
222 | Command next = commands.get(commandIndex);
223 | next.writeCommand(out);
224 | String line = "\necho " + token + " " + commandIndex + " $?\n";
225 | out.write(line.getBytes());
226 | out.flush();
227 | commandIndex++;
228 | } else if (close) {
229 | out.write("\nexit 0\n".getBytes());
230 | out.flush();
231 | Log.d(RootCommands.TAG, "Closing shell");
232 | shellProcess.waitFor();
233 | out.close();
234 | return;
235 | } else {
236 | Thread.sleep(50);
237 | }
238 | }
239 | } catch (InterruptedException e) {
240 | Log.e(RootCommands.TAG, "interrupted while writing command", e);
241 | }
242 | }
243 |
244 | /**
245 | * Reads output line by line, seperated by token written after every command
246 | *
247 | * @throws IOException
248 | * @throws InterruptedException
249 | */
250 | private void readOutput() throws IOException, InterruptedException {
251 | Command command = null;
252 |
253 | // index of current command
254 | int commandIndex = 0;
255 | while (true) {
256 | String lineStdOut = stdOutErr.readLine();
257 |
258 | // terminate on EOF
259 | if (lineStdOut == null)
260 | break;
261 |
262 | if (command == null) {
263 |
264 | // break on close after last command
265 | if (commandIndex >= commands.size()) {
266 | if (close)
267 | break;
268 | continue;
269 | }
270 |
271 | // get current command
272 | command = commands.get(commandIndex);
273 | }
274 |
275 | int pos = lineStdOut.indexOf(token);
276 | if (pos > 0) {
277 | command.processOutput(lineStdOut.substring(0, pos));
278 | }
279 | if (pos >= 0) {
280 | lineStdOut = lineStdOut.substring(pos);
281 | String fields[] = lineStdOut.split(" ");
282 | int id = Integer.parseInt(fields[1]);
283 | if (id == commandIndex) {
284 | command.setExitCode(Integer.parseInt(fields[2]));
285 |
286 | // go to next command
287 | commandIndex++;
288 | command = null;
289 | continue;
290 | }
291 | }
292 | command.processOutput(lineStdOut);
293 | }
294 | Log.d(RootCommands.TAG, "Read all output");
295 | shellProcess.waitFor();
296 | stdOutErr.close();
297 | destroyShellProcess();
298 |
299 | while (commandIndex < commands.size()) {
300 | if (command == null) {
301 | command = commands.get(commandIndex);
302 | }
303 | command.terminated("Unexpected Termination!");
304 | commandIndex++;
305 | command = null;
306 | }
307 | }
308 |
309 | /**
310 | * Add command to shell queue
311 | *
312 | * @param command
313 | * @return
314 | * @throws IOException
315 | */
316 | public Command add(Command command) throws IOException {
317 | if (close)
318 | throw new IOException("Unable to add commands to a closed shell");
319 | synchronized (commands) {
320 | commands.add(command);
321 | // set shell on the command object, to know where the command is running on
322 | command.addedToShell(this, (commands.size() - 1));
323 | commands.notifyAll();
324 | }
325 |
326 | return command;
327 | }
328 |
329 | /**
330 | * Close shell
331 | *
332 | * @throws IOException
333 | */
334 | public void close() throws IOException {
335 | synchronized (commands) {
336 | this.close = true;
337 | commands.notifyAll();
338 | }
339 | }
340 |
341 | /**
342 | * Returns number of queued commands
343 | *
344 | * @return
345 | */
346 | public int getCommandsSize() {
347 | return commands.size();
348 | }
349 |
350 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/SystemCommands.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands;
18 |
19 | import android.annotation.TargetApi;
20 | import android.content.ContentResolver;
21 | import android.content.Context;
22 | import android.location.LocationManager;
23 | import android.os.PowerManager;
24 | import android.provider.Settings;
25 |
26 | /**
27 | * This methods work when the apk is installed as a system app (under /system/app)
28 | */
29 | public class SystemCommands {
30 | Context context;
31 |
32 | public SystemCommands(Context context) {
33 | super();
34 | this.context = context;
35 | }
36 |
37 | /**
38 | * Get GPS status
39 | *
40 | * @return
41 | */
42 | public boolean getGPS() {
43 | return ((LocationManager) context.getSystemService(Context.LOCATION_SERVICE))
44 | .isProviderEnabled(LocationManager.GPS_PROVIDER);
45 | }
46 |
47 | /**
48 | * Enable/Disable GPS
49 | *
50 | * @param value
51 | */
52 | @TargetApi(8)
53 | public void setGPS(boolean value) {
54 | ContentResolver localContentResolver = context.getContentResolver();
55 | Settings.Secure.setLocationProviderEnabled(localContentResolver,
56 | LocationManager.GPS_PROVIDER, value);
57 | }
58 |
59 | /**
60 | * TODO: Not ready yet
61 | */
62 | @TargetApi(8)
63 | public void reboot() {
64 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
65 | pm.reboot("recovery");
66 | pm.reboot(null);
67 |
68 | // not working:
69 | // reboot(null);
70 | }
71 |
72 | /**
73 | * Reboot the device immediately, passing 'reason' (may be null) to the underlying __reboot
74 | * system call. Should not return.
75 | *
76 | * Taken from com.android.server.PowerManagerService.reboot
77 | */
78 | // public void reboot(String reason) {
79 | //
80 | // // final String finalReason = reason;
81 | // Runnable runnable = new Runnable() {
82 | // public void run() {
83 | // synchronized (this) {
84 | // // ShutdownThread.reboot(mContext, finalReason, false);
85 | // try {
86 | // Class> clazz = Class.forName("com.android.internal.app.ShutdownThread");
87 | //
88 | // // if (mReboot) {
89 | // Method method = clazz.getMethod("reboot", Context.class, String.class,
90 | // Boolean.TYPE);
91 | // method.invoke(null, context, null, false);
92 | //
93 | // // if (mReboot) {
94 | // // Method method = clazz.getMethod("reboot", Context.class, String.class,
95 | // // Boolean.TYPE);
96 | // // method.invoke(null, mContext, mReason, mConfirm);
97 | // // } else {
98 | // // Method method = clazz.getMethod("shutdown", Context.class, Boolean.TYPE);
99 | // // method.invoke(null, mContext, mConfirm);
100 | // // }
101 | // } catch (Exception e) {
102 | // e.printStackTrace();
103 | // }
104 | // }
105 | //
106 | // }
107 | // };
108 | // // ShutdownThread must run on a looper capable of displaying the UI.
109 | // mHandler.post(runnable);
110 | //
111 | // // PowerManager.reboot() is documented not to return so just wait for the inevitable.
112 | // // synchronized (runnable) {
113 | // // while (true) {
114 | // // try {
115 | // // runnable.wait();
116 | // // } catch (InterruptedException e) {
117 | // // }
118 | // // }
119 | // // }
120 | // }
121 |
122 | }
123 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/command/Command.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks, Jeremy Lakeman (RootTools)
4 | *
5 | * Licensed under the Apache License, Version 2.0 (the "License");
6 | * you may not use this file except in compliance with the License.
7 | * You may obtain a copy of the License at
8 | *
9 | * http://www.apache.org/licenses/LICENSE-2.0
10 | *
11 | * Unless required by applicable law or agreed to in writing, software
12 | * distributed under the License is distributed on an "AS IS" BASIS,
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | * See the License for the specific language governing permissions and
15 | * limitations under the License.
16 | */
17 |
18 | package org.sufficientlysecure.rootcommands.command;
19 |
20 | import java.io.IOException;
21 | import java.io.OutputStream;
22 | import java.util.concurrent.TimeoutException;
23 |
24 | import org.sufficientlysecure.rootcommands.RootCommands;
25 | import org.sufficientlysecure.rootcommands.Shell;
26 | import org.sufficientlysecure.rootcommands.util.BrokenBusyboxException;
27 | import org.sufficientlysecure.rootcommands.util.Log;
28 |
29 | public abstract class Command {
30 | final String command[];
31 | boolean finished = false;
32 | boolean brokenBusyboxDetected = false;
33 | int exitCode;
34 | int id;
35 | int timeout = RootCommands.DEFAULT_TIMEOUT;
36 | Shell shell = null;
37 |
38 | public Command(String... command) {
39 | this.command = command;
40 | }
41 |
42 | public Command(int timeout, String... command) {
43 | this.command = command;
44 | this.timeout = timeout;
45 | }
46 |
47 | /**
48 | * This is called from Shell after adding it
49 | *
50 | * @param shell
51 | * @param id
52 | */
53 | public void addedToShell(Shell shell, int id) {
54 | this.shell = shell;
55 | this.id = id;
56 | }
57 |
58 | /**
59 | * Gets command string executed on the shell
60 | *
61 | * @return
62 | */
63 | public String getCommand() {
64 | StringBuilder sb = new StringBuilder();
65 | for (int i = 0; i < command.length; i++) {
66 | // redirect stderr to stdout
67 | sb.append(command[i] + " 2>&1");
68 | sb.append('\n');
69 | }
70 | Log.d(RootCommands.TAG, "Sending command(s): " + sb.toString());
71 | return sb.toString();
72 | }
73 |
74 | public void writeCommand(OutputStream out) throws IOException {
75 | out.write(getCommand().getBytes());
76 | }
77 |
78 | public void processOutput(String line) {
79 | Log.d(RootCommands.TAG, "ID: " + id + ", Output: " + line);
80 |
81 | /*
82 | * Try to detect broken toolbox/busybox binaries (see
83 | * https://code.google.com/p/busybox-android/issues/detail?id=1)
84 | *
85 | * It is giving "Value too large for defined data type" on certain file operations (e.g. ls
86 | * and chown) in certain directories (e.g. /data/data)
87 | */
88 | if (line.contains("Value too large for defined data type")) {
89 | Log.e(RootCommands.TAG, "Busybox is broken with high probability due to line: " + line);
90 | brokenBusyboxDetected = true;
91 | }
92 |
93 | // now execute specific output parsing
94 | output(id, line);
95 | }
96 |
97 | public abstract void output(int id, String line);
98 |
99 | public void processAfterExecution(int exitCode) {
100 | Log.d(RootCommands.TAG, "ID: " + id + ", ExitCode: " + exitCode);
101 |
102 | afterExecution(id, exitCode);
103 | }
104 |
105 | public abstract void afterExecution(int id, int exitCode);
106 |
107 | public void commandFinished(int id) {
108 | Log.d(RootCommands.TAG, "Command " + id + " finished.");
109 | }
110 |
111 | public void setExitCode(int code) {
112 | synchronized (this) {
113 | exitCode = code;
114 | finished = true;
115 | commandFinished(id);
116 | this.notifyAll();
117 | }
118 | }
119 |
120 | /**
121 | * Close the shell
122 | *
123 | * @param reason
124 | */
125 | public void terminate(String reason) {
126 | try {
127 | shell.close();
128 | Log.d(RootCommands.TAG, "Terminating the shell.");
129 | terminated(reason);
130 | } catch (IOException e) {
131 | }
132 | }
133 |
134 | public void terminated(String reason) {
135 | setExitCode(-1);
136 | Log.d(RootCommands.TAG, "Command " + id + " did not finish, because of " + reason);
137 | }
138 |
139 | /**
140 | * Waits for this command to finish and forwards exitCode into afterExecution method
141 | *
142 | * @param timeout
143 | * @throws TimeoutException
144 | * @throws BrokenBusyboxException
145 | */
146 | public void waitForFinish() throws TimeoutException, BrokenBusyboxException {
147 | synchronized (this) {
148 | while (!finished) {
149 | try {
150 | this.wait(timeout);
151 | } catch (InterruptedException e) {
152 | Log.e(RootCommands.TAG, "InterruptedException in waitForFinish()", e);
153 | }
154 |
155 | if (!finished) {
156 | finished = true;
157 | terminate("Timeout");
158 | throw new TimeoutException("Timeout has occurred.");
159 | }
160 | }
161 |
162 | if (brokenBusyboxDetected) {
163 | throw new BrokenBusyboxException();
164 | }
165 |
166 | processAfterExecution(exitCode);
167 | }
168 | }
169 |
170 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/command/ExecutableCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands.command;
18 |
19 | import java.io.File;
20 |
21 | import android.annotation.SuppressLint;
22 | import android.content.Context;
23 | import android.os.Build;
24 |
25 | public abstract class ExecutableCommand extends Command {
26 | public static final String EXECUTABLE_PREFIX = "lib";
27 | public static final String EXECUTABLE_SUFFIX = "_exec.so";
28 |
29 | /**
30 | * This class provides a way to use your own binaries!
31 | *
32 | * Include your own executables, renamed from * to lib*_exec.so, in your libs folder under the
33 | * architecture directories. Now they will be deployed by Android the same way libraries are
34 | * deployed!
35 | *
36 | * See README for more information how to use your own executables!
37 | *
38 | * @param context
39 | * @param executableName
40 | * @param parameters
41 | */
42 | public ExecutableCommand(Context context, String executableName, String parameters) {
43 | super(getLibDirectory(context) + File.separator + EXECUTABLE_PREFIX + executableName
44 | + EXECUTABLE_SUFFIX + " " + parameters);
45 | }
46 |
47 | /**
48 | * Get full path to lib directory of app
49 | *
50 | * @return dir as String
51 | */
52 | @SuppressLint("NewApi")
53 | private static String getLibDirectory(Context context) {
54 | if (Build.VERSION.SDK_INT >= 9) {
55 | return context.getApplicationInfo().nativeLibraryDir;
56 | } else {
57 | return context.getApplicationInfo().dataDir + File.separator + "lib";
58 | }
59 | }
60 |
61 | public abstract void output(int id, String line);
62 |
63 | public abstract void afterExecution(int id, int exitCode);
64 |
65 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands.command;
18 |
19 | public class SimpleCommand extends Command {
20 | private StringBuilder sb = new StringBuilder();
21 |
22 | public SimpleCommand(String... command) {
23 | super(command);
24 | }
25 |
26 | @Override
27 | public void output(int id, String line) {
28 | sb.append(line).append('\n');
29 | }
30 |
31 | @Override
32 | public void afterExecution(int id, int exitCode) {
33 | }
34 |
35 | public String getOutput() {
36 | return sb.toString();
37 | }
38 |
39 | public int getExitCode() {
40 | return exitCode;
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/command/SimpleExecutableCommand.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands.command;
18 |
19 | import android.content.Context;
20 |
21 | public class SimpleExecutableCommand extends ExecutableCommand {
22 | private StringBuilder sb = new StringBuilder();
23 |
24 | public SimpleExecutableCommand(Context context, String executableName, String parameters) {
25 | super(context, executableName, parameters);
26 | }
27 |
28 | @Override
29 | public void output(int id, String line) {
30 | sb.append(line).append('\n');
31 | }
32 |
33 | @Override
34 | public void afterExecution(int id, int exitCode) {
35 | }
36 |
37 | public String getOutput() {
38 | return sb.toString();
39 | }
40 |
41 | public int getExitCode() {
42 | return exitCode;
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/util/BrokenBusyboxException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands.util;
18 |
19 | import java.io.IOException;
20 |
21 | public class BrokenBusyboxException extends IOException {
22 | private static final long serialVersionUID = 8337358201589488409L;
23 |
24 | public BrokenBusyboxException() {
25 | super();
26 | }
27 |
28 | public BrokenBusyboxException(String detailMessage) {
29 | super(detailMessage);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/util/Log.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands.util;
18 |
19 | import org.sufficientlysecure.rootcommands.RootCommands;
20 |
21 | /**
22 | * Wraps Android Logging to enable or disable debug output using Constants
23 | *
24 | */
25 | public final class Log {
26 |
27 | public static void v(String tag, String msg) {
28 | if (RootCommands.DEBUG) {
29 | android.util.Log.v(tag, msg);
30 | }
31 | }
32 |
33 | public static void v(String tag, String msg, Throwable tr) {
34 | if (RootCommands.DEBUG) {
35 | android.util.Log.v(tag, msg, tr);
36 | }
37 | }
38 |
39 | public static void d(String tag, String msg) {
40 | if (RootCommands.DEBUG) {
41 | android.util.Log.d(tag, msg);
42 | }
43 | }
44 |
45 | public static void d(String tag, String msg, Throwable tr) {
46 | if (RootCommands.DEBUG) {
47 | android.util.Log.d(tag, msg, tr);
48 | }
49 | }
50 |
51 | public static void i(String tag, String msg) {
52 | if (RootCommands.DEBUG) {
53 | android.util.Log.i(tag, msg);
54 | }
55 | }
56 |
57 | public static void i(String tag, String msg, Throwable tr) {
58 | if (RootCommands.DEBUG) {
59 | android.util.Log.i(tag, msg, tr);
60 | }
61 | }
62 |
63 | public static void w(String tag, String msg) {
64 | android.util.Log.w(tag, msg);
65 | }
66 |
67 | public static void w(String tag, String msg, Throwable tr) {
68 | android.util.Log.w(tag, msg, tr);
69 | }
70 |
71 | public static void w(String tag, Throwable tr) {
72 | android.util.Log.w(tag, tr);
73 | }
74 |
75 | public static void e(String tag, String msg) {
76 | android.util.Log.e(tag, msg);
77 | }
78 |
79 | public static void e(String tag, String msg, Throwable tr) {
80 | android.util.Log.e(tag, msg, tr);
81 | }
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/util/RootAccessDeniedException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands.util;
18 |
19 | import java.io.IOException;
20 |
21 | public class RootAccessDeniedException extends IOException {
22 | private static final long serialVersionUID = 9088998884166225540L;
23 |
24 | public RootAccessDeniedException() {
25 | super();
26 | }
27 |
28 | public RootAccessDeniedException(String detailMessage) {
29 | super(detailMessage);
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/util/UnsupportedArchitectureException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package org.sufficientlysecure.rootcommands.util;
18 |
19 | public class UnsupportedArchitectureException extends Exception {
20 | private static final long serialVersionUID = 7826528799780001655L;
21 |
22 | public UnsupportedArchitectureException() {
23 | super();
24 | }
25 |
26 | public UnsupportedArchitectureException(String detailMessage) {
27 | super(detailMessage);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/org/sufficientlysecure/rootcommands/util/Utils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2012 Dominik Schürmann
3 | * Copyright (c) 2012 Michael Elsdörfer (Android Autostarts)
4 | * Copyright (c) 2012 Stephen Erickson, Chris Ravenscroft, Adam Shanks (RootTools)
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 | * http://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 | package org.sufficientlysecure.rootcommands.util;
20 |
21 | import java.io.File;
22 | import java.io.IOException;
23 | import java.util.ArrayList;
24 | import java.util.Map;
25 |
26 | import org.sufficientlysecure.rootcommands.RootCommands;
27 |
28 | public class Utils {
29 | /*
30 | * The emulator and ADP1 device both have a su binary in /system/xbin/su, but it doesn't allow
31 | * apps to use it (su app_29 $ su su: uid 10029 not allowed to su).
32 | *
33 | * Cyanogen used to have su in /system/bin/su, in newer versions it's a symlink to
34 | * /system/xbin/su.
35 | *
36 | * The Archos tablet has it in /data/bin/su, since they don't have write access to /system yet.
37 | */
38 | static final String[] BinaryPlaces = { "/data/bin/", "/system/bin/", "/system/xbin/", "/sbin/",
39 | "/data/local/xbin/", "/data/local/bin/", "/system/sd/xbin/", "/system/bin/failsafe/",
40 | "/data/local/" };
41 |
42 | /**
43 | * Determine the path of the su executable.
44 | *
45 | * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was
46 | * agreed by Michael Elsdörfer
47 | */
48 | public static String getSuPath() {
49 | for (String p : BinaryPlaces) {
50 | File su = new File(p + "su");
51 | if (su.exists()) {
52 | Log.d(RootCommands.TAG, "su found at: " + p);
53 | return su.getAbsolutePath();
54 | } else {
55 | Log.v(RootCommands.TAG, "No su in: " + p);
56 | }
57 | }
58 | Log.d(RootCommands.TAG, "No su found in a well-known location, " + "will just use \"su\".");
59 | return "su";
60 | }
61 |
62 | /**
63 | * This code is adapted from java.lang.ProcessBuilder.start().
64 | *
65 | * The problem is that Android doesn't allow us to modify the map returned by
66 | * ProcessBuilder.environment(), even though the docstring indicates that it should. This is
67 | * because it simply returns the SystemEnvironment object that System.getenv() gives us. The
68 | * relevant portion in the source code is marked as "// android changed", so presumably it's not
69 | * the case in the original version of the Apache Harmony project.
70 | *
71 | * Note that simply passing the environment variables we want to Process.exec won't be good
72 | * enough, since that would override the environment we inherited completely.
73 | *
74 | * We needed to be able to set a CLASSPATH environment variable for our new process in order to
75 | * use the "app_process" command directly. Note: "app_process" takes arguments passed on to the
76 | * Dalvik VM as well; this might be an alternative way to set the class path.
77 | *
78 | * Code from https://github.com/miracle2k/android-autostarts, use under Apache License was
79 | * agreed by Michael Elsdörfer
80 | */
81 | public static Process runWithEnv(String command, ArrayList customAddedEnv,
82 | String baseDirectory) throws IOException {
83 |
84 | Map environment = System.getenv();
85 | String[] envArray = new String[environment.size()
86 | + (customAddedEnv != null ? customAddedEnv.size() : 0)];
87 | int i = 0;
88 | for (Map.Entry entry : environment.entrySet()) {
89 | envArray[i++] = entry.getKey() + "=" + entry.getValue();
90 | }
91 | if (customAddedEnv != null) {
92 | for (String entry : customAddedEnv) {
93 | envArray[i++] = entry;
94 | }
95 | }
96 |
97 | Process process;
98 | if (baseDirectory == null) {
99 | process = Runtime.getRuntime().exec(command, envArray, null);
100 | } else {
101 | process = Runtime.getRuntime().exec(command, envArray, new File(baseDirectory));
102 | }
103 | return process;
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/app/src/main/res/create_ico.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | convert $1 -resize 144x drawable-xxhdpi/$(basename $1)
3 | convert $1 -resize 96x drawable-xhdpi/$(basename $1)
4 | convert $1 -resize 72x drawable-hdpi/$(basename $1)
5 | convert $1 -resize 48x drawable-mdpi/$(basename $1)
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/android_unknown_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-hdpi/android_unknown_app.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_action_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-hdpi/ic_action_settings.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-hdpi/v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/android_unknown_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-mdpi/android_unknown_app.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_action_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-mdpi/ic_action_settings.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-mdpi/v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/android_unknown_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-xhdpi/android_unknown_app.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_action_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-xhdpi/ic_action_settings.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-xhdpi/v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/android_unknown_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-xxhdpi/android_unknown_app.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_action_settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-xxhdpi/ic_action_settings.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/v2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-xxhdpi/v2.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/android_unknown_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/app/src/main/res/drawable-xxxhdpi/android_unknown_app.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/about.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
13 |
14 |
21 |
22 |
27 |
28 |
35 |
36 |
43 |
44 |
51 |
57 |
58 |
65 |
70 |
71 |
78 |
84 |
85 |
92 |
97 |
98 |
105 |
110 |
111 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_tabbed_main.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_wizard.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/advanced_connection.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
12 |
13 |
19 |
20 |
27 |
28 |
35 |
36 |
37 |
38 |
42 |
43 |
44 |
45 |
46 |
52 |
53 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_row.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_tabbed_apps.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
16 |
17 |
26 |
27 |
36 |
37 |
42 |
43 |
47 |
48 |
49 |
50 |
62 |
63 |
70 |
71 |
75 |
76 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_tabbed_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
17 |
18 |
27 |
28 |
37 |
38 |
39 |
44 |
45 |
50 |
51 |
59 |
60 |
68 |
69 |
77 |
78 |
81 |
85 |
90 |
91 |
96 |
97 |
103 |
104 |
110 |
111 |
117 |
118 |
124 |
125 |
131 |
132 |
133 |
134 |
139 |
140 |
148 |
149 |
157 |
158 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_wizard.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
12 |
13 |
18 |
19 |
26 |
27 |
32 |
33 |
34 |
35 |
39 |
40 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/res/raw/activate_portal.sh:
--------------------------------------------------------------------------------
1 | #!/system/bin/sh
2 |
3 | settings put global captive_portal_server clients3.google.com
4 | settings put global captive_portal_detection_enabled 1
--------------------------------------------------------------------------------
/app/src/main/res/raw/deactivate_portal.sh:
--------------------------------------------------------------------------------
1 | #!/system/bin/sh
2 |
3 | settings put global captive_portal_server 127.0.0.1
4 | settings put global captive_portal_detection_enabled 0
--------------------------------------------------------------------------------
/app/src/main/res/raw/userinit.sh:
--------------------------------------------------------------------------------
1 | #!/system/bin/sh
2 |
3 | # check if orwall is installed
4 | if [ ! -d '/data/data/org.ethack.orwall' ]; then
5 | log -p i -t orwall "orWall doesn't seem to be installed."
6 | exit 0
7 | fi
8 |
9 | IP6TABLES=/system/bin/ip6tables
10 | IPTABLES=/system/bin/iptables
11 |
12 | # check if iptables can lock
13 | if ${IPTABLES} --help | grep -q -e "--wait"; then
14 | IP6TABLES=/system/bin/ip6tables\ -w
15 | IPTABLES=/system/bin/iptables\ -w
16 | fi
17 |
18 | log() {
19 | command log -p d -t orwall "$@"
20 | }
21 |
22 | run() {
23 | log "$@"
24 | command $@ 2>&1 | while read line ; do
25 | log " $line"
26 | done
27 | }
28 |
29 | ORBOT_UID=$(cat /data/system/packages.list | sed -n 's/^org.torproject.android //p' | cut -d ' ' -f1)
30 |
31 | # paranoia / this script should run one time only
32 | command ${IPTABLES} -C ow_OUTPUT_LOCK -j DROP
33 | if [ $? -eq 0 ]; then
34 | log "orwall seems to be already initialized"
35 | exit 0
36 | else
37 | log "Starting orwall init as $(id)"
38 | fi
39 |
40 | # FIXME: Running iptables first time seems to initalize it.
41 | sleep 1
42 | run "$IPTABLES --list"
43 |
44 | # OUTPUT
45 | run "$IPTABLES -P OUTPUT DROP"
46 | run "$IPTABLES -N ow_OUTPUT_LOCK"
47 | run "$IPTABLES -A ow_OUTPUT_LOCK -m owner --uid-owner $ORBOT_UID -m conntrack --ctstate NEW,RELATED,ESTABLISHED -j ACCEPT"
48 | run "$IPTABLES -A ow_OUTPUT_LOCK -j DROP"
49 | run "$IPTABLES -I OUTPUT -j ow_OUTPUT_LOCK"
50 |
51 | # INPUT
52 | run "$IPTABLES -P INPUT DROP"
53 | run "$IPTABLES -N ow_INPUT_LOCK"
54 | run "$IPTABLES -A ow_INPUT_LOCK -m owner --uid-owner $ORBOT_UID -m conntrack --ctstate NEW,RELATED,ESTABLISHED -j ACCEPT"
55 | run "$IPTABLES -A ow_INPUT_LOCK -j DROP"
56 | run "$IPTABLES -I INPUT -j ow_INPUT_LOCK"
57 |
58 | ## Block all traffic at boot ##
59 | run "$IP6TABLES -P INPUT DROP"
60 | run "$IP6TABLES -P OUTPUT DROP"
61 | run "$IP6TABLES -P FORWARD DROP"
62 | run "$IP6TABLES -I INPUT -j REJECT"
63 | run "$IP6TABLES -I OUTPUT -j REJECT"
64 | run "$IP6TABLES -I FORWARD -j REJECT"
65 |
66 | # output iptables status: filter
67 | #run "$IPTABLES -nL -t filter"
68 | # output iptables status: nat
69 | #run "$IPTABLES -nL -t nat"
70 |
--------------------------------------------------------------------------------
/app/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 9050
4 | 9040
5 | 5400
6 | 8118
7 | 5
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/no_translation.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | orWall
4 |
5 |
6 | https://orwall.org \nhttps://twitter.com/orWallApp \nhttps://github.com/EthACKdotOrg/orWall
7 |
8 |
9 | EthACK (https://www.ethack.org) \nSwissTengu (https://www.swisstengu.ch) \nHenri Gourvest(hgourvest@progdigy.com)
10 |
11 |
12 | super-command (Apache2) \nNetCipher (3-clauses BSD license)
13 |
14 | GPLv3
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | %1$d minutes, %2$d seconds until end of permission.
4 | End of Browser bypass.
5 |
6 | Settings
7 | About
8 |
9 | Enable SIP
10 | Authorize Browser
11 |
12 | Preferences
13 |
14 | Network
15 | Manage some network options. Beware, you may go insecure!
16 | LAN connections
17 |
18 | Allows LAN to bypass the firewall and Orbot.\n
19 | \n
20 | WARNING: this will allow ALL apps to access LAN. And .onion won\'t resolve anymore.
21 |
22 | Enable Android Debug Bridge (ADB)
23 | Open TCP 5555 in both directions so that you may connect to your device over the network.
24 | Enable SSH incoming connections
25 | Opens incoming TCP 22 port so that you may connect to your device through SSH.
26 |
27 | Tethering rules
28 |
29 | Captive portal detection
30 | Enable captive portal detection
31 | Caution! This may leads to data leakage as the OS will probe network each time you connect to the Wireless!
32 | Browser grace time in minutes (default: 5)
33 | Set up grace time in order to log into captive portals. Five minutes should be enough.
34 |
35 | Special applications
36 | Select applications with special rights, like your preferred browser app for Captive Portal authentication or VoIP.
37 | Browser for Portal login
38 | Choose browser application
39 | This browser will be authorized, on request, to bypass Tor in order to connect to a Captive Portal. You can activate the temporary bypass directly from the Menu.
40 | Choose SIP application
41 | Selected application will see its UDP traffic allowed outside of Tor.
42 |
43 | Set proxy ports
44 | Set Orbot proxy ports if you changed them in its configuration
45 | Proxy SOCKS port (Default: 9050)
46 | Transparent proxy port (Default: 9040)
47 | DNS Proxy port (Default: 5400)
48 | Polipo port (Default: 8118)
49 |
50 | Project links
51 |
52 | License
53 | Unlinked to Tor Project
54 | This product is produced independently from the Tor® anonymity software and carries no guarantee from The Tor Project about quality, suitability or anything else.
55 | Author(s)
56 |
57 | Put your apps behind Orbot and block all unwanted traffic in one go.
58 | External libraries
59 |
60 | Init script installation
61 |
62 | This application uses an init-script.\n
63 | This script blocks all IP communication at boot time, preventing possible leaks from your device.\n
64 | We strongly recommend you accept this script.\n
65 | \n
66 | Script location is %s
67 |
68 | Install
69 | Refuse
70 |
71 | Tethering activated in orWall
72 | Tethering deactivated in orWall
73 |
74 | orWall status
75 | orWall is being initialized
76 | Enabled
77 | Disabled
78 | Disable orWall?
79 | You\'re going to disable orWall.\n\n
80 | This means your device will allow all outgoing connections. As well, outgoing connections
81 | will not be forced to any anonymous proxy anymore.\n
82 | \n
83 | This may be dangerous!\n
84 | \n
85 | Please keep in mind deactivation will last until next reboot.
86 |
87 | Accept
88 |
89 | New rules are being added
90 | Rules are being removed
91 | Rules are being updated
92 | An error occurred, please contact the dev! #%1$d
93 |
94 | Long press on an app will show you advanced settings
95 | Close
96 | Advanced settings for %1$s
97 | Connection provider
98 | Connection type
99 | Force connection (using REDIRECT)
100 | Use application native capacity (Fenced path)
101 | Bypass Proxy
102 | Save
103 | Cancel
104 |
105 | Start Wizard
106 | Step
107 | What is orWall
108 | Quick settings
109 | Application settings
110 | Welcome to orWall
111 | Close wizard
112 |
113 | Init script activation:
114 |
115 | This application uses an init-script in %1$s.\n
116 | Unfortunately, it seems your Android device doesn\'t support it.\n
117 | \n
118 | You may experience data leaks at boot time.\n
119 | \n
120 | We cannot do anything against that :(.
121 |
122 | Root access (read-only):
123 | IPTables installed (read-only):
124 | We didn\'t found the iptables binary on this ROM… This application will not work.
125 | Comment support in IPTables (read-only):
126 | Orbot (read-only):
127 |
128 | Warning! IPTables doesn\'t seem to be initialized!
129 |
130 | Welcome to orWall!\n
131 | This wizard will let you learn what orWall can do and what it cannot. It will also give you some
132 | hints on what might or might not be done.\n
133 | \n
134 | orWall is not a firewall\n
135 | Keep it in mind. orWall will not replace AFWall nor DroidWall nor anything like that.\n
136 | orWall main aim is to allow you forcing apps though some local proxy when those apps don\'t
137 | know how to talk to this proxy; it will also allow you to prevent any leaks, using an init-script.\n
138 | \n
139 | How to use orWall\n
140 | You will find three panels:\n
141 | - Home\n
142 | - Apps\n
143 | - Logs (which isn\'t available for now)\n
144 | \n
145 | On the Home panel, you will find some main settings.\n
146 | From there, you are able to deactivate orWall (until next reboot), activate Tethering support (by default,
147 | orWall rules block that usage), as well as give a temporary authorization to a web browser to bypass the whole proxy
148 | thing, enable SIP application to send UDP requests, and enable Local Area Network (LAN) connections.\n
149 | \n
150 | On the Apps panel, you will find a list of your installed applications.\n
151 | \n
152 | Bellow you will find some information about current orWall status. Note that some switches may be disabled.
153 | This happens if, for some reason, an option isn\'t available on your device. It is also true for read-only settings.
154 |
155 |
156 | Disable orWall\n
157 | This option let you deactivate orWall. Just keep in mind the deactivation will ends once you reboot
158 | your device.
159 | \n
160 | Enable tethering\n
161 | You\'ll need to activate this option if you want to share your Internet connection from your device.\n
162 | Once this option is enabled, orWall will detect automatically when you enable the connection sharing, and
163 | activate a subset of firewall rules.\n
164 | \n
165 | It may take a while before this takes effect.\n
166 | WARNING! This option doesn\'t work as expected for now :(.\n
167 | \n
168 | Authorize browser\n
169 | This option allows you to lock up your device, while still being able to authenticate to some
170 | captive portal (like in hotels). An application you select through Settings>Special Applications
171 | will get an unlimited access to the network for a give time.\n
172 | Once you enable this option, a timer will start. Once it reaches the end of the grace time, the
173 | browser will be locked as previously.\n
174 | The grace time may be set in Settings>Network. Default value is 5 minutes.\n
175 | \n
176 | Enable SIP\n
177 | Toggling this option will allow a selected application to access remote hosts using UDP protocol.\n
178 | \n
179 | Enable LAN connections\n
180 | This will let you access to your Local Area Network.
181 |
182 |
183 | Default behavior\n
184 | When you touch an app in the "Apps" tab, it will toggle its status: either the app can access the Net
185 | using Orbot, or it\'s locked out.\n
186 | The list view will refresh eventually when you come back to orWall later (this must be improved).\n
187 | \n
188 | Advanced settings\n
189 | A long touch will display a dialog in order to let you configure a bit more the app accesses.\n
190 | The following options are available for now:\n
191 | - Connection provider: let you chose between Tor and… well, Tor for now.\n
192 | - Connection type: either the application can be configured in order to use a local proxy, or you\'ll need
193 | to force it through the proxy.\n
194 | - Complete bypass: this option shouldn\'t be use at all, as it will allow the application to bypass completely
195 | your proxy, for life.\n
196 | \n
197 | Forced connection\n
198 | It uses iptables REDIRECT target in order to force the application through local proxy\n
199 | It will be shown on the application list as "TransProxy via [proxy]".\n
200 | \n
201 | Fenced connection\n
202 | This is for application aware of proxy settings. Some applications like Twidere, Lightning or ChatSecure
203 | already embed settings allowing you to make them go through Orbot or other proxies.\n
204 | Selecting this option in orWall will allow such application to connect to the Proxy, while preventing them to connect
205 | to other services.\n
206 | Imagine this option as a fenced road: You may access freely the content of the road, but you can\'t go elsewhere.
207 |
208 |
209 | orWall is deactivated
210 | You may be at risk!
211 |
212 | IPTables was not detected
213 | It seems root access has been denied
214 |
215 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/fragment_apps_prefs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/fragment_network_prefs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
16 |
17 |
18 |
19 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/fragment_proxy_ports.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_preference.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/other_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/preferences_header.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
15 |
16 |
21 |
22 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:2.1.2'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EthACKdotOrg/orWall/37b361e24d1f090e824ad025f7eba128ea6ce1e4/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/orWall.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------