diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 458d48bf..af077f41 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -25,6 +25,7 @@
+
diff --git a/res/drawable/bak/tornotification.png b/res/drawable/bak/tornotification.png
new file mode 100755
index 00000000..978aa88f
Binary files /dev/null and b/res/drawable/bak/tornotification.png differ
diff --git a/res/drawable/bak/tornotificationoff.png b/res/drawable/bak/tornotificationoff.png
new file mode 100755
index 00000000..3bee1709
Binary files /dev/null and b/res/drawable/bak/tornotificationoff.png differ
diff --git a/res/drawable/bak/toroff.png b/res/drawable/bak/toroff.png
new file mode 100755
index 00000000..7e2602b7
Binary files /dev/null and b/res/drawable/bak/toroff.png differ
diff --git a/res/drawable/bak/toron.png b/res/drawable/bak/toron.png
new file mode 100755
index 00000000..5a8bd362
Binary files /dev/null and b/res/drawable/bak/toron.png differ
diff --git a/res/drawable/bak/torstarting.png b/res/drawable/bak/torstarting.png
new file mode 100755
index 00000000..e0bca5da
Binary files /dev/null and b/res/drawable/bak/torstarting.png differ
diff --git a/res/drawable/bak/torstopping.png b/res/drawable/bak/torstopping.png
new file mode 100755
index 00000000..0e8eeed5
Binary files /dev/null and b/res/drawable/bak/torstopping.png differ
diff --git a/res/drawable/bgtitanium.jpg b/res/drawable/bgtitanium.jpg
new file mode 100755
index 00000000..14556292
Binary files /dev/null and b/res/drawable/bgtitanium.jpg differ
diff --git a/res/drawable/ic_menu_check.png b/res/drawable/ic_menu_check.png
new file mode 100644
index 00000000..167ffb18
Binary files /dev/null and b/res/drawable/ic_menu_check.png differ
diff --git a/res/drawable/ic_menu_exit.png b/res/drawable/ic_menu_exit.png
new file mode 100755
index 00000000..40183ebc
Binary files /dev/null and b/res/drawable/ic_menu_exit.png differ
diff --git a/res/layout/layout_about.xml b/res/layout/layout_about.xml
index 87423c62..f8223aec 100644
--- a/res/layout/layout_about.xml
+++ b/res/layout/layout_about.xml
@@ -11,13 +11,28 @@
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
+
+
+
+ android:textColor="#00ff00" />
-
+ android:textColor="#00ff00" />
+ android:textColor="#00ff00" />
+ android:textColor="#00ff00" />
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/layout_wizard_root.xml b/res/layout/layout_wizard_root.xml
new file mode 100644
index 00000000..f71c284a
--- /dev/null
+++ b/res/layout/layout_wizard_root.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/layout_wizard_stock.xml b/res/layout/layout_wizard_stock.xml
new file mode 100644
index 00000000..b8f15d10
--- /dev/null
+++ b/res/layout/layout_wizard_stock.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/layout_wizard_tips.xml b/res/layout/layout_wizard_tips.xml
new file mode 100644
index 00000000..825be2f5
--- /dev/null
+++ b/res/layout/layout_wizard_tips.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/layout/layout_wizard_welcome.xml b/res/layout/layout_wizard_welcome.xml
new file mode 100644
index 00000000..b0c3fafd
--- /dev/null
+++ b/res/layout/layout_wizard_welcome.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4f230b3a..6bde2df3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -58,10 +58,52 @@ and all DNS requests. This includes the built-in Browser, Gmail, YouTube and Map
Automatic Torifying of Apps
Tor Everything
-Send traffic for all apps through Tor
+Proxy traffic for all apps through Tor
Tor binaries successfully installed!
The Tor binary files were unable to be installed. Please check the log and notify tor-assistants@torproject.org
Application Error
+
+Welcome to Orbot
+
+About Orbot
+Next
+Back
+Finish
+
+
+
+ Orbot brings Tor to Android. Tor is free software and an open network that helps you defend against a form of network surveillance that threatens personal freedom and privacy, confidential business activities and relationships, and state security known as traffic analysis.\n\n*WARNING:* Simply installing Orbot will _not_ magically anonymize your mobile traffic! This wizard will help you get started.
+ Some Orbot Details
+ Orbot is an open-source application that contains Tor, LibEvent and Privoxy. It provides a local HTTP proxy (8118) and a SOCKS proxy (9050) into the Tor network. Orbot also has the ability, on rooted device, to send all internet traffic through Tor.
+ Grant Permissions
+ Permissions Warning
+ Excellent! We\'ve detected that you have root permissions enabled for Orbot. We will use this power wisely.
+ We\'ve detected that you do not have root permissions enabled. Your application data usage will NOT be transparently routed through Tor without root access.
+
+ If you choose to continue WITHOUT root, you must use apps that know how to talk to Tor.
+ I understand and would like to continue without root
+
+ Attempt to enable root access
+ Configure Torification
+ Orbot gives you the option to route all application traffic through Tor OR to choose your applications individually.
+ Proxy All Apps Through Tor
+ Select Individual Apps for Tor
+
+
+ Orbot-enabled Apps
+ We encourage you to download & use apps that know how to connect directly to Orbot. Click on the buttons below to install.
+ OtrChat - From the Orbot dev team, a secure Instant Messaging client for Android.
+ OrWeb (Android 1.x Only) - From the Orbot dev team, a web browser designed to work with Tor.
+
+ Orbot is ready!
+ Hundreds of thousands of people around the world use Tor for a wide variety of reasons: journalists and bloggers, human rights workers, law enforcement officers, soldiers, corporations, citizens of repressive regimes, and just ordinary citizens... and now you are ready to, as well!
+
+ https://github.com/downloads/guardianproject/OtRChat/OtRChat-0.0.1-alpha-build6a.apk
+ http://github.com/downloads/guardianproject/Orweb/Orweb-0.0.1c.apk.apk
+
+
+
+
diff --git a/src/org/torproject/android/AppManager.java b/src/org/torproject/android/AppManager.java
new file mode 100644
index 00000000..433425a9
--- /dev/null
+++ b/src/org/torproject/android/AppManager.java
@@ -0,0 +1,263 @@
+/* Copyright (c) 2009, Nathan Freitas, Orbot / The Guardian Project - http://openideals.com/guardian */
+/* See LICENSE for licensing information */
+
+package org.torproject.android;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageView;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+
+public class AppManager extends Activity implements OnCheckedChangeListener, OnClickListener, TorConstants {
+
+ private static TorifiedApp[] apps = null;
+
+ private ListView listApps;
+
+ private AppManager mAppManager;
+
+
+ private boolean appsLoaded = false;
+
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ this.setContentView(R.layout.layout_apps);
+
+ mAppManager = this;
+
+
+
+ }
+
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ listApps = (ListView)findViewById(R.id.applistview);
+
+ if (!appsLoaded)
+ loadApps();
+ }
+
+
+
+ private void loadApps ()
+ {
+ final TorifiedApp[] apps = getApps(this);
+
+ Arrays.sort(apps, new Comparator() {
+ public int compare(TorifiedApp o1, TorifiedApp o2) {
+ if (o1.isTorified() == o2.isTorified()) return o1.getName().compareTo(o2.getName());
+ if (o1.isTorified()) return -1;
+ return 1;
+ }
+ });
+
+ final LayoutInflater inflater = getLayoutInflater();
+
+ final ListAdapter adapter = new ArrayAdapter(this,R.layout.layout_apps_item,R.id.itemtext,apps) {
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ListEntry entry;
+ if (convertView == null) {
+ // Inflate a new view
+ convertView = inflater.inflate(R.layout.layout_apps_item, parent, false);
+ entry = new ListEntry();
+ entry.icon = (ImageView) convertView.findViewById(R.id.itemicon);
+ entry.box = (CheckBox) convertView.findViewById(R.id.itemcheck);
+ entry.text = (TextView) convertView.findViewById(R.id.itemtext);
+
+ entry.text.setOnClickListener(mAppManager);
+ entry.text.setOnClickListener(mAppManager);
+
+ convertView.setTag(entry);
+
+ entry.box.setOnCheckedChangeListener(mAppManager);
+ } else {
+ // Convert an existing view
+ entry = (ListEntry) convertView.getTag();
+ }
+
+
+ final TorifiedApp app = apps[position];
+
+
+ entry.icon.setImageDrawable(app.getIcon());
+ entry.text.setText(app.getName());
+
+ final CheckBox box = entry.box;
+ box.setTag(app);
+ box.setChecked(app.isTorified());
+
+ entry.text.setTag(box);
+ entry.icon.setTag(box);
+
+ return convertView;
+ }
+ };
+
+ listApps.setAdapter(adapter);
+
+ appsLoaded = true;
+
+ }
+
+ private static class ListEntry {
+ private CheckBox box;
+ private TextView text;
+ private ImageView icon;
+ }
+
+ /* (non-Javadoc)
+ * @see android.app.Activity#onStop()
+ */
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ //Log.i(getClass().getName(),"Exiting Preferences");
+ }
+
+
+ public static TorifiedApp[] getApps (Context context)
+ {
+ if (apps != null)
+ return apps;
+
+ final SharedPreferences prefs = context.getSharedPreferences(PREFS_KEY, 0);
+
+ String tordAppString = prefs.getString(PREFS_KEY_TORIFIED, "");
+ String[] tordApps;
+
+ StringTokenizer st = new StringTokenizer(tordAppString,"|");
+ tordApps = new String[st.countTokens()];
+ int tordIdx = 0;
+ while (st.hasMoreTokens())
+ {
+ tordApps[tordIdx++] = st.nextToken();
+ }
+
+ Arrays.sort(tordApps);
+
+ //else load the apps up
+ PackageManager pMgr = context.getPackageManager();
+
+ List lAppInfo = pMgr.getInstalledApplications(0);
+
+ Iterator itAppInfo = lAppInfo.iterator();
+
+ apps = new TorifiedApp[lAppInfo.size()];
+
+ ApplicationInfo aInfo = null;
+
+ int appIdx = 0;
+
+ while (itAppInfo.hasNext())
+ {
+ aInfo = itAppInfo.next();
+
+ apps[appIdx] = new TorifiedApp();
+
+ apps[appIdx].setEnabled(aInfo.enabled);
+ apps[appIdx].setUid(aInfo.uid);
+ apps[appIdx].setUsername(pMgr.getNameForUid(apps[appIdx].getUid()));
+ apps[appIdx].setProcname(aInfo.processName);
+ apps[appIdx].setName(pMgr.getApplicationLabel(aInfo).toString());
+ apps[appIdx].setIcon(pMgr.getApplicationIcon(aInfo));
+
+ // check if this application is allowed
+ if (Arrays.binarySearch(tordApps, apps[appIdx].getUsername()) >= 0) {
+ apps[appIdx].setTorified(true);
+ }
+ else
+ {
+ apps[appIdx].setTorified(false);
+ }
+
+ appIdx++;
+ }
+
+ return apps;
+ }
+
+
+ public static void saveAppSettings (Context context)
+ {
+ if (apps == null)
+ return;
+
+ final SharedPreferences prefs = context.getSharedPreferences(PREFS_KEY, 0);
+
+ StringBuilder tordApps = new StringBuilder();
+
+ for (int i = 0; i < apps.length; i++)
+ {
+ if (apps[i].isTorified())
+ {
+ tordApps.append(apps[i].getUsername());
+ tordApps.append("|");
+ }
+ }
+
+ Editor edit = prefs.edit();
+ edit.putString(PREFS_KEY_TORIFIED, tordApps.toString());
+ edit.commit();
+
+ }
+
+
+ /**
+ * Called an application is check/unchecked
+ */
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ final TorifiedApp app = (TorifiedApp) buttonView.getTag();
+ if (app != null) {
+ app.setTorified(isChecked);
+ }
+
+ saveAppSettings(this);
+
+ }
+
+
+
+ @Override
+ public void onClick(View v) {
+
+ CheckBox cbox = (CheckBox)v.getTag();
+
+ final TorifiedApp app = (TorifiedApp)cbox.getTag();
+ if (app != null) {
+ app.setTorified(!app.isTorified());
+ cbox.setChecked(app.isTorified());
+ }
+
+ saveAppSettings(this);
+
+ }
+
+}
diff --git a/src/org/torproject/android/HiddenServiceManager.java b/src/org/torproject/android/HiddenServiceManager.java
new file mode 100644
index 00000000..ca8f7e5c
--- /dev/null
+++ b/src/org/torproject/android/HiddenServiceManager.java
@@ -0,0 +1,25 @@
+package org.torproject.android;
+
+
+//list view with add/remove hidden services - user is prompted for port
+
+public class HiddenServiceManager {
+
+}
+/*
+ *
+ * ## Once you have configured a hidden service, you can look at the
+## contents of the file ".../hidden_service/hostname" for the address
+## to tell people.
+##
+## HiddenServicePort x y:z says to redirect requests on port x to the
+## address y:z.
+
+#HiddenServiceDir @LOCALSTATEDIR@/lib/tor/hidden_service/
+#HiddenServicePort 80 127.0.0.1:80
+
+#HiddenServiceDir @LOCALSTATEDIR@/lib/tor/other_hidden_service/
+#HiddenServicePort 80 127.0.0.1:80
+#HiddenServicePort 22 127.0.0.1:22
+*/
+
diff --git a/src/org/torproject/android/OnBootReceiver.java b/src/org/torproject/android/OnBootReceiver.java
new file mode 100644
index 00000000..37406586
--- /dev/null
+++ b/src/org/torproject/android/OnBootReceiver.java
@@ -0,0 +1,18 @@
+package org.torproject.android;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class OnBootReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent serviceIntent = new Intent();
+ serviceIntent.setAction("org.torproject.android.service.TorService");
+ context.startService(serviceIntent);
+
+ }
+
+}
+
diff --git a/src/org/torproject/android/Orbot.java b/src/org/torproject/android/Orbot.java
index 0d9a31eb..9f63cad5 100644
--- a/src/org/torproject/android/Orbot.java
+++ b/src/org/torproject/android/Orbot.java
@@ -19,6 +19,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -35,7 +36,10 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.widget.Button;
import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.TextView;
@@ -58,10 +62,14 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
/* The primary interface we will be calling on the service. */
ITorService mService = null;
+ Orbot mOrbot = null;
+
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mOrbot = this;
+
setTheme(android.R.style.Theme_Black_NoTitleBar);
//setTitle(getString(R.string.app_name) + ' ' + getString(R.string.app_version));
showMain();
@@ -244,10 +252,8 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
-
mNotificationManager.cancelAll();
-
if (mService != null)
{
try {
@@ -258,6 +264,23 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
}
}
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mOrbot);
+
+ boolean showWizard = prefs.getBoolean("show_wizard",true);
+
+ if (showWizard)
+ {
+
+ Editor pEdit = prefs.edit();
+
+ pEdit.putBoolean("show_wizard",false);
+
+ pEdit.commit();
+
+ showHelp();
+ }
+
+
}
/* (non-Javadoc)
@@ -272,7 +295,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
//updateStatus ("");
- hasRoot = TorTransProxy.hasRootAccess();
+
}
@@ -297,7 +320,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
{
bindService(); //connect the UI activity to the remote service
-
currentView = R.layout.layout_main;
setContentView(currentView);
@@ -358,64 +380,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
private void showHelp ()
{
- LayoutInflater li = LayoutInflater.from(this);
- View view = li.inflate(R.layout.layout_help, null);
-
- StringBuilder msg = new StringBuilder();
- msg.append(getString(R.string.help_text_1));
- msg.append("\n\n");
-
- if (hasRoot)
- {
- msg.append("Your device is ROOTED. Please enable the 'Transparent Proxying' setting to select which apps to send through Tor.");
- }
- else
- {
-
- msg.append("Your device is NOT rooted.\n");
-
- msg.append(getString(R.string.help_text_5));
-
- msg.append("\n\n");
-
- msg.append(getString(R.string.not_anonymous_yet));
- }
-
- /*
-
- msg.append(getString(R.string.help_text_2));
- msg.append("\n\n");
- msg.append(getString(R.string.help_text_3));
- msg.append("\n\n");
- msg.append(getString(R.string.help_text_4));
- msg.append("\n\n");
- msg.append(getString(R.string.help_text_5));
- msg.append("\n\n");
- */
-
-
- new AlertDialog.Builder(this)
- .setTitle(getString(R.string.menu_info))
- .setMessage(msg.toString())
- .setNeutralButton(getString(R.string.button_about), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
-
- showAbout();
- }
- })
- .setNegativeButton(getString(R.string.button_close), new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- // Log.d(TAG, "Close pressed");
- }
- })
- .show();
- }
-
- private void showHelpWizard ()
- {
-
- //sshowAlert("Configure",getString(R.string.not_anonymous_yet));
-
+ new WizardHelper(this).showWizard();
}
@@ -447,6 +412,8 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
}
+
+
/*
* Read in the Preferences and write then to the .torrc file
*/
@@ -611,17 +578,8 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
return;
}
- mService.updateConfiguration("UseBridges", "1", false);
- if (autoUpdateBridges)
- {
- mService.updateConfiguration("UpdateBridgesFromAuthority", "1", false);
-
- }
- else
- {
- mService.updateConfiguration("UpdateBridgesFromAuthority", "0", false);
- }
+ mService.updateConfiguration("UseBridges", "1", false);
String bridgeDelim = "\n";
@@ -637,6 +595,9 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
mService.updateConfiguration("bridge", st.nextToken(), false);
}
+
+ mService.updateConfiguration("UpdateBridgesFromAuthority", "0", false);
+
}
else
{
@@ -721,7 +682,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
lblStatus.setText(getString(R.string.status_activated));
- showHelpWizard ();
+
/*
@@ -998,7 +959,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants
};
boolean mIsBound = false;
- boolean hasRoot = false;
private void bindService ()
{
diff --git a/src/org/torproject/android/SettingsPreferences.java b/src/org/torproject/android/SettingsPreferences.java
index 2afb5837..45db4405 100644
--- a/src/org/torproject/android/SettingsPreferences.java
+++ b/src/org/torproject/android/SettingsPreferences.java
@@ -4,6 +4,7 @@
package org.torproject.android;
import org.torproject.android.service.TorServiceUtils;
+import org.torproject.android.service.TorTransProxy;
import android.content.Intent;
import android.os.Bundle;
@@ -28,7 +29,7 @@ public class SettingsPreferences
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
- hasRoot = TorServiceUtils.hasRoot();
+ hasRoot = TorTransProxy.hasRootAccess();
}
diff --git a/src/org/torproject/android/TorConstants.java b/src/org/torproject/android/TorConstants.java
index e48056cc..b6124492 100644
--- a/src/org/torproject/android/TorConstants.java
+++ b/src/org/torproject/android/TorConstants.java
@@ -46,6 +46,8 @@ public interface TorConstants {
public final static String PREF_REACHABLE_ADDRESSES = "pref_reachable_addresses";
public final static String PREF_REACHABLE_ADDRESSES_PORTS = "pref_reachable_addresses_ports";
public final static String PREF_TRANSPARENT = "pref_transparent";
+ public final static String PREF_TRANSPARENT_ALL = "pref_transparent_all";
+
}
diff --git a/src/org/torproject/android/WizardActivity.java b/src/org/torproject/android/WizardActivity.java
new file mode 100644
index 00000000..c7fb0eec
--- /dev/null
+++ b/src/org/torproject/android/WizardActivity.java
@@ -0,0 +1,71 @@
+package org.torproject.android;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+
+public class WizardActivity extends Activity implements OnClickListener
+{
+
+ protected void onCreate(Bundle savedInstanceState)
+ {
+
+ this.setContentView(R.layout.layout_help);
+
+ }
+
+
+
+ @Override
+ protected void onStart() {
+
+ super.onStart();
+
+
+ }
+
+
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ showStep1();
+ }
+
+
+
+ public void showStep1()
+ {
+ showDialog("Test","this is nathan's test","foo","bar",this);
+ }
+
+ private void showDialog (String title, String msg, String button1, String button2, OnClickListener ocListener)
+ {
+
+ new AlertDialog.Builder(this)
+ .setInverseBackgroundForced(true)
+ .setTitle(title)
+ .setMessage(msg)
+ .setNeutralButton(button1, ocListener)
+ .setNegativeButton(button2, ocListener)
+ .show();
+
+
+ }
+
+
+
+ @Override
+ public void onClick(DialogInterface arg0, int arg1) {
+
+
+ }
+
+
+
+}
diff --git a/src/org/torproject/android/WizardHelper.java b/src/org/torproject/android/WizardHelper.java
new file mode 100644
index 00000000..df6a67ff
--- /dev/null
+++ b/src/org/torproject/android/WizardHelper.java
@@ -0,0 +1,371 @@
+package org.torproject.android;
+
+import org.torproject.android.service.TorTransProxy;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.net.Uri;
+import android.preference.PreferenceManager;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.TextView;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.Toast;
+
+public class WizardHelper implements TorConstants {
+
+ private Context context;
+ private Dialog currentDialog;
+
+ public WizardHelper (Context context)
+ {
+ this.context = context;
+ }
+
+
+ public void showWizard ()
+ {
+ showWizardStep1();
+ }
+
+ public void showWizardStep1()
+ {
+
+
+ String title = context.getString(R.string.wizard_title);
+
+ LayoutInflater li = LayoutInflater.from(context);
+ View view = li.inflate(R.layout.layout_wizard_welcome, null);
+
+
+ showCustomDialog(title, view,context.getString(R.string.btn_next),context.getString(R.string.wizard_btn_tell_me_more),new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ if (which == DialogInterface.BUTTON_NEUTRAL)
+ {
+
+ showWizardStep2();
+ }
+ else if (which == DialogInterface.BUTTON_POSITIVE)
+ {
+ showAbout();
+ }
+
+ }
+ });
+ }
+
+ public void showWizardStep2()
+ {
+
+ String title = null;
+ String msg = null;
+
+
+ title = context.getString(R.string.wizard_permissions_root);
+ msg = context.getString(R.string.wizard_premissions_msg_root);
+
+
+ title = context.getString(R.string.wizard_permissions_stock);
+
+ LayoutInflater li = LayoutInflater.from(context);
+ View view = li.inflate(R.layout.layout_wizard_stock, null);
+
+ Button btn1 = (Button)view.findViewById(R.id.WizardRootButtonEnable);
+
+ btn1.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+
+
+ boolean hasRoot = TorTransProxy.hasRootAccess();
+
+ if (hasRoot)
+ {
+ currentDialog.dismiss();
+ showWizardStep2Root();
+ }
+ else
+ {
+ Toast.makeText(context, "Unable to get root access", Toast.LENGTH_LONG).show();
+ }
+ }
+ });
+
+ showCustomDialog(title, view,context.getString(R.string.btn_next),context.getString(R.string.btn_back),new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ if (which == DialogInterface.BUTTON_NEUTRAL)
+ {
+ showWizardTipsAndTricks();
+ }
+ else if (which == DialogInterface.BUTTON_POSITIVE)
+ {
+ showWizardStep1();
+ }
+
+ }
+ });
+
+
+ }
+
+ public void showWizardStep2Root()
+ {
+
+ String title = null;
+ String msg = null;
+
+
+
+ title = context.getString(R.string.wizard_permissions_root);
+ msg = context.getString(R.string.wizard_premissions_msg_root);
+
+
+
+ showDialog(title, msg,context.getString(R.string.btn_next),context.getString(R.string.btn_back),new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ if (which == DialogInterface.BUTTON_NEUTRAL)
+ {
+ showWizardRootConfigureTorification();
+ }
+ else if (which == DialogInterface.BUTTON_POSITIVE)
+ {
+ showWizardStep1();
+ }
+
+ }
+ });
+
+
+ }
+
+ public void showWizardTipsAndTricks()
+ {
+
+ String title = context.getString(R.string.wizard_tips_tricks);
+
+ LayoutInflater li = LayoutInflater.from(context);
+ View view = li.inflate(R.layout.layout_wizard_tips, null);
+
+ Button btn1 = (Button)view.findViewById(R.id.WizardRootButtonInstallOtrchat);
+
+ btn1.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+
+ String url = context.getString(R.string.otrchat_apk_url);
+ context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+
+ }
+ });
+
+ Button btn2 = (Button)view.findViewById(R.id.WizardRootButtonInstallOrweb);
+
+ btn2.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+
+ String url = context.getString(R.string.orweb_apk_url);
+ context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+
+ }
+ });
+
+ showCustomDialog(title, view,context.getString(R.string.btn_next),context.getString(R.string.btn_back),new DialogInterface.OnClickListener() {
+
+
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+
+ if (which == DialogInterface.BUTTON_NEUTRAL)
+ {
+ showWizardFinal();
+
+ }
+ else if (which == DialogInterface.BUTTON_POSITIVE)
+ {
+ showWizardStep2();
+ }
+
+ }
+ });
+ }
+
+ public void showWizardRootConfigureTorification()
+ {
+
+ LayoutInflater li = LayoutInflater.from(context);
+ View view = li.inflate(R.layout.layout_wizard_root, null);
+
+ CheckBox cb1 = (CheckBox)view.findViewById(R.id.WizardRootCheckBox01);
+ Button btn1 = (Button)view.findViewById(R.id.WizardRootButton01);
+
+ cb1.setOnCheckedChangeListener(new OnCheckedChangeListener (){
+
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ Editor pEdit = prefs.edit();
+
+ pEdit.putBoolean(PREF_TRANSPARENT, isChecked);
+ pEdit.putBoolean(PREF_TRANSPARENT_ALL, isChecked);
+
+ pEdit.commit();
+
+ //Button btn1 = (Button)buttonView.getParent().findViewById(R.id.WizardRootButton01);
+ //btn1.setEnabled(!isChecked);
+
+ }
+
+ });
+
+
+
+ btn1.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+
+ Editor pEdit = prefs.edit();
+ pEdit.putBoolean(PREF_TRANSPARENT, true);
+ pEdit.putBoolean(PREF_TRANSPARENT_ALL, false);
+ pEdit.commit();
+
+ context.startActivity(new Intent(context, AppManager.class));
+
+ }
+ });
+
+ showCustomDialog(context.getString(R.string.wizard_configure),view,context.getString(R.string.btn_next),context.getString(R.string.btn_back),new DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ dialog.dismiss();
+
+ if (which == DialogInterface.BUTTON_NEUTRAL)
+ {
+ showWizardTipsAndTricks();
+
+ }
+ else if (which == DialogInterface.BUTTON_POSITIVE)
+ {
+ showWizardStep2();
+ }
+
+ }
+ });
+
+
+
+ }
+
+
+ private void showWizardFinal ()
+ {
+ String title = null;
+ String msg = null;
+
+
+ title = context.getString(R.string.wizard_final);
+ msg = context.getString(R.string.wizard_final_msg);
+
+ new AlertDialog.Builder(context)
+ .setIcon(R.drawable.icon)
+ .setTitle(title)
+ .setPositiveButton(R.string.button_close, null)
+ .setMessage(msg)
+ .show();
+
+
+
+
+
+ }
+
+ private void showDialog (String title, String msg, String button1, String button2, DialogInterface.OnClickListener ocListener)
+ {
+
+// dialog.setContentView(R.layout.custom_dialog);
+
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setIcon(R.drawable.icon)
+ .setTitle(title)
+ .setMessage(msg)
+ .setNeutralButton(button1, ocListener)
+ .setPositiveButton(button2, ocListener);
+
+
+ currentDialog = builder.show();
+
+
+ }
+
+ private void showCustomDialog (String title, View view, String button1, String button2, DialogInterface.OnClickListener ocListener)
+ {
+
+ currentDialog = new AlertDialog.Builder(context)
+ .setIcon(R.drawable.icon)
+ .setTitle(title)
+ .setView(view)
+ .setNeutralButton(button1, ocListener)
+ .setPositiveButton(button2, ocListener)
+ .show();
+
+
+ }
+
+ private void showAbout ()
+ {
+
+ LayoutInflater li = LayoutInflater.from(context);
+ View view = li.inflate(R.layout.layout_about, null);
+ TextView versionName = (TextView)view.findViewById(R.id.versionName);
+ versionName.setText(R.string.app_version);
+
+ new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.button_about))
+ .setView(view)
+ .setNeutralButton(context.getString(R.string.btn_back), new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ showWizard();
+ }
+ })
+ .show();
+ }
+
+}
+
diff --git a/src/org/torproject/android/service/TorService.java b/src/org/torproject/android/service/TorService.java
index b8808da1..cd7a8ab9 100644
--- a/src/org/torproject/android/service/TorService.java
+++ b/src/org/torproject/android/service/TorService.java
@@ -49,7 +49,6 @@ public class TorService extends Service implements TorServiceConstants, Runnable
private ArrayList configBuffer = null;
- private boolean hasRoot = false;
private String appHome = null;
private String torBinaryPath = null;
@@ -61,8 +60,6 @@ public class TorService extends Service implements TorServiceConstants, Runnable
Log.i(TAG,"TorService: onCreate");
-
-
}
@@ -223,15 +220,25 @@ public class TorService extends Service implements TorServiceConstants, Runnable
sendCallbackMessage("Web proxy shutdown");
- killTorProcess ();
+ try
+ {
+ killTorProcess ();
- currentStatus = STATUS_READY;
+ currentStatus = STATUS_READY;
- showToolbarNotification (getString(R.string.status_disabled),R.drawable.tornotificationoff);
- sendCallbackMessage(getString(R.string.status_disabled));
+ showToolbarNotification (getString(R.string.status_disabled),R.drawable.tornotificationoff);
+ sendCallbackMessage(getString(R.string.status_disabled));
- setupTransProxy(false);
+ setupTransProxy(false);
+ }
+ catch (Exception e)
+ {
+ Log.i(TAG, "An error occured stopping Tor",e);
+ logNotice("An error occured stopping Tor: " + e.getMessage());
+ sendCallbackMessage("Something bad happened. Check the log");
+
+ }
}
@@ -257,7 +264,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
}
}
- private void killTorProcess ()
+ private void killTorProcess () throws Exception
{
if (conn != null)
@@ -318,6 +325,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
String APK_EXT = ".apk";
+ int MAX_TRIES = 10;
String buildPath = apkBase + TOR_APP_USERNAME + APK_EXT;
Log.i(TAG, "Checking APK location: " + buildPath);
@@ -327,7 +335,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable
if (fileApk.exists())
return fileApk.getAbsolutePath();
- for (int i = 0; i < 10; i++)
+ for (int i = 0; i < MAX_TRIES; i++)
{
buildPath = apkBase + TOR_APP_USERNAME + '-' + i + APK_EXT;
fileApk = new File(buildPath);
@@ -338,10 +346,33 @@ public class TorService extends Service implements TorServiceConstants, Runnable
return fileApk.getAbsolutePath();
}
+ String apkBaseExt = "/mnt/asec/" + TOR_APP_USERNAME;
+ String pkgFile = "/pkg.apk";
+
+ buildPath = apkBaseExt + pkgFile;
+ fileApk = new File(buildPath);
+
+ Log.i(TAG, "Checking external storage APK location: " + buildPath);
+
+ if (fileApk.exists())
+ return fileApk.getAbsolutePath();
+
+ for (int i = 0; i < MAX_TRIES; i++)
+ {
+ buildPath = apkBaseExt + '-' + i + pkgFile;
+ fileApk = new File(buildPath);
+
+ Log.i(TAG, "Checking external storage APK location: " + buildPath);
+
+ if (fileApk.exists())
+ return fileApk.getAbsolutePath();
+ }
+
+
return null;
}
- private boolean checkTorBinaries ()
+ private boolean checkTorBinaries () throws Exception
{
//android.os.Debug.waitForDebugger();
@@ -366,8 +397,8 @@ public class TorService extends Service implements TorServiceConstants, Runnable
return false;
}
- torBinaryPath = appHome + '/' + TOR_BINARY_ASSET_KEY;
- privoxyPath = appHome + '/' + PRIVOXY_ASSET_KEY;
+ torBinaryPath = appHome + TOR_BINARY_ASSET_KEY;
+ privoxyPath = appHome + PRIVOXY_ASSET_KEY;
boolean torBinaryExists = new File(torBinaryPath).exists();
boolean privoxyBinaryExists = new File(privoxyPath).exists();
@@ -401,15 +432,22 @@ public class TorService extends Service implements TorServiceConstants, Runnable
return false;
}
+ }
+ else
+ {
+ logNotice("Found Tor binary: " + torBinaryPath);
+
+ logNotice("Found prvoxy binary: " + privoxyPath);
+
}
StringBuilder log = new StringBuilder ();
- logNotice("Setting permission on Tor binary");
+ logNotice("(re)Setting permission on Tor binary");
String[] cmd1 = {SHELL_CMD_CHMOD + ' ' + CHMOD_EXE_VALUE + ' ' + torBinaryPath};
TorServiceUtils.doShellCommand(cmd1, log, false, true);
- logNotice("Setting permission on Privoxy binary");
+ logNotice("(re)Setting permission on Privoxy binary");
String[] cmd2 = {SHELL_CMD_CHMOD + ' ' + CHMOD_EXE_VALUE + ' ' + privoxyPath};
TorServiceUtils.doShellCommand(cmd2, log, false, true);
@@ -525,6 +563,9 @@ public class TorService extends Service implements TorServiceConstants, Runnable
private void runPrivoxyShellCmd () throws Exception
{
+
+ Log.i(TAG,"Starting privoxy process");
+
int privoxyProcId = TorServiceUtils.findProcessId(privoxyPath);
StringBuilder log = null;
@@ -538,9 +579,9 @@ public class TorService extends Service implements TorServiceConstants, Runnable
String privoxyConfigPath = appHome + PRIVOXYCONFIG_ASSET_KEY;
String[] cmds =
- { privoxyPath + " " + privoxyConfigPath };
+ { privoxyPath + " " + privoxyConfigPath + " &" };
- logNotice (cmds[0]);
+ logNotice (cmds[0]);
TorServiceUtils.doShellCommand(cmds, log, false, true);
@@ -845,13 +886,19 @@ public class TorService extends Service implements TorServiceConstants, Runnable
if (appHome == null)
{
- checkTorBinaries();
+ try
+ {
+ checkTorBinaries();
- findExistingProc ();
+ findExistingProc ();
- _torInstance = this;
-
- hasRoot = TorServiceUtils.hasRoot();
+ _torInstance = this;
+ }
+ catch (Exception e)
+ {
+ Log.i(TAG,"Unable to check for Tor binaries",e);
+ return null;
+ }
}
if (ITorService.class.getName().equals(intent.getAction())) {
@@ -1037,15 +1084,27 @@ public class TorService extends Service implements TorServiceConstants, Runnable
logNotice ("Transparent Proxying: " + enableTransparentProxy);
+ boolean hasRoot = TorTransProxy.hasRootAccess();
+
if (enabled)
{
-
+
if (hasRoot && enableTransparentProxy)
{
- TorTransProxy.setDNSProxying();
- TorTransProxy.setTransparentProxyingByApp(this,AppManager.getApps(this),transProxyAll);
+ try
+ {
+ TorTransProxy.setDNSProxying();
+ boolean success = TorTransProxy.setTransparentProxyingByApp(this,AppManager.getApps(this),transProxyAll);
+
+ logNotice ("TorTransProxy enabled: " + success);
+
+ } catch (Exception e) {
+ logNotice("WARNING: Error configuring transparenty proxying: " + e.getMessage());
+
+ Log.w(TAG, "error refreshing iptables: err=" + e.getMessage(), e);
+ }
}
else
diff --git a/src/org/torproject/android/service/TorServiceUtils.java b/src/org/torproject/android/service/TorServiceUtils.java
index ada14271..115f18a4 100644
--- a/src/org/torproject/android/service/TorServiceUtils.java
+++ b/src/org/torproject/android/service/TorServiceUtils.java
@@ -123,26 +123,16 @@ public class TorServiceUtils implements TorServiceConstants {
}
- public static boolean hasRoot ()
+ public static int doShellCommand(String[] cmds, StringBuilder log, boolean runAsRoot, boolean waitFor) throws Exception
{
- String[] cmds = {"exit 0"};
-
- int code = doShellCommand(cmds,null,true, true);
-
- return (code == 0);
- }
-
- public static int doShellCommand(String[] cmds, StringBuilder log, boolean isRoot, boolean waitFor)
- {
- Log.i(TAG,"executing shell cmds: " + cmds[0] + "; isRoot=" + isRoot);
+ Log.i(TAG,"executing shell cmds: " + cmds[0] + "; runAsRoot=" + runAsRoot);
Process proc = null;
int exitCode = -1;
- try {
- if (isRoot)
+ if (runAsRoot)
proc = Runtime.getRuntime().exec("su");
else
proc = Runtime.getRuntime().exec("sh");
@@ -163,10 +153,7 @@ public class TorServiceUtils implements TorServiceConstants {
if (waitFor)
{
- exitCode = proc.waitFor();
- log.append("process exit code: ");
- log.append(exitCode);
- log.append("\n");
+
final char buf[] = new char[10];
@@ -183,12 +170,15 @@ public class TorServiceUtils implements TorServiceConstants {
while ((read=reader.read(buf)) != -1) {
if (log != null) log.append(buf, 0, read);
}
+
+ exitCode = proc.waitFor();
+ log.append("process exit code: ");
+ log.append(exitCode);
+ log.append("\n");
+
Log.i(TAG,"command process exit value: " + exitCode);
}
-
- } catch (Exception e) {
- Log.w(TAG, "Error executing shell cmd: " + e.getMessage());
- }
+
return exitCode;
diff --git a/src/org/torproject/android/service/TorTransProxy.java b/src/org/torproject/android/service/TorTransProxy.java
index f2315823..8b2bc771 100644
--- a/src/org/torproject/android/service/TorTransProxy.java
+++ b/src/org/torproject/android/service/TorTransProxy.java
@@ -1,13 +1,8 @@
package org.torproject.android.service;
-import java.util.Iterator;
-import java.util.List;
-
import org.torproject.android.TorifiedApp;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.util.Log;
public class TorTransProxy {
@@ -23,29 +18,35 @@ public class TorTransProxy {
private final static String IPTABLES_ADD = " -A ";
//private final static String IPTABLES_DELETE = " -D "; //not deleting manually anymore - just calling a system wide flush of iptables rules
- private final static String IPTABLES_DROP_ALL = " -j DROP ";
- private static boolean hasRoot = false;
+ // private final static String IPTABLES_DROP_ALL = " -j DROP ";
/**
* Check if we have root access
* @return boolean true if we have root
*/
public static boolean hasRootAccess() {
- if (hasRoot) return true;
+
+
+ StringBuilder log = new StringBuilder();
+
try {
+
// Run an empty script just to check root access
- String[] cmd = {"exit 0"};
- if (TorServiceUtils.doShellCommand(cmd, null, true, true) == 0) {
- hasRoot = true;
+ String[] cmd = {"whoami"};
+ int exitCode = TorServiceUtils.doShellCommand(cmd, log, true, true);
+ if (exitCode == 0) {
+
return true;
}
+
} catch (Exception e) {
+ Log.w(TAG,"Error checking for root access: " + e.getMessage() ,e);
}
- Log.w(TAG, "Could not acquire root access.");
+ Log.w(TAG, "Could not acquire root access: " + log.toString());
return false;
}
- public static int setDNSProxying ()
+ public static int setDNSProxying () throws Exception
{
final StringBuilder log = new StringBuilder();
@@ -93,7 +94,8 @@ public class TorTransProxy {
}
}
- public static boolean setTransparentProxyingByApp(Context context, TorifiedApp[] apps, boolean forceAll) {
+ public static boolean setTransparentProxyingByApp(Context context, TorifiedApp[] apps, boolean forceAll) throws Exception
+ {
String command = null;
@@ -104,9 +106,6 @@ public class TorTransProxy {
StringBuilder res = new StringBuilder();
int code = -1;
- try {
-
-
for (int i = 0; i < apps.length; i++)
{
if (forceAll || apps[i].isTorified())
@@ -147,9 +146,7 @@ public class TorTransProxy {
String msg = res.toString();
Log.e(TAG, msg);
- } catch (Exception e) {
- Log.w(TAG, "error refreshing iptables: err=" + code + "; resp=" + res.toString(), e);
- }
+
return false;
}