re-do the AndroidSAMSecureSession so it doesn't require me to ask permission to draw over other apps. Permission to draw over other apps gives apps the power to take screenshots, so it's a special permission on Android.

This commit is contained in:
idk
2022-05-28 02:12:23 -04:00
parent cd12d84d47
commit 1f434d8a05
12 changed files with 249 additions and 100 deletions

4
.gitignore vendored
View File

@ -1,2 +1,4 @@
etc/docker.signing.properties
signing.properties
signing.properties.idea
.idea/
.gradle/

View File

@ -7,7 +7,7 @@ repositories {
android {
compileSdkVersion 28
defaultConfig {
versionCode 4745266
versionCode 4745267
versionName "$I2P_VERSION"
minSdkVersion 14
targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION as String)

View File

@ -55,6 +55,10 @@
<action android:name="net.i2p.android.router.START_I2P" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="net.i2p.android.router.service.APPROVE_SAM" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- Addressbook filters -->
<intent-filter>

View File

@ -19,6 +19,7 @@ import net.i2p.android.router.MainFragment;
import net.i2p.android.router.R;
import net.i2p.android.router.SettingsActivity;
import net.i2p.android.router.addressbook.AddressbookContainer;
import net.i2p.android.router.service.AndroidSAMSecureSession;
import net.i2p.android.router.service.RouterService;
import net.i2p.android.router.service.State;
import net.i2p.android.router.util.Connectivity;
@ -134,6 +135,7 @@ public class I2PActivity extends I2PActivityBase implements
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
handleIntents();
}
@ -143,17 +145,29 @@ public class I2PActivity extends I2PActivityBase implements
Intent intent = getIntent();
String action = intent.getAction();
Util.d("handleIntent: intent=" + intent.toString());
if (action == null)
return;
Util.d("handleIntent: action=" + action);
Bundle extra = intent.getExtras();
if (extra != null)
Util.d("handleIntent extra=" + extra.toString());
switch (action) {
case "net.i2p.android.router.START_I2P":
if (mViewPager.getCurrentItem() != 0)
mViewPager.setCurrentItem(0, false);
autoStart();
break;
case "net.i2p.android.router.service.APPROVE_SAM":
Util.w("Affirmed SAM Connection");
String ID = extra.getString("ID");
Util.d("SAM ID was: " + ID);
AndroidSAMSecureSession.affirmResult(ID);
break;
case Intent.ACTION_PICK:
mViewPager.setFixedPage(2, R.string.select_an_address);
break;

View File

@ -1,104 +1,137 @@
package net.i2p.android.router.service;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.DialogInterface;
import android.content.res.AssetManager;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.RequiresApi;
import android.support.v4.content.ContextCompat;
import android.widget.Toast;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import net.i2p.android.I2PActivity;
import net.i2p.android.I2PActivityBase;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Notifications;
import net.i2p.android.router.util.Util;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.sam.*;
import net.i2p.sam.SAMException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* Implements SAMSecureSessionInterface on Android platforms using a Toast
* as the interactive channel.
*
*
* @since 1.8.0
*/
public class AndroidSAMSecureSession implements SAMSecureSessionInterface {
public class AndroidSAMSecureSession extends AppCompatActivity implements SAMSecureSessionInterface {
private static final String URI_I2P_ANDROID = "net.i2p.android";
private final Context mCtx;
private final RouterService _routerService;
private final StatusBar _statusBar;
static private Map<String, Integer> results = new HashMap<>();
public AndroidSAMSecureSession(Context ctx) {
mCtx = ctx;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
private class SAMSecureRunnable implements Runnable {
private int result = -1;
public static void affirmResult(String clientId) {
Util.d("Affirmed result for: " + clientId);
results.put(clientId, 1);
}
@Override
public void run() {
AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext());
public AndroidSAMSecureSession(Context ctx, RouterService rCtx, StatusBar statusBar) {
mCtx = ctx;
_routerService = rCtx;
_statusBar = statusBar;
}
builder.setTitle(mCtx.getResources().getString(R.string.settings_confirm_sam));
builder.setMessage(mCtx.getResources().getString(R.string.settings_confirm_sam));
builder.setPositiveButton(mCtx.getResources().getString(R.string.settings_confirm_allow_sam), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result = 1;
dialog.dismiss();
}
});
builder.setNegativeButton(mCtx.getResources().getString(R.string.settings_confirm_deny_sam), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result = 0;
dialog.dismiss();
}
});
AlertDialog alert = builder.create();
alert.show();
}
private synchronized void waitForResult() {
for (int i=0;i<60;i++) {
if (result != - 1)
break;
Util.i("Waiting on user to approve SAM connection");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Util.e("SAMSecureSession Error", e);
}
private void waitForResult(String clientId) {
for (int i=0;i<60;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Util.e("SAMSecureSession Error", e);
}
Integer result = results.get(clientId);
if (result == null)
continue;
if (result != -1)
break;
Util.d("Waiting on user to approve SAM connection for: "+clientId);
}
}
private boolean isResult() {
if (result == 0)
return false;
if (result == 1)
return true;
private boolean isResult(String clientId) {
waitForResult(clientId);
final Integer finResult = results.get(clientId);
if (finResult == null)
return false;
_routerService.updateStatus();
if (finResult == 0) {
Util.w("SAM connection cancelled by user request");
return false;
}
public boolean checkResult() {
waitForResult();
return isResult();
if (finResult == 1) {
Util.w("SAM connection allowed by user action");
return true;
}
Util.w("SAM connection denied by timeout.");
return false;
}
public Context getApplicationContext() {
return mCtx.getApplicationContext();
private boolean checkResult(String clientId) {
Intent intent = new Intent("net.i2p.android.router.service.APPROVE_SAM", null, mCtx, I2PActivity.class );
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setPackage(URI_I2P_ANDROID);
Bundle bundle = new Bundle();
bundle.putBoolean("approveSAMConnection", true);
bundle.putString("ID", clientId);
intent.putExtras(bundle);
PendingIntent pendingIntent = PendingIntent.getActivity(
mCtx, 7656,
intent,
PendingIntent.FLAG_UPDATE_CURRENT, bundle);
String dlgText = mCtx.getString(R.string.settings_confirm_sam) + "\n";//""</br>";
dlgText += mCtx.getString(R.string.settings_confirm_sam_id) + clientId + "\n";//""</br>";
dlgText += mCtx.getString(R.string.settings_confirm_allow_sam) + "\n";//""</br>";
dlgText += mCtx.getString(R.string.settings_confirm_deny_sam) + "\n";//""</br>";
_statusBar.replaceIntent(StatusBar.ICON_ACTIVE, dlgText, pendingIntent);
return isResult(clientId);
}
@Override
public boolean approveOrDenySecureSession(Properties i2cpProps, Properties props) throws SAMException {
Handler handler = new Handler(Looper.getMainLooper());
SAMSecureRunnable ssr = new SAMSecureRunnable();
handler.post(ssr);
return ssr.checkResult();
String ID = props.getProperty("USER");
if (ID == null)
ID = i2cpProps.getProperty("inbound.nickname");
if (ID == null)
ID = i2cpProps.getProperty("outbound.nickname");
if (ID == null)
ID = props.getProperty("ID");
if (ID == null) {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
ID = "No_ID_Present";
}
if (messageDigest != null) {
String combinedProps = i2cpProps.toString() + props.toString();
messageDigest.update(combinedProps.getBytes());
ID = messageDigest.digest().toString();
}else{
ID = "No_ID_Present";
}
}
return checkResult(ID);
}
}

View File

@ -1,19 +1,23 @@
package net.i2p.android.router.service;
import android.app.Notification;
import android.content.Context;
//import net.i2p.BOB.BOB;
import net.i2p.I2PAppContext;
import net.i2p.addressbook.DaemonThread;
import android.content.Intent;
import android.os.Looper;
import android.preference.PreferenceManager;
import net.i2p.android.apps.NewsFetcher;
import net.i2p.android.router.service.AndroidSAMSecureSession;
import net.i2p.android.router.util.Notifications;
import net.i2p.android.router.util.Util;
import net.i2p.i2ptunnel.TunnelControllerGroup;
import net.i2p.router.Job;
import net.i2p.router.JobImpl;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.startup.RouterAppManager;
import net.i2p.util.I2PAppThread;
import net.i2p.sam.SAMBridge;
import net.i2p.sam.SAMSecureSessionInterface;
@ -45,19 +49,41 @@ import java.util.Properties;
class LoadClientsJob extends JobImpl {
private final Context mCtx;
private final RouterService _routerService;
private final Notifications _notif;
private DaemonThread _addressbook;
public SAMBridge SAM_BRIDGE;
private final StatusBar _statusBar;
// private BOB _bob;
/** this is the delay to load (and start) the clients. */
private static final long LOAD_DELAY = 60 * 1000;
public LoadClientsJob(Context ctx, RouterContext rCtx, Notifications notif, StatusBar status) {
super(rCtx);
mCtx = ctx;
_routerService = null;
_notif = notif;
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
_statusBar = status;
}
public LoadClientsJob(Context ctx, RouterContext rCtx, RouterService rSvc, Notifications notif, StatusBar status) {
super(rCtx);
mCtx = ctx;
_routerService = rSvc;
_notif = notif;
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
_statusBar = status;
}
public LoadClientsJob(Context ctx, RouterContext rCtx, Notifications notif) {
super(rCtx);
mCtx = ctx;
_routerService = null;
_notif = notif;
getTiming().setStartAfter(getContext().clock().now() + LOAD_DELAY);
_statusBar = null;
}
public String getName() {
@ -153,7 +179,9 @@ class LoadClientsJob extends JobImpl {
Util.d("Starting SAM");
try {
Util.i("Starting the SAM API");
SAMSecureSessionInterface _secureSession = new AndroidSAMSecureSession(mCtx);
Looper.prepare();
AndroidSAMSecureSession _androidSecureSession = new AndroidSAMSecureSession(mCtx, _routerService, _statusBar);
SAMSecureSessionInterface _secureSession = _androidSecureSession;
SAM_BRIDGE = new SAMBridge("127.0.0.1",
7656,
false,

View File

@ -236,7 +236,7 @@ public class RouterService extends Service {
throw new IllegalStateException("Router has no context?");
}
_context.router().setKillVMOnEnd(false);
Job loadJob = new LoadClientsJob(RouterService.this, _context, _notif);
Job loadJob = new LoadClientsJob(RouterService.this, _context, RouterService.this, _notif, _statusBar);
_context.jobQueue().addJob(loadJob);
_context.addShutdownTask(new ShutdownHook());
_context.addFinalShutdownTask(new FinalShutdownHook());
@ -262,6 +262,16 @@ public class RouterService extends Service {
private String _currTitle;
private boolean _hadTunnels;
public void updateStatus() {
RouterContext ctx = _context;
if(ctx != null && (_state == State.RUNNING || _state == State.ACTIVE || _state == State.GRACEFUL_SHUTDOWN)) {
Router router = ctx.router();
if(router.isAlive()) {
updateStatus(ctx);
}
}
}
private void updateStatus(RouterContext ctx) {
int active = ctx.commSystem().countActivePeers();
int known = Math.max(ctx.netDb().getKnownRouters() - 1, 0);

View File

@ -13,6 +13,7 @@ import android.widget.Toast;
import net.i2p.android.I2PActivity;
import net.i2p.android.router.R;
import net.i2p.android.router.util.Notifications;
class StatusBar {
@ -45,37 +46,69 @@ class StatusBar {
int icon = ICON_STARTING;
// won't be shown if replace() is called
String text = mCtx.getString(R.string.notification_status_starting);
mNotifyBuilder = notifyBuilder(icon, text);
}
private NotificationCompat.Builder notifyBuilder(int icon, String text) {
NotificationCompat.Builder tNotifyBuilder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mNotifyBuilder = new NotificationCompat.Builder(mCtx);
tNotifyBuilder = new NotificationCompat.Builder(mCtx);
} else {
mNotifyBuilder = new NotificationCompat.Builder(mCtx, NOTIFICATION_CHANNEL_ID);
tNotifyBuilder = new NotificationCompat.Builder(mCtx, NOTIFICATION_CHANNEL_ID);
}
tNotifyBuilder.setContentText(text);
tNotifyBuilder.setSmallIcon(icon);
tNotifyBuilder.setColor(mCtx.getResources().getColor(R.color.primary_light));
tNotifyBuilder.setOngoing(true);
tNotifyBuilder.setPriority(NotificationManager.IMPORTANCE_LOW);
tNotifyBuilder.setCategory(Notification.CATEGORY_SERVICE);
mNotifyBuilder.setContentText(text);
mNotifyBuilder.setSmallIcon(icon);
mNotifyBuilder.setColor(mCtx.getResources().getColor(R.color.primary_light));
mNotifyBuilder.setOngoing(true);
mNotifyBuilder.setPriority(NotificationManager.IMPORTANCE_LOW);
mNotifyBuilder.setCategory(Notification.CATEGORY_SERVICE);
PendingIntent pi = this.pendingIntent();
tNotifyBuilder.setContentIntent(pi);
return tNotifyBuilder;
}
private PendingIntent pendingIntent() {
Intent intent = new Intent(mCtx, I2PActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(mCtx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mNotifyBuilder.setContentIntent(pi);
return pi;
}
public void replace(int icon, int textResource) {
PendingIntent pi = pendingIntent();
mNotifyBuilder.setContentIntent(pi);
replace(icon, mCtx.getString(textResource));
}
public void replace(int icon, String title) {
PendingIntent pi = pendingIntent();
mNotifyBuilder.setContentIntent(pi);
mNotifyBuilder.setSmallIcon(icon)
.setStyle(null)
.setTicker(title);
update(title);
}
public void replaceIntent(int icon, String text, PendingIntent pi) {
mNotifyBuilder.setContentIntent(pi);
mNotifyBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
mNotifyBuilder.setAutoCancel(true);
mNotifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
mNotifyBuilder.setSmallIcon(icon).setContentText(text);
update(null, text);
}
public void replaceIntent(int icon, int textResource, PendingIntent pi) {
String text = mCtx.getString(textResource);
mNotifyBuilder.setContentIntent(pi);
mNotifyBuilder.setPriority(NotificationCompat.PRIORITY_MAX);
mNotifyBuilder.setAutoCancel(true);
mNotifyBuilder.setStyle(new NotificationCompat.BigTextStyle().bigText(text));
mNotifyBuilder.setSmallIcon(icon).setContentText(text);
update(null, text);
}
public void update(String title) {
update(title, null);
}
@ -87,8 +120,10 @@ class StatusBar {
}
public void update(String title, String text) {
mNotifyBuilder.setContentTitle(title)
.setContentText(text);
if (title != null)
mNotifyBuilder.setContentTitle(title);
if (text != null)
mNotifyBuilder.setContentText(text);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mNotificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_LOW);
mNotificationManager.createNotificationChannel(mNotificationChannel);

View File

@ -172,4 +172,28 @@
<item>ECDSA-P521</item>
<item>Ed25519</item>
</string-array>
<string-array name="speed_increments">
<item>100 KB/s</item>
<item>200 KB/s</item>
<item>300 KB/s</item>
<item>400 KB/s</item>
<item>500 KB/s</item>
<item>600 KB/s</item>
<item>700 KB/s</item>
<item>800 KB/s</item>
<item>900 KB/s</item>
<item>1000 KB/s</item>
</string-array>
<string-array name="speed_values">
<item>100</item>
<item>200</item>
<item>300</item>
<item>400</item>
<item>500</item>
<item>600</item>
<item>700</item>
<item>800</item>
<item>900</item>
<item>1000</item>
</string-array>
</resources>

View File

@ -189,8 +189,9 @@
<string name="settings_label_sam">SAM interface</string>
<string name="settings_desc_sam">Allow third-party apps to create tunnels using SAM (requires router restart)</string>
<string name="settings_confirm_sam">An application is trying to make a SAM connection.</string>
<string name="settings_confirm_allow_sam">Allow</string>
<string name="settings_confirm_deny_sam">Deny</string>
<string name="settings_confirm_sam_id">Connection Name/ID:</string>
<string name="settings_confirm_allow_sam">Tap to allow</string>
<string name="settings_confirm_deny_sam">Ignore to deny</string>
<string name="settings_label_exploratory_pool">Exploratory pool</string>
<string name="settings_desc_exploratory_pool">Tunnel parameters</string>
<string name="settings_label_expl_inbound">Inbound tunnels</string>
@ -423,5 +424,5 @@
<string name="no_market_app">No market app found, please install manually</string>
<string name="unset">Unset</string>
<string name="running_background">I2P is running in the background</string>
<string name="running_background">I2P is running in the background</string>
</resources>

View File

@ -17,22 +17,20 @@
<!--
i2np.bandwidth.inboundKBytesPerSecond=100
-->
<com.pavelsikun.seekbarpreference.SeekBarPreference
<DropDownPreference
android:key="@string/PROP_INBOUND_BANDWIDTH"
android:title="@string/settings_desc_bw_inbound"
app:msbp_defaultValue="100"
app:msbp_maxValue="1000"
app:msbp_measurementUnit="KB/s"
android:entries="@array/speed_increments"
android:entryValues="@array/speed_values"
/>
<!--
i2np.bandwidth.outboundKBytesPerSecond=30
-->
<com.pavelsikun.seekbarpreference.SeekBarPreference
<DropDownPreference
android:key="@string/PROP_OUTBOUND_BANDWIDTH"
android:title="@string/settings_desc_bw_outbound"
app:msbp_defaultValue="100"
app:msbp_maxValue="1000"
app:msbp_measurementUnit="KB/s"
android:title="@string/settings_desc_bw_inbound"
android:entries="@array/speed_increments"
android:entryValues="@array/speed_values"
/>
</PreferenceCategory>

View File

@ -17,4 +17,4 @@ POM_DEVELOPER_EMAIL=hankhill19580@gmail.com
ANDROID_BUILD_TARGET_SDK_VERSION=30
ANDROID_BUILD_SDK_VERSION=28
I2P_VERSION=1.7.0-21
I2P_VERSION=1.8.0-1