Merge pull request #1473 from open-keychain/orbot-helper
Updated OrbotHelper with the latest NetCipher
This commit is contained in:
commit
3dbe0033dc
|
@ -371,32 +371,38 @@ public class ImportKeysListFragment extends ListFragment implements
|
|||
Runnable showOrbotDialog = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mParcelableProxy = ParcelableProxy
|
||||
.getForNoProxy();
|
||||
mShowingOrbotDialog = false;
|
||||
restartLoaders();
|
||||
}
|
||||
};
|
||||
OrbotHelper.DialogActions dialogActions =
|
||||
new OrbotHelper.DialogActions() {
|
||||
@Override
|
||||
public void onOrbotStarted() {
|
||||
mShowingOrbotDialog = false;
|
||||
restartLoaders();
|
||||
}
|
||||
|
||||
final Runnable dialogDismiss = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mShowingOrbotDialog = false;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void onNeutralButton() {
|
||||
mParcelableProxy = ParcelableProxy
|
||||
.getForNoProxy();
|
||||
mShowingOrbotDialog = false;
|
||||
restartLoaders();
|
||||
}
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(
|
||||
ignoreTor, dialogDismiss, getActivity())) {
|
||||
@Override
|
||||
public void onCancel() {
|
||||
mShowingOrbotDialog = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(dialogActions,
|
||||
getActivity())) {
|
||||
// looks like we didn't have to show the
|
||||
// dialog after all
|
||||
mShowingOrbotDialog = false;
|
||||
restartLoaders();
|
||||
}
|
||||
}
|
||||
};
|
||||
new Handler().post(showOrbotDialog );
|
||||
new Handler().post(showOrbotDialog);
|
||||
mShowingOrbotDialog = true;
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -22,18 +22,20 @@ import android.content.Intent;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
/**
|
||||
* Simply encapsulates a dialog. If orbot is not installed, it shows an install dialog, else a
|
||||
* dialog to enable orbot.
|
||||
*/
|
||||
public class OrbotRequiredDialogActivity extends FragmentActivity {
|
||||
public class OrbotRequiredDialogActivity extends FragmentActivity
|
||||
implements OrbotHelper.DialogActions {
|
||||
|
||||
// if suppplied and true will start Orbot directly without showing dialog
|
||||
public static final String EXTRA_START_ORBOT = "start_orbot";
|
||||
|
||||
// to provide any previous crypto input into which proxy preference is merged
|
||||
public static final String EXTRA_CRYPTO_INPUT = "extra_crypto_input";
|
||||
|
@ -45,47 +47,68 @@ public class OrbotRequiredDialogActivity extends FragmentActivity {
|
|||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
|
||||
if (mCryptoInputParcel == null) {
|
||||
// compatibility with usages that don't use a CryptoInputParcel
|
||||
mCryptoInputParcel = new CryptoInputParcel();
|
||||
}
|
||||
showDialog();
|
||||
|
||||
boolean startOrbotDirect = getIntent().getBooleanExtra(EXTRA_START_ORBOT, false);
|
||||
if (startOrbotDirect) {
|
||||
OrbotHelper.bestPossibleOrbotStart(this, this);
|
||||
} else {
|
||||
showDialog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an install or start orbot dialog depending on orbot's presence and state
|
||||
* Displays an install or start orbot dialog (or silent orbot start) depending on orbot's
|
||||
* presence and state
|
||||
*/
|
||||
public void showDialog() {
|
||||
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
||||
public void run() {
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Intent intent = new Intent();
|
||||
mCryptoInputParcel.addParcelableProxy(ParcelableProxy.getForNoProxy());
|
||||
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
Runnable dialogDismissed = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, dialogDismissed,
|
||||
Preferences.getPreferences(OrbotRequiredDialogActivity.this)
|
||||
.getProxyPrefs(),
|
||||
if (OrbotHelper.putOrbotInRequiredState(OrbotRequiredDialogActivity.this,
|
||||
OrbotRequiredDialogActivity.this)) {
|
||||
// no action required after all
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
|
||||
setResult(RESULT_OK, intent);
|
||||
onOrbotStarted();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
switch (requestCode) {
|
||||
case OrbotHelper.START_TOR_RESULT: {
|
||||
onOrbotStarted(); // assumption that orbot was started, no way to tell for sure
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOrbotStarted() {
|
||||
Intent intent = new Intent();
|
||||
// send back unmodified CryptoInputParcel for a retry
|
||||
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNeutralButton() {
|
||||
Intent intent = new Intent();
|
||||
mCryptoInputParcel.addParcelableProxy(ParcelableProxy.getForNoProxy());
|
||||
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -250,9 +250,6 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
|
|||
public class KeyserverListAdapter extends RecyclerView.Adapter<KeyserverListAdapter.ViewHolder>
|
||||
implements ItemTouchHelperAdapter {
|
||||
|
||||
// to update the ViewHolder associated with first item, for when an item is deleted
|
||||
private ViewHolder mFirstItem;
|
||||
|
||||
private final List<String> mKeyservers;
|
||||
|
||||
public KeyserverListAdapter(List<String> keyservers) {
|
||||
|
@ -263,15 +260,11 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
|
|||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.settings_keyserver_item, parent, false);
|
||||
ViewHolder viewHolder = new ViewHolder(view);
|
||||
return viewHolder;
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||
if (position == 0) {
|
||||
mFirstItem = holder;
|
||||
}
|
||||
holder.keyserverUrl.setText(mKeyservers.get(position));
|
||||
|
||||
// Start a drag whenever the handle view it touched
|
||||
|
|
|
@ -200,19 +200,30 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
|||
mStartSearch.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity())
|
||||
.getProxyPrefs();
|
||||
final Preferences.ProxyPrefs proxyPrefs =
|
||||
Preferences.getPreferences(getActivity()).getProxyPrefs();
|
||||
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() {
|
||||
@Override
|
||||
public void run() {
|
||||
public void onOrbotStarted() {
|
||||
mStartSearch.setEnabled(false);
|
||||
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNeutralButton() {
|
||||
mStartSearch.setEnabled(false);
|
||||
new DescribeKey(ParcelableProxy.getForNoProxy())
|
||||
.execute(fingerprint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
|
||||
getActivity())) {
|
||||
if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) {
|
||||
mStartSearch.setEnabled(false);
|
||||
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ import android.widget.TextView.OnEditorActionListener;
|
|||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
|
@ -216,15 +217,25 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
|
|||
if (mVerifyKeyserverCheckBox.isChecked()) {
|
||||
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity())
|
||||
.getProxyPrefs();
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() {
|
||||
@Override
|
||||
public void run() {
|
||||
public void onOrbotStarted() {
|
||||
verifyConnection(keyserverUrl,
|
||||
proxyPrefs.parcelableProxy.getProxy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNeutralButton() {
|
||||
verifyConnection(keyserverUrl, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancel() {
|
||||
// do nothing
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
|
||||
getActivity())) {
|
||||
if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) {
|
||||
verifyConnection(keyserverUrl, proxyPrefs.parcelableProxy.getProxy());
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -18,15 +18,19 @@
|
|||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
@ -43,8 +47,11 @@ public class OrbotStartDialogFragment extends DialogFragment {
|
|||
private static final String ARG_MESSAGE = "message";
|
||||
private static final String ARG_MIDDLE_BUTTON = "middleButton";
|
||||
|
||||
private static final int ORBOT_REQUEST_CODE = 1;
|
||||
|
||||
public static final int MESSAGE_MIDDLE_BUTTON = 1;
|
||||
public static final int MESSAGE_DIALOG_DISMISSED = 2; // for either cancel or enable pressed
|
||||
public static final int MESSAGE_DIALOG_CANCELLED = 2; // for either cancel or enable pressed
|
||||
public static final int MESSAGE_ORBOT_STARTED = 3; // for either cancel or enable pressed
|
||||
|
||||
public static OrbotStartDialogFragment newInstance(Messenger messenger, int title, int message, int middleButton) {
|
||||
Bundle args = new Bundle();
|
||||
|
@ -59,6 +66,7 @@ public class OrbotStartDialogFragment extends DialogFragment {
|
|||
return fragment;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
|
@ -77,7 +85,7 @@ public class OrbotStartDialogFragment extends DialogFragment {
|
|||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = MESSAGE_DIALOG_DISMISSED;
|
||||
msg.what = MESSAGE_DIALOG_CANCELLED;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
|
@ -89,23 +97,6 @@ public class OrbotStartDialogFragment extends DialogFragment {
|
|||
}
|
||||
});
|
||||
|
||||
builder.setPositiveButton(R.string.orbot_start_dialog_start, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
getActivity().startActivityForResult(OrbotHelper.getOrbotStartIntent(), 1);
|
||||
|
||||
Message msg = Message.obtain();
|
||||
msg.what = MESSAGE_DIALOG_DISMISSED;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNeutralButton(middleButton, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
@ -121,6 +112,52 @@ public class OrbotStartDialogFragment extends DialogFragment {
|
|||
}
|
||||
});
|
||||
|
||||
builder.setPositiveButton(R.string.orbot_start_dialog_start, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// actual onClick defined in onStart, this is just to make the button appear
|
||||
}
|
||||
});
|
||||
|
||||
return builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
//super.onStart() is where dialog.show() is actually called on the underlying dialog,
|
||||
// so we have to do it after this point
|
||||
AlertDialog d = (AlertDialog) getDialog();
|
||||
if (d != null) {
|
||||
Button positiveButton = d.getButton(Dialog.BUTTON_POSITIVE);
|
||||
positiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
startActivityForResult(OrbotHelper.getShowOrbotStartIntent(),
|
||||
ORBOT_REQUEST_CODE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == ORBOT_REQUEST_CODE) {
|
||||
// assume Orbot was started
|
||||
final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||
|
||||
Message msg = Message.obtain();
|
||||
msg.what = MESSAGE_ORBOT_STARTED;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.dialog;
|
|||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.os.Messenger;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
|
||||
import org.sufficientlysecure.keychain.ui.util.InstallDialogFragmentHelper;
|
||||
|
@ -34,7 +35,7 @@ public class SupportInstallDialogFragment extends DialogFragment {
|
|||
* and "Cancel") and an optional third button. Callbacks are provided only for the middle button, if set.
|
||||
*
|
||||
* @param messenger required only for callback from middle button if it has been set
|
||||
* @param title
|
||||
* @param title xml resource for title of the install dialog
|
||||
* @param message content of dialog
|
||||
* @param packageToInstall package name of application to install
|
||||
* @param middleButton if not null, adds a third button to the app with a call back
|
||||
|
@ -57,16 +58,17 @@ public class SupportInstallDialogFragment extends DialogFragment {
|
|||
/**
|
||||
* To create a DialogFragment with only two buttons
|
||||
*
|
||||
* @param title
|
||||
* @param message
|
||||
* @param packageToInstall
|
||||
* @param title xml string resource for title of the dialog
|
||||
* @param message xml string resource to display as dialog body
|
||||
* @param packageToInstall name of package to install
|
||||
* @return
|
||||
*/
|
||||
public static SupportInstallDialogFragment newInstance(int title, int message,
|
||||
String packageToInstall) {
|
||||
String packageToInstall) {
|
||||
return newInstance(null, title, message, packageToInstall, -1, false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
|
|
|
@ -51,8 +51,11 @@ public class ParcelableProxy implements Parcelable {
|
|||
if (mProxyHost == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Proxy(mProxyType, new InetSocketAddress(mProxyHost, mProxyPort));
|
||||
/*
|
||||
* InetSocketAddress.createUnresolved so we can use this method even in the main thread
|
||||
* (no network call)
|
||||
*/
|
||||
return new Proxy(mProxyType, InetSocketAddress.createUnresolved(mProxyHost, mProxyPort));
|
||||
}
|
||||
|
||||
protected ParcelableProxy(Parcel in) {
|
||||
|
|
|
@ -49,69 +49,219 @@
|
|||
|
||||
package org.sufficientlysecure.keychain.util.orbot;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.SupportInstallDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.OrbotStartDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.PreferenceInstallDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This class is taken from the NetCipher library: https://github.com/guardianproject/NetCipher/
|
||||
*/
|
||||
public class OrbotHelper {
|
||||
|
||||
public interface DialogActions {
|
||||
void onOrbotStarted();
|
||||
|
||||
void onNeutralButton();
|
||||
|
||||
void onCancel();
|
||||
}
|
||||
|
||||
private final static int REQUEST_CODE_STATUS = 100;
|
||||
|
||||
public final static String ORBOT_PACKAGE_NAME = "org.torproject.android";
|
||||
public final static String TOR_BIN_PATH = "/data/data/org.torproject.android/app_bin/tor";
|
||||
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
|
||||
*/
|
||||
@SuppressWarnings("unused") // we might use this later, sent by Orbot
|
||||
public final static String STATUS_OFF = "OFF";
|
||||
/**
|
||||
* All tor-related services and daemons have completed starting
|
||||
*/
|
||||
public final static String STATUS_ON = "ON";
|
||||
@SuppressWarnings("unused") // we might use this later, sent by Orbot
|
||||
public final static String STATUS_STARTING = "STARTING";
|
||||
@SuppressWarnings("unused") // we might use this later, sent by Orbot
|
||||
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";
|
||||
/**
|
||||
* request code used to start tor
|
||||
*/
|
||||
public final static int START_TOR_RESULT = 0x9234;
|
||||
|
||||
public static boolean isOrbotRunning() {
|
||||
int procId = TorServiceUtils.findProcessId(TOR_BIN_PATH);
|
||||
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(ORBOT_PACKAGE_NAME, context);
|
||||
return isAppInstalled(context, ORBOT_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
private static boolean isAppInstalled(String uri, Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
boolean installed;
|
||||
private static boolean isAppInstalled(Context context, String uri) {
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
|
||||
installed = true;
|
||||
return true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
installed = false;
|
||||
return false;
|
||||
}
|
||||
return installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {@code 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* hack to get around the fact that PreferenceActivity still supports only android.app.DialogFragment
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static android.app.DialogFragment getPreferenceInstallDialogFragment() {
|
||||
return PreferenceInstallDialogFragment.newInstance(R.string.orbot_install_dialog_title,
|
||||
R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
public static DialogFragment getInstallDialogFragment() {
|
||||
return SupportInstallDialogFragment.newInstance(R.string.orbot_install_dialog_title,
|
||||
R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
public static DialogFragment getInstallDialogFragmentWithThirdButton(Messenger messenger, int middleButton) {
|
||||
return SupportInstallDialogFragment.newInstance(messenger, R.string.orbot_install_dialog_title,
|
||||
R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME, middleButton, true);
|
||||
|
@ -123,13 +273,6 @@ public class OrbotHelper {
|
|||
middleButton);
|
||||
}
|
||||
|
||||
public static Intent getOrbotStartIntent() {
|
||||
Intent intent = new Intent(ACTION_START_TOR);
|
||||
intent.setPackage(ORBOT_PACKAGE_NAME);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks preferences to see if Orbot is required, and if yes, if it is installed and running
|
||||
*
|
||||
|
@ -141,26 +284,23 @@ public class OrbotHelper {
|
|||
Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(context).getProxyPrefs();
|
||||
if (!proxyPrefs.torEnabled) {
|
||||
return true;
|
||||
} else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning()) {
|
||||
} else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning(context)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if Tor is enabled and if it is, that Orbot is installed and runnign. Generates appropriate dialogs.
|
||||
* checks if Tor is enabled and if it is, that Orbot is installed and running. Generates appropriate dialogs.
|
||||
*
|
||||
* @param middleButton resourceId of string to display as the middle button of install and enable dialogs
|
||||
* @param middleButtonRunnable runnable to be executed if the user clicks on the middle button
|
||||
* @param proxyPrefs
|
||||
* @param fragmentActivity
|
||||
* @param middleButton resourceId of string to display as the middle button of install and enable dialogs
|
||||
* @param proxyPrefs proxy preferences used to determine if Tor is required to be started
|
||||
* @return true if Tor is not enabled or Tor is enabled and Orbot is installed and running, else false
|
||||
*/
|
||||
public static boolean putOrbotInRequiredState(final int middleButton,
|
||||
final Runnable middleButtonRunnable,
|
||||
final Runnable dialogDismissRunnable,
|
||||
final DialogActions dialogActions,
|
||||
Preferences.ProxyPrefs proxyPrefs,
|
||||
FragmentActivity fragmentActivity) {
|
||||
final FragmentActivity fragmentActivity) {
|
||||
|
||||
if (!proxyPrefs.torEnabled) {
|
||||
return true;
|
||||
|
@ -172,10 +312,12 @@ public class OrbotHelper {
|
|||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case SupportInstallDialogFragment.MESSAGE_MIDDLE_CLICKED:
|
||||
middleButtonRunnable.run();
|
||||
dialogActions.onNeutralButton();
|
||||
break;
|
||||
case SupportInstallDialogFragment.MESSAGE_DIALOG_DISMISSED:
|
||||
dialogDismissRunnable.run();
|
||||
// both install and cancel buttons mean we don't go ahead with an
|
||||
// operation, so it's okay to cancel
|
||||
dialogActions.onCancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -187,25 +329,39 @@ public class OrbotHelper {
|
|||
).show(fragmentActivity.getSupportFragmentManager(), "OrbotHelperOrbotInstallDialog");
|
||||
|
||||
return false;
|
||||
} else if (!OrbotHelper.isOrbotRunning()) {
|
||||
} else if (!OrbotHelper.isOrbotRunning(fragmentActivity)) {
|
||||
|
||||
Handler ignoreTorHandler = new Handler() {
|
||||
final Handler dialogHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case OrbotStartDialogFragment.MESSAGE_MIDDLE_BUTTON:
|
||||
middleButtonRunnable.run();
|
||||
dialogActions.onNeutralButton();
|
||||
break;
|
||||
case OrbotStartDialogFragment.MESSAGE_DIALOG_DISMISSED:
|
||||
dialogDismissRunnable.run();
|
||||
case OrbotStartDialogFragment.MESSAGE_DIALOG_CANCELLED:
|
||||
dialogActions.onCancel();
|
||||
break;
|
||||
case OrbotStartDialogFragment.MESSAGE_ORBOT_STARTED:
|
||||
dialogActions.onOrbotStarted();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OrbotHelper.getOrbotStartDialogFragment(new Messenger(ignoreTorHandler),
|
||||
middleButton)
|
||||
.show(fragmentActivity.getSupportFragmentManager(), "OrbotHelperOrbotStartDialog");
|
||||
new SilentStartManager() {
|
||||
|
||||
@Override
|
||||
protected void onOrbotStarted() {
|
||||
dialogActions.onOrbotStarted();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSilentStartDisabled() {
|
||||
getOrbotStartDialogFragment(new Messenger(dialogHandler), middleButton)
|
||||
.show(fragmentActivity.getSupportFragmentManager(),
|
||||
"OrbotHelperOrbotStartDialog");
|
||||
}
|
||||
}.startOrbotAndListen(fragmentActivity);
|
||||
|
||||
return false;
|
||||
} else {
|
||||
|
@ -213,40 +369,83 @@ public class OrbotHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean putOrbotInRequiredState(final int middleButton,
|
||||
final Runnable middleButtonRunnable,
|
||||
Preferences.ProxyPrefs proxyPrefs,
|
||||
public static boolean putOrbotInRequiredState(DialogActions dialogActions,
|
||||
FragmentActivity fragmentActivity) {
|
||||
Runnable emptyRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
};
|
||||
return putOrbotInRequiredState(middleButton, middleButtonRunnable, emptyRunnable,
|
||||
proxyPrefs, fragmentActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* generates a standard Orbot install/enable dialog if necessary, based on proxy settings in
|
||||
* preferences
|
||||
*
|
||||
* @param ignoreTorRunnable run when the "Ignore Tor" button is pressed
|
||||
* @param fragmentActivity used to start the activ
|
||||
* @return
|
||||
*/
|
||||
public static boolean putOrbotInRequiredState(Runnable ignoreTorRunnable,
|
||||
FragmentActivity fragmentActivity) {
|
||||
return putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTorRunnable,
|
||||
Preferences.getPreferences(fragmentActivity).getProxyPrefs(), fragmentActivity);
|
||||
}
|
||||
|
||||
public static boolean putOrbotInRequiredState(Runnable ignoreTorRunnable,
|
||||
Runnable dismissDialogRunnable,
|
||||
FragmentActivity fragmentActivity) {
|
||||
return putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTorRunnable,
|
||||
dismissDialogRunnable,
|
||||
return putOrbotInRequiredState(R.string.orbot_ignore_tor,
|
||||
dialogActions,
|
||||
Preferences.getPreferences(fragmentActivity).getProxyPrefs(),
|
||||
fragmentActivity);
|
||||
}
|
||||
|
||||
/**
|
||||
* will attempt a silent start, which if disabled will fallback to the
|
||||
* {@link #requestShowOrbotStart(Activity) requestShowOrbotStart} method, which returns the
|
||||
* result in {@link Activity#onActivityResult(int requestCode, int resultCode, Intent data)}
|
||||
* with a {@code requestCode} of {@code START_TOR_RESULT}, which will have to be implemented by
|
||||
* activities wishing to respond to a change in Orbot state.
|
||||
*/
|
||||
public static void bestPossibleOrbotStart(final DialogActions dialogActions,
|
||||
final Activity activity) {
|
||||
new SilentStartManager() {
|
||||
|
||||
@Override
|
||||
protected void onOrbotStarted() {
|
||||
dialogActions.onOrbotStarted();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSilentStartDisabled() {
|
||||
requestShowOrbotStart(activity);
|
||||
}
|
||||
}.startOrbotAndListen(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* base class for listening to silent orbot starts. Also handles display of progress dialog.
|
||||
*/
|
||||
private static abstract class SilentStartManager {
|
||||
|
||||
private ProgressDialog mProgressDialog;
|
||||
|
||||
public void startOrbotAndListen(Context context) {
|
||||
mProgressDialog = new ProgressDialog(ThemeChanger.getDialogThemeWrapper(context));
|
||||
mProgressDialog.setMessage(context.getString(R.string.progress_starting_orbot));
|
||||
mProgressDialog.setCancelable(false);
|
||||
mProgressDialog.show();
|
||||
|
||||
BroadcastReceiver receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
switch (intent.getStringExtra(OrbotHelper.EXTRA_STATUS)) {
|
||||
case OrbotHelper.STATUS_ON:
|
||||
context.unregisterReceiver(this);
|
||||
// generally Orbot starts working a little after this status is received
|
||||
new Handler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mProgressDialog.dismiss();
|
||||
onOrbotStarted();
|
||||
}
|
||||
}, 1000);
|
||||
break;
|
||||
case OrbotHelper.STATUS_STARTS_DISABLED:
|
||||
context.unregisterReceiver(this);
|
||||
mProgressDialog.dismiss();
|
||||
onSilentStartDisabled();
|
||||
break;
|
||||
|
||||
}
|
||||
Log.d(Constants.TAG, "Orbot silent start broadcast: " +
|
||||
intent.getStringExtra(OrbotHelper.EXTRA_STATUS));
|
||||
}
|
||||
};
|
||||
context.registerReceiver(receiver, new IntentFilter(OrbotHelper.ACTION_STATUS));
|
||||
|
||||
requestStartTor(context);
|
||||
}
|
||||
|
||||
protected abstract void onOrbotStarted();
|
||||
|
||||
protected abstract void onSilentStartDisabled();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,8 @@
|
|||
|
||||
package org.sufficientlysecure.keychain.util.orbot;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import android.content.Context;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
@ -62,24 +63,27 @@ import java.util.StringTokenizer;
|
|||
* This class is taken from the NetCipher library: https://github.com/guardianproject/NetCipher/
|
||||
*/
|
||||
public class TorServiceUtils {
|
||||
// various console cmds
|
||||
|
||||
private final static String TAG = "TorUtils";
|
||||
|
||||
public final static String SHELL_CMD_PS = "ps";
|
||||
public final static String SHELL_CMD_PIDOF = "pidof";
|
||||
|
||||
public static int findProcessId(String command) {
|
||||
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) {
|
||||
if (procId == -1)
|
||||
procId = findProcessIdWithPS(command);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
procId = findProcessIdWithPS(command);
|
||||
} catch (Exception e2) {
|
||||
Log.e(Constants.TAG, "Unable to get proc id for command: " + URLEncoder.encode(command), e2);
|
||||
Log.e(TAG, "Unable to get proc id for command: " + URLEncoder.encode(command), e2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,7 +94,9 @@ public class TorServiceUtils {
|
|||
public static int findProcessIdWithPidOf(String command) throws Exception {
|
||||
|
||||
int procId = -1;
|
||||
|
||||
Runtime r = Runtime.getRuntime();
|
||||
|
||||
Process procPs;
|
||||
|
||||
String baseName = new File(command).getName();
|
||||
|
@ -115,18 +121,23 @@ public class TorServiceUtils {
|
|||
}
|
||||
|
||||
return procId;
|
||||
|
||||
}
|
||||
|
||||
// use 'ps' command
|
||||
public static int findProcessIdWithPS(String command) throws Exception {
|
||||
|
||||
int procId = -1;
|
||||
|
||||
Runtime r = Runtime.getRuntime();
|
||||
|
||||
Process procPs;
|
||||
|
||||
procPs = r.exec(SHELL_CMD_PS);
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains(' ' + command)) {
|
||||
|
||||
|
@ -140,5 +151,6 @@ public class TorServiceUtils {
|
|||
}
|
||||
|
||||
return procId;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -421,6 +421,8 @@
|
|||
|
||||
<string name="progress_verifying_keyserver_url">"verifying keyserver…"</string>
|
||||
|
||||
<string name="progress_starting_orbot">"Starting Orbot…"</string>
|
||||
|
||||
<!-- action strings -->
|
||||
<string name="hint_cloud_search_hint">"Search via Name, Email…"</string>
|
||||
|
||||
|
|
Loading…
Reference in a new issue