[tor-commits] [tor-browser/tor-browser-52.7.2esr-8.0-1] Orfox: NetCipher enabled, checks if orbot is installed

gk at torproject.org gk at torproject.org
Tue Mar 20 11:26:34 UTC 2018


commit 67aca67cc7b0802f19024ad39f3d70733a47ce49
Author: Amogh Pradeep <amoghbl1 at gmail.com>
Date:   Mon Jul 20 21:46:25 2015 -0400

    Orfox: NetCipher enabled, checks if orbot is installed
    
    Signed-off-by: Amogh Pradeep <amoghbl1 at gmail.com>
---
 .../base/java/org/mozilla/gecko/BrowserApp.java    |  32 ++-
 mobile/android/base/moz.build                      |   6 +
 mobile/android/base/strings.xml.in                 |   5 +
 .../netcipher/proxy/OrbotHelper.java               | 186 ++++++++++++++++
 .../netcipher/proxy/TorServiceUtils.java           | 233 +++++++++++++++++++++
 mobile/android/orfox/strings.xml.in                |   5 +
 6 files changed, 462 insertions(+), 5 deletions(-)

diff --git a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
index 9023618669ef..a532b454a263 100644
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -174,6 +174,8 @@ import android.animation.ObjectAnimator;
 import org.json.JSONException;
 import org.json.JSONObject;
 
+import info.guardianproject.netcipher.proxy.OrbotHelper;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -1068,6 +1070,30 @@ public class BrowserApp extends GeckoApp
         }
     }
 
+    public void checkStartOrbot() {
+        if (!OrbotHelper.isOrbotInstalled(this)) {
+            final Intent intent = OrbotHelper.getOrbotInstallIntent(this);
+
+            AlertDialog.Builder builder = new AlertDialog.Builder(this);
+            builder.setTitle(R.string.install_orbot);
+            builder.setMessage(R.string.you_must_have_orbot);
+            builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialogInterface, int i) {
+                    startActivity(intent);
+                }
+            });
+            builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
+                @Override
+                public void onClick(DialogInterface dialogInterface, int i) {
+                }
+            });
+            builder.show();
+        } else {
+            OrbotHelper.requestStartTor(this);
+        }
+    }
+
     @Override
     public void onResume() {
         super.onResume();
@@ -1076,11 +1102,7 @@ public class BrowserApp extends GeckoApp
             return;
         }
 
-        if (!mHasResumed) {
-            EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this,
-                    "Prompt:ShowTop");
-            mHasResumed = true;
-        }
+        checkStartOrbot();
 
         processTabQueue();
 
diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build
index 6c88464ab521..4dbb1c25fdab 100644
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -12,6 +12,7 @@ include('android-services.mozbuild')
 
 geckoview_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/main/'
 geckoview_thirdparty_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/thirdparty/'
+geckoview_netcipher_source_dir = TOPSRCDIR + '/mobile/android/geckoview/src/thirdparty/java/info/guardianproject/netcipher/proxy/'
 thirdparty_source_dir = TOPSRCDIR + '/mobile/android/thirdparty/'
 
 constants_jar = add_java_jar('constants')
@@ -289,6 +290,11 @@ gvjar.sources += [geckoview_thirdparty_source_dir + f for f in [
     'java/com/googlecode/eyesfree/braille/selfbraille/WriteData.java',
 ]]
 
+gvjar.sources += [geckoview_netcipher_source_dir + f for f in [
+    'OrbotHelper.java',
+    'TorServiceUtils.java',
+]]
+
 gvjar.extra_jars += [
     CONFIG['ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB'],
     CONFIG['ANDROID_SUPPORT_V4_AAR_LIB'],
diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in
index 3511a4eca644..ec39107f8f60 100644
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -28,6 +28,11 @@
 #include ../search/strings/search_strings.xml.in
 
 #include ../services/strings.xml.in
+#include ../orfox/strings.xml.in
+
+  <string name="no_space_to_start_error">&no_space_to_start_error;</string>
+  <string name="error_loading_file">&error_loading_file;</string>
+
 
   <string name="firstrun_panel_title_welcome">&firstrun_panel_title_welcome;</string>
 
diff --git a/mobile/android/geckoview/src/thirdparty/java/info/guardianproject/netcipher/proxy/OrbotHelper.java b/mobile/android/geckoview/src/thirdparty/java/info/guardianproject/netcipher/proxy/OrbotHelper.java
new file mode 100644
index 000000000000..d6a632fda37d
--- /dev/null
+++ b/mobile/android/geckoview/src/thirdparty/java/info/guardianproject/netcipher/proxy/OrbotHelper.java
@@ -0,0 +1,186 @@
+
+package info.guardianproject.netcipher.proxy;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+
+
+import java.util.List;
+
+public class OrbotHelper {
+
+    private final static int REQUEST_CODE_STATUS = 100;
+
+    public final static String ORBOT_PACKAGE_NAME = "org.torproject.android";
+    public final static String ORBOT_MARKET_URI = "market://details?id=" + ORBOT_PACKAGE_NAME;
+    public final static String ORBOT_FDROID_URI = "https://f-droid.org/repository/browse/?fdid="
+            + ORBOT_PACKAGE_NAME;
+    public final static String ORBOT_PLAY_URI = "https://play.google.com/store/apps/details?id="
+            + ORBOT_PACKAGE_NAME;
+
+    /**
+     * A request to Orbot to transparently start Tor services
+     */
+    public final static String ACTION_START = "org.torproject.android.intent.action.START";
+    /**
+     * {@link Intent} send by Orbot with {@code ON/OFF/STARTING/STOPPING} status
+     */
+    public final static String ACTION_STATUS = "org.torproject.android.intent.action.STATUS";
+    /**
+     * {@code String} that contains a status constant: {@link #STATUS_ON},
+     * {@link #STATUS_OFF}, {@link #STATUS_STARTING}, or
+     * {@link #STATUS_STOPPING}
+     */
+    public final static String EXTRA_STATUS = "org.torproject.android.intent.extra.STATUS";
+    /**
+     * A {@link String} {@code packageName} for Orbot to direct its status reply
+     * to, used in {@link #ACTION_START} {@link Intent}s sent to Orbot
+     */
+    public final static String EXTRA_PACKAGE_NAME = "org.torproject.android.intent.extra.PACKAGE_NAME";
+
+    /**
+     * All tor-related services and daemons are stopped
+     */
+    public final static String STATUS_OFF = "OFF";
+    /**
+     * All tor-related services and daemons have completed starting
+     */
+    public final static String STATUS_ON = "ON";
+    public final static String STATUS_STARTING = "STARTING";
+    public final static String STATUS_STOPPING = "STOPPING";
+    /**
+     * The user has disabled the ability for background starts triggered by
+     * apps. Fallback to the old Intent that brings up Orbot.
+     */
+    public final static String STATUS_STARTS_DISABLED = "STARTS_DISABLED";
+
+    public final static String ACTION_START_TOR = "org.torproject.android.START_TOR";
+    public final static String ACTION_REQUEST_HS = "org.torproject.android.REQUEST_HS_PORT";
+    public final static int START_TOR_RESULT = 0x048079234;
+    public final static int HS_REQUEST_CODE = 9999;
+
+    private final static String FDROID_PACKAGE_NAME = "org.fdroid.fdroid";
+    private final static String PLAY_PACKAGE_NAME = "com.android.vending";
+
+    private OrbotHelper() {
+        // only static utility methods, do not instantiate
+    }
+
+    public static boolean isOrbotRunning(Context context) {
+        int procId = TorServiceUtils.findProcessId(context);
+
+        return (procId != -1);
+    }
+
+    public static boolean isOrbotInstalled(Context context) {
+        return isAppInstalled(context, ORBOT_PACKAGE_NAME);
+    }
+
+    private static boolean isAppInstalled(Context context, String uri) {
+        try {
+            PackageManager pm = context.getPackageManager();
+            pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
+            return true;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public static void requestHiddenServiceOnPort(Activity activity, int port) {
+        Intent intent = new Intent(ACTION_REQUEST_HS);
+        intent.setPackage(ORBOT_PACKAGE_NAME);
+        intent.putExtra("hs_port", port);
+
+        activity.startActivityForResult(intent, HS_REQUEST_CODE);
+    }
+
+    /**
+     * First, checks whether Orbot is installed. If Orbot is installed, then a
+     * broadcast {@link Intent} is sent to request Orbot to start transparently
+     * in the background. When Orbot receives this {@code Intent}, it will
+     * immediately reply to this all with its status via an
+     * {@link #ACTION_STATUS} {@code Intent} that is broadcast to the
+     * {@code packageName} of the provided {@link Context} (i.e.
+     * {@link Context#getPackageName()}.
+     *
+     * @param context the app {@link Context} will receive the reply
+     * @return whether the start request was sent to Orbot
+     */
+    public static boolean requestStartTor(Context context) {
+        if (OrbotHelper.isOrbotInstalled(context)) {
+            Log.i("OrbotHelper", "requestStartTor " + context.getPackageName());
+            Intent intent = getOrbotStartIntent();
+            intent.putExtra(EXTRA_PACKAGE_NAME, context.getPackageName());
+            context.sendBroadcast(intent);
+            return true;
+        }
+        return false;
+    }
+
+    public static Intent getOrbotStartIntent() {
+        Intent intent = new Intent(ACTION_START);
+        intent.setPackage(ORBOT_PACKAGE_NAME);
+        return intent;
+    }
+
+    /**
+     * First, checks whether Orbot is installed, then checks whether Orbot is
+     * running. If Orbot is installed and not running, then an {@link Intent} is
+     * sent to request Orbot to start, which will show the main Orbot screen.
+     * The result will be returned in
+     * {@link Activity#onActivityResult(int requestCode, int resultCode, Intent data)}
+     * with a {@code requestCode} of {@link START_TOR_RESULT}
+     *
+     * @param activity the {@link Activity} that gets the
+     *            {@code START_TOR_RESULT} result
+     * @return whether the start request was sent to Orbot
+     */
+    public static boolean requestShowOrbotStart(Activity activity) {
+        if (OrbotHelper.isOrbotInstalled(activity)) {
+            if (!OrbotHelper.isOrbotRunning(activity)) {
+                Intent intent = getShowOrbotStartIntent();
+                activity.startActivityForResult(intent, START_TOR_RESULT);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static Intent getShowOrbotStartIntent() {
+        Intent intent = new Intent(ACTION_START_TOR);
+        intent.setPackage(ORBOT_PACKAGE_NAME);
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        return intent;
+    }
+
+    public static Intent getOrbotInstallIntent(Context context) {
+        final Intent intent = new Intent(Intent.ACTION_VIEW);
+        intent.setData(Uri.parse(ORBOT_MARKET_URI));
+
+        PackageManager pm = context.getPackageManager();
+        List<ResolveInfo> resInfos = pm.queryIntentActivities(intent, 0);
+
+        String foundPackageName = null;
+        for (ResolveInfo r : resInfos) {
+            Log.i("OrbotHelper", "market: " + r.activityInfo.packageName);
+            if (TextUtils.equals(r.activityInfo.packageName, FDROID_PACKAGE_NAME)
+                    || TextUtils.equals(r.activityInfo.packageName, PLAY_PACKAGE_NAME)) {
+                foundPackageName = r.activityInfo.packageName;
+                break;
+            }
+        }
+
+        if (foundPackageName == null) {
+            intent.setData(Uri.parse(ORBOT_FDROID_URI));
+        } else {
+            intent.setPackage(foundPackageName);
+        }
+        return intent;
+    }
+}
diff --git a/mobile/android/geckoview/src/thirdparty/java/info/guardianproject/netcipher/proxy/TorServiceUtils.java b/mobile/android/geckoview/src/thirdparty/java/info/guardianproject/netcipher/proxy/TorServiceUtils.java
new file mode 100644
index 000000000000..e553ecac3543
--- /dev/null
+++ b/mobile/android/geckoview/src/thirdparty/java/info/guardianproject/netcipher/proxy/TorServiceUtils.java
@@ -0,0 +1,233 @@
+/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
+/* See LICENSE for licensing information */
+
+package info.guardianproject.netcipher.proxy;
+
+import android.content.Context;
+import android.util.Log;
+
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.URLEncoder;
+import java.util.StringTokenizer;
+
+public class TorServiceUtils {
+
+    private final static String TAG = "TorUtils";
+    // various console cmds
+    public final static String SHELL_CMD_CHMOD = "chmod";
+    public final static String SHELL_CMD_KILL = "kill -9";
+    public final static String SHELL_CMD_RM = "rm";
+    public final static String SHELL_CMD_PS = "ps";
+    public final static String SHELL_CMD_PIDOF = "pidof";
+
+    public final static String CHMOD_EXE_VALUE = "700";
+
+    public static boolean isRootPossible()
+    {
+
+        StringBuilder log = new StringBuilder();
+
+        try {
+
+            // Check if Superuser.apk exists
+            File fileSU = new File("/system/app/Superuser.apk");
+            if (fileSU.exists())
+                return true;
+
+            fileSU = new File("/system/app/superuser.apk");
+            if (fileSU.exists())
+                return true;
+
+            fileSU = new File("/system/bin/su");
+            if (fileSU.exists())
+            {
+                String[] cmd = {
+                    "su"
+                };
+                int exitCode = TorServiceUtils.doShellCommand(cmd, log, false, true);
+                if (exitCode != 0)
+                    return false;
+                else
+                    return true;
+            }
+
+            // Check for 'su' binary
+            String[] cmd = {
+                "which su"
+            };
+            int exitCode = TorServiceUtils.doShellCommand(cmd, log, false, true);
+
+            if (exitCode == 0) {
+                Log.d(TAG, "root exists, but not sure about permissions");
+                return true;
+
+            }
+
+        } catch (IOException e) {
+            // this means that there is no root to be had (normally) so we won't
+            // log anything
+            Log.e(TAG, "Error checking for root access", e);
+
+        } catch (Exception e) {
+            Log.e(TAG, "Error checking for root access", e);
+            // this means that there is no root to be had (normally)
+        }
+
+        Log.e(TAG, "Could not acquire root permissions");
+
+        return false;
+    }
+
+    public static int findProcessId(Context context) {
+        String dataPath = context.getFilesDir().getParentFile().getParentFile().getAbsolutePath();
+        String command = dataPath + "/" + OrbotHelper.ORBOT_PACKAGE_NAME + "/app_bin/tor";
+        int procId = -1;
+
+        try {
+            procId = findProcessIdWithPidOf(command);
+
+            if (procId == -1)
+                procId = findProcessIdWithPS(command);
+        } catch (Exception e) {
+            try {
+                procId = findProcessIdWithPS(command);
+            } catch (Exception e2) {
+                Log.e(TAG, "Unable to get proc id for command: " + URLEncoder.encode(command), e2);
+            }
+        }
+
+        return procId;
+    }
+
+    // use 'pidof' command
+    public static int findProcessIdWithPidOf(String command) throws Exception
+    {
+
+        int procId = -1;
+
+        Runtime r = Runtime.getRuntime();
+
+        Process procPs = null;
+
+        String baseName = new File(command).getName();
+        // fix contributed my mikos on 2010.12.10
+        procPs = r.exec(new String[] {
+                SHELL_CMD_PIDOF, baseName
+        });
+        // procPs = r.exec(SHELL_CMD_PIDOF);
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
+        String line = null;
+
+        while ((line = reader.readLine()) != null)
+        {
+
+            try
+            {
+                // this line should just be the process id
+                procId = Integer.parseInt(line.trim());
+                break;
+            } catch (NumberFormatException e)
+            {
+                Log.e("TorServiceUtils", "unable to parse process pid: " + line, e);
+            }
+        }
+
+        return procId;
+
+    }
+
+    // use 'ps' command
+    public static int findProcessIdWithPS(String command) throws Exception
+    {
+
+        int procId = -1;
+
+        Runtime r = Runtime.getRuntime();
+
+        Process procPs = null;
+
+        procPs = r.exec(SHELL_CMD_PS);
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
+        String line = null;
+
+        while ((line = reader.readLine()) != null)
+        {
+            if (line.indexOf(' ' + command) != -1)
+            {
+
+                StringTokenizer st = new StringTokenizer(line, " ");
+                st.nextToken(); // proc owner
+
+                procId = Integer.parseInt(st.nextToken().trim());
+
+                break;
+            }
+        }
+
+        return procId;
+
+    }
+
+    public static int doShellCommand(String[] cmds, StringBuilder log, boolean runAsRoot,
+            boolean waitFor) throws Exception
+    {
+
+        Process proc = null;
+        int exitCode = -1;
+
+        if (runAsRoot)
+            proc = Runtime.getRuntime().exec("su");
+        else
+            proc = Runtime.getRuntime().exec("sh");
+
+        OutputStreamWriter out = new OutputStreamWriter(proc.getOutputStream());
+
+        for (int i = 0; i < cmds.length; i++)
+        {
+            // TorService.logMessage("executing shell cmd: " + cmds[i] +
+            // "; runAsRoot=" + runAsRoot + ";waitFor=" + waitFor);
+
+            out.write(cmds[i]);
+            out.write("\n");
+        }
+
+        out.flush();
+        out.write("exit\n");
+        out.flush();
+
+        if (waitFor)
+        {
+
+            final char buf[] = new char[10];
+
+            // Consume the "stdout"
+            InputStreamReader reader = new InputStreamReader(proc.getInputStream());
+            int read = 0;
+            while ((read = reader.read(buf)) != -1) {
+                if (log != null)
+                    log.append(buf, 0, read);
+            }
+
+            // Consume the "stderr"
+            reader = new InputStreamReader(proc.getErrorStream());
+            read = 0;
+            while ((read = reader.read(buf)) != -1) {
+                if (log != null)
+                    log.append(buf, 0, read);
+            }
+
+            exitCode = proc.waitFor();
+
+        }
+
+        return exitCode;
+
+    }
+}
diff --git a/mobile/android/orfox/strings.xml.in b/mobile/android/orfox/strings.xml.in
new file mode 100644
index 000000000000..e3a22974ed78
--- /dev/null
+++ b/mobile/android/orfox/strings.xml.in
@@ -0,0 +1,5 @@
+<!-- NetCipher Integration Strings, used for dialog -->
+  <string name="install_orbot">Install Orbot?</string>
+  <string name="you_must_have_orbot">You must have Orbot installed and activated to proxy traffic through it. Would you like to download it?</string>
+  <string name="yes">Yes</string>
+  <string name="no">No</string>





More information about the tor-commits mailing list