diff --git a/.travis.yml b/.travis.yml
index 52c7ec63..0ac44119 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,8 +4,8 @@ jdk:
android:
components:
- tools
- - build-tools-27.0.2
- - android-27
+ - build-tools-28.0.3
+ - android-29
- extra-android-m2repository
env:
diff --git a/app/build.gradle b/app/build.gradle
index 240904ce..a1ea44f7 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -15,14 +15,14 @@ sonarqube {
}
android {
- compileSdkVersion 27
- buildToolsVersion '27.0.2'
+ compileSdkVersion 29
+ buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 14
- targetSdkVersion 27
- versionCode 55
- versionName "2.2.8"
+ targetSdkVersion 29
+ versionCode 57
+ versionName "2.2.12"
applicationId "com.aaronjwood.portauthority"
setProperty("archivesBaseName", "PortAuthority-$versionName")
}
@@ -65,15 +65,12 @@ android {
}
dependencies {
- compile 'com.android.support:support-v4:27.0.2'
- compile 'com.android.support:appcompat-v7:27.0.2'
- compile 'com.squareup.okhttp3:okhttp:3.9.1'
- compile 'jcifs:jcifs:1.3.17'
- compile 'dnsjava:dnsjava:2.1.7'
- //This does absolutely nothing
- releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
- debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.4'
- testCompile 'junit:junit:4.12'
- testCompile 'org.mockito:mockito-core:1.10.19'
- testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
+ implementation 'com.android.support:support-v4:28.0.0'
+ implementation 'com.android.support:appcompat-v7:28.0.0'
+ implementation 'com.squareup.okhttp3:okhttp:4.4.0'
+ implementation 'jcifs:jcifs:1.3.17'
+ implementation 'dnsjava:dnsjava:3.0.1'
+ debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'org.mockito:mockito-core:1.10.19'
}
diff --git a/app/proguard.pro b/app/proguard.pro
index 26ef9e2c..0b7eb396 100644
--- a/app/proguard.pro
+++ b/app/proguard.pro
@@ -1,9 +1,3 @@
--dontnote org.xbill.DNS.spi.DNSJavaNameServiceDescriptor
--dontwarn org.xbill.DNS.spi.DNSJavaNameServiceDescriptor
--keep class org.xbill.** { *; }
--keepattributes EnclosingMethod
--keepattributes InnerClasses
-
-dontwarn okhttp3.**
-dontwarn okio.**
-dontwarn javax.annotation.**
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6f79851c..1dc1ead1 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,10 +5,11 @@
+
+
adapter;
protected ListView portList;
- protected final List ports = Collections.synchronizedList(new ArrayList());
+ protected final List ports = Collections.synchronizedList(new ArrayList<>());
protected ProgressDialog scanProgressDialog;
protected Dialog portRangeDialog;
protected Handler handler;
@@ -114,7 +114,7 @@ protected void onDestroy() {
public void onSaveInstanceState(Bundle savedState) {
super.onSaveInstanceState(savedState);
- String[] savedList = ports.toArray(new String[ports.size()]);
+ String[] savedList = ports.toArray(new String[0]);
savedState.putStringArray("ports", savedList);
}
@@ -141,12 +141,9 @@ public void onRestoreInstanceState(Bundle savedInstanceState) {
* @param stop Stopping port picker
*/
protected void resetPortRangeScanClick(final NumberPicker start, final NumberPicker stop) {
- portRangeDialog.findViewById(R.id.resetPortRangeScan).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- start.setValue(Constants.MIN_PORT_VALUE);
- stop.setValue(Constants.MAX_PORT_VALUE);
- }
+ portRangeDialog.findViewById(R.id.resetPortRangeScan).setOnClickListener(v -> {
+ start.setValue(Constants.MIN_PORT_VALUE);
+ stop.setValue(Constants.MAX_PORT_VALUE);
});
}
@@ -250,13 +247,9 @@ public void onItemClick(AdapterView> parent, View view, int position, long id)
*/
@Override
public void processFinish(final int output) {
- handler.post(new Runnable() {
-
- @Override
- public void run() {
- if (scanProgressDialog != null) {
- scanProgressDialog.incrementProgressBy(output);
- }
+ handler.post(() -> {
+ if (scanProgressDialog != null) {
+ scanProgressDialog.incrementProgressBy(output);
}
});
}
@@ -307,24 +300,16 @@ private String formatOpenPort(SparseArray entry, int scannedPort, String
*/
private void addOpenPort(final String port) {
setAnimations();
- handler.post(new Runnable() {
-
- @Override
- public void run() {
- ports.add(port);
- Collections.sort(ports, new Comparator() {
-
- @Override
- public int compare(String lhs, String rhs) {
- int left = Integer.parseInt(lhs.substring(0, lhs.indexOf('-') - 1));
- int right = Integer.parseInt(rhs.substring(0, rhs.indexOf('-') - 1));
+ handler.post(() -> {
+ ports.add(port);
+ Collections.sort(ports, (lhs, rhs) -> {
+ int left = Integer.parseInt(lhs.substring(0, lhs.indexOf('-') - 1));
+ int right = Integer.parseInt(rhs.substring(0, rhs.indexOf('-') - 1));
- return left - right;
- }
- });
+ return left - right;
+ });
- adapter.notifyDataSetChanged();
- }
+ adapter.notifyDataSetChanged();
});
}
@@ -335,11 +320,6 @@ public int compare(String lhs, String rhs) {
* @param Exception
*/
public void processFinish(final T output) {
- handler.post(new Runnable() {
- @Override
- public void run() {
- Errors.showError(getApplicationContext(), output.getLocalizedMessage());
- }
- });
+ handler.post(() -> Errors.showError(getApplicationContext(), output.getLocalizedMessage()));
}
}
diff --git a/app/src/main/java/com/aaronjwood/portauthority/activity/LanHostActivity.java b/app/src/main/java/com/aaronjwood/portauthority/activity/LanHostActivity.java
index ab2dea7c..b3931761 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/activity/LanHostActivity.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/activity/LanHostActivity.java
@@ -169,23 +169,19 @@ public void onClick(View v) {
*/
private void setupWol() {
Button wakeUpButton = findViewById(R.id.wakeOnLan);
- wakeUpButton.setOnClickListener(new View.OnClickListener() {
-
- @Override
- public void onClick(View v) {
- try {
- if (!wifi.isConnectedWifi()) {
- Errors.showError(getApplicationContext(), getResources().getString(R.string.notConnectedLan));
- return;
- }
- } catch (Wireless.NoConnectivityManagerException e) {
+ wakeUpButton.setOnClickListener(v -> {
+ try {
+ if (!wifi.isConnectedWifi()) {
Errors.showError(getApplicationContext(), getResources().getString(R.string.notConnectedLan));
return;
}
-
- host.wakeOnLan();
- Toast.makeText(getApplicationContext(), String.format(getResources().getString(R.string.waking), host.getHostname()), Toast.LENGTH_LONG).show();
+ } catch (Wireless.NoConnectivityManagerException e) {
+ Errors.showError(getApplicationContext(), getResources().getString(R.string.notConnectedLan));
+ return;
}
+
+ host.wakeOnLan();
+ Toast.makeText(getApplicationContext(), String.format(getResources().getString(R.string.waking), host.getHostname()), Toast.LENGTH_LONG).show();
});
}
diff --git a/app/src/main/java/com/aaronjwood/portauthority/activity/MainActivity.java b/app/src/main/java/com/aaronjwood/portauthority/activity/MainActivity.java
index 5283ebc1..0ee3ff60 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/activity/MainActivity.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/activity/MainActivity.java
@@ -1,21 +1,26 @@
package com.aaronjwood.portauthority.activity;
+import android.Manifest;
+import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.sqlite.SQLiteException;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
@@ -55,13 +60,14 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public final class MainActivity extends AppCompatActivity implements MainAsyncResponse {
private final static int TIMER_INTERVAL = 1500;
+ private final static int COARSE_LOCATION_REQUEST = 1;
+ private final static int FINE_LOCATION_REQUEST = 2;
private Wireless wifi;
private ListView hostList;
@@ -78,7 +84,7 @@ public final class MainActivity extends AppCompatActivity implements MainAsyncRe
private Handler scanHandler;
private IntentFilter intentFilter = new IntentFilter();
private HostAdapter hostAdapter;
- private List hosts = Collections.synchronizedList(new ArrayList());
+ private List hosts = Collections.synchronizedList(new ArrayList<>());
private Database db;
private DownloadAsyncTask ouiTask;
private DownloadAsyncTask portTask;
@@ -123,17 +129,67 @@ protected void onCreate(Bundle savedInstanceState) {
discoverHostsBtn = findViewById(R.id.discoverHosts);
discoverHostsStr = getResources().getString(R.string.hostDiscovery);
- wifi = new Wireless(getApplicationContext());
+ Context context = getApplicationContext();
+ wifi = new Wireless(context);
scanHandler = new Handler(Looper.getMainLooper());
checkDatabase();
- db = Database.getInstance(getApplicationContext());
+ db = Database.getInstance(context);
setupHostsAdapter();
setupDrawer();
setupHostDiscovery();
intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+
+ ssidAccess(context);
+ }
+
+ /**
+ * Android 8+ now requires extra location permissions to read the SSID.
+ * Determine what permissions to prompt the user for based on saved state.
+ *
+ * @param context
+ */
+ private void ssidAccess(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ if (UserPreference.getCoarseLocationPermDiag(context) || UserPreference.getFineLocationPermDiag(context)) {
+ return;
+ }
+
+ Activity activity = this;
+ String title = "Android 8-9 SSID Access";
+ String message = "Android 8-9 requires coarse location permissions to read the SSID. " +
+ "If this is not something you're comfortable with just deny the request and go without the functionality.";
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ title = "Android 10+ SSID Access";
+ message = "Android 10+ requires fine location permissions to read the SSID. " +
+ "If this is not something you're comfortable with just deny the request and go without the functionality.";
+ }
+
+ new AlertDialog.Builder(activity, R.style.DialogTheme).setTitle(title)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
+ dialogInterface.dismiss();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ UserPreference.saveFineLocationPermDiag(context);
+ } else {
+ UserPreference.saveCoarseLocationPermDiag(context);
+ }
+
+ String perm = Manifest.permission.ACCESS_COARSE_LOCATION;
+ int request = COARSE_LOCATION_REQUEST;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ perm = Manifest.permission.ACCESS_FINE_LOCATION;
+ request = FINE_LOCATION_REQUEST;
+ }
+
+ if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(activity, new String[]{perm}, request);
+ }
+ })
+ .setIcon(android.R.drawable.ic_dialog_alert).show().setCanceledOnTouchOutside(false);
+ }
}
/**
@@ -150,22 +206,13 @@ public void checkDatabase() {
"This will download the official OUI list from Wireshark and port list from IANA. " +
"Note that you won't be able to resolve any MAC vendors or identify services without this data. " +
"You can always perform this from the menu later.")
- .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- dialogInterface.dismiss();
- ouiTask = new DownloadOuisAsyncTask(db, new OuiParser(), activity);
- portTask = new DownloadPortDataAsyncTask(db, new PortParser(), activity);
- ouiTask.execute();
- portTask.execute();
- }
- }).setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialogInterface, int i) {
- dialogInterface.cancel();
- }
- }).setIcon(android.R.drawable.ic_dialog_alert).show().setCanceledOnTouchOutside(false);
+ .setPositiveButton(android.R.string.yes, (dialogInterface, i) -> {
+ dialogInterface.dismiss();
+ ouiTask = new DownloadOuisAsyncTask(db, new OuiParser(), activity);
+ portTask = new DownloadPortDataAsyncTask(db, new PortParser(), activity);
+ ouiTask.execute();
+ portTask.execute();
+ }).setNegativeButton(android.R.string.no, (dialogInterface, i) -> dialogInterface.cancel()).setIcon(android.R.drawable.ic_dialog_alert).show().setCanceledOnTouchOutside(false);
}
/**
@@ -336,38 +383,18 @@ public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.sortHostname:
if (sortAscending) {
- hostAdapter.sort(new Comparator() {
- @Override
- public int compare(Host lhs, Host rhs) {
- return rhs.getHostname().toLowerCase().compareTo(lhs.getHostname().toLowerCase());
- }
- });
+ hostAdapter.sort((lhs, rhs) -> rhs.getHostname().toLowerCase().compareTo(lhs.getHostname().toLowerCase()));
} else {
- hostAdapter.sort(new Comparator() {
- @Override
- public int compare(Host lhs, Host rhs) {
- return lhs.getHostname().toLowerCase().compareTo(rhs.getHostname().toLowerCase());
- }
- });
+ hostAdapter.sort((lhs, rhs) -> lhs.getHostname().toLowerCase().compareTo(rhs.getHostname().toLowerCase()));
}
sortAscending = !sortAscending;
return true;
case R.id.sortVendor:
if (sortAscending) {
- hostAdapter.sort(new Comparator() {
- @Override
- public int compare(Host lhs, Host rhs) {
- return rhs.getVendor().toLowerCase().compareTo(lhs.getVendor().toLowerCase());
- }
- });
+ hostAdapter.sort((lhs, rhs) -> rhs.getVendor().toLowerCase().compareTo(lhs.getVendor().toLowerCase()));
} else {
- hostAdapter.sort(new Comparator() {
- @Override
- public int compare(Host lhs, Host rhs) {
- return lhs.getVendor().toLowerCase().compareTo(rhs.getVendor().toLowerCase());
- }
- });
+ hostAdapter.sort((lhs, rhs) -> lhs.getVendor().toLowerCase().compareTo(rhs.getVendor().toLowerCase()));
}
sortAscending = !sortAscending;
@@ -455,7 +482,7 @@ public void run() {
Errors.showError(context, resources.getString(R.string.failedSignal));
return;
}
-
+
signalStrength.setText(String.format(resources.getString(R.string.signalLink), signal, speed));
signalHandler.postDelayed(this, TIMER_INTERVAL);
}
@@ -565,7 +592,7 @@ public void onItemClick(AdapterView> parent, View view, int position, long id)
private void getInternalIp() {
try {
int netmask = wifi.getInternalWifiSubnet();
- String internalIpWithSubnet = wifi.getInternalWifiIpAddress(String.class) + "/" + Integer.toString(netmask);
+ String internalIpWithSubnet = wifi.getInternalWifiIpAddress(String.class) + "/" + netmask;
internalIp.setText(internalIpWithSubnet);
} catch (UnknownHostException | Wireless.NoWifiManagerException e) {
Errors.showError(getApplicationContext(), getResources().getString(R.string.notConnectedLan));
@@ -679,31 +706,23 @@ public void onRestoreInstanceState(Bundle savedState) {
*/
@Override
public void processFinish(final Host h, final AtomicInteger i) {
- scanHandler.post(new Runnable() {
-
- @Override
- public void run() {
- hosts.add(h);
- hostAdapter.sort(new Comparator() {
-
- @Override
- public int compare(Host lhs, Host rhs) {
- try {
- int leftIp = ByteBuffer.wrap(InetAddress.getByName(lhs.getIp()).getAddress()).getInt();
- int rightIp = ByteBuffer.wrap(InetAddress.getByName(rhs.getIp()).getAddress()).getInt();
-
- return leftIp - rightIp;
- } catch (UnknownHostException ignored) {
- return 0;
- }
- }
- });
+ scanHandler.post(() -> {
+ hosts.add(h);
+ hostAdapter.sort((lhs, rhs) -> {
+ try {
+ int leftIp = ByteBuffer.wrap(InetAddress.getByName(lhs.getIp()).getAddress()).getInt();
+ int rightIp = ByteBuffer.wrap(InetAddress.getByName(rhs.getIp()).getAddress()).getInt();
- discoverHostsBtn.setText(discoverHostsStr + " (" + hosts.size() + ")");
- if (i.decrementAndGet() == 0) {
- discoverHostsBtn.setAlpha(1);
- discoverHostsBtn.setEnabled(true);
+ return leftIp - rightIp;
+ } catch (UnknownHostException ignored) {
+ return 0;
}
+ });
+
+ discoverHostsBtn.setText(discoverHostsStr + " (" + hosts.size() + ")");
+ if (i.decrementAndGet() == 0) {
+ discoverHostsBtn.setAlpha(1);
+ discoverHostsBtn.setEnabled(true);
}
});
}
@@ -738,13 +757,9 @@ public void processFinish(String output) {
*/
@Override
public void processFinish(final boolean output) {
- scanHandler.post(new Runnable() {
-
- @Override
- public void run() {
- if (output && scanProgressDialog != null && scanProgressDialog.isShowing()) {
- scanProgressDialog.dismiss();
- }
+ scanHandler.post(() -> {
+ if (output && scanProgressDialog != null && scanProgressDialog.isShowing()) {
+ scanProgressDialog.dismiss();
}
});
}
@@ -757,12 +772,6 @@ public void run() {
*/
@Override
public void processFinish(final T output) {
- scanHandler.post(new Runnable() {
-
- @Override
- public void run() {
- Errors.showError(getApplicationContext(), output.getLocalizedMessage());
- }
- });
+ scanHandler.post(() -> Errors.showError(getApplicationContext(), output.getLocalizedMessage()));
}
}
diff --git a/app/src/main/java/com/aaronjwood/portauthority/activity/WanHostActivity.java b/app/src/main/java/com/aaronjwood/portauthority/activity/WanHostActivity.java
index accfb974..2565a748 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/activity/WanHostActivity.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/activity/WanHostActivity.java
@@ -138,13 +138,7 @@ public void processFinish(boolean output) {
}
if (!output) {
- handler.post(new Runnable() {
-
- @Override
- public void run() {
- Toast.makeText(getApplicationContext(), "Please enter a valid URL or IP address", Toast.LENGTH_SHORT).show();
- }
- });
+ handler.post(() -> Toast.makeText(getApplicationContext(), "Please enter a valid URL or IP address", Toast.LENGTH_SHORT).show());
}
}
}
diff --git a/app/src/main/java/com/aaronjwood/portauthority/async/DnsLookupAsyncTask.java b/app/src/main/java/com/aaronjwood/portauthority/async/DnsLookupAsyncTask.java
index d853eafa..4713dc08 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/async/DnsLookupAsyncTask.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/async/DnsLookupAsyncTask.java
@@ -1,14 +1,21 @@
package com.aaronjwood.portauthority.async;
+import android.content.Context;
import android.os.AsyncTask;
import com.aaronjwood.portauthority.response.DnsAsyncResponse;
+import org.xbill.DNS.ExtendedResolver;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
+import org.xbill.DNS.Resolver;
import org.xbill.DNS.TextParseException;
+import org.xbill.DNS.config.AndroidResolverConfigProvider;
+import org.xbill.DNS.config.InitializationException;
import java.lang.ref.WeakReference;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
public class DnsLookupAsyncTask extends AsyncTask {
@@ -33,11 +40,22 @@ public DnsLookupAsyncTask(DnsAsyncResponse delegate) {
protected String doInBackground(String... params) {
String domain = params[0];
int recordType = Integer.parseInt(params[1]);
- Record[] records;
-
+ AndroidResolverConfigProvider resolverCfg = new AndroidResolverConfigProvider();
+ Context ctx = (Context) this.delegate.get();
+ AndroidResolverConfigProvider.setContext(ctx);
try {
- records = new Lookup(domain, recordType).run();
- if (records == null) {
+ resolverCfg.initialize();
+ String[] servers = new String[resolverCfg.servers().size()];
+ for (int i = 0; i < resolverCfg.servers().size(); i++) {
+ InetSocketAddress server = resolverCfg.servers().get(i);
+ servers[i] = server.getHostName();
+ }
+
+ Resolver resolver = new ExtendedResolver(servers);
+ Lookup lookup = new Lookup(domain, recordType);
+ lookup.setResolver(resolver);
+ Record[] records = lookup.run();
+ if (records == null || records.length == 0) {
return "No records found.";
}
@@ -49,7 +67,11 @@ protected String doInBackground(String... params) {
return answer.toString();
} catch (TextParseException e) {
- return "Error performing lookup!";
+ return "Error performing lookup: " + e.getMessage();
+ } catch (InitializationException e) {
+ return "Error initializing resolver: " + e.getMessage();
+ } catch (UnknownHostException e) {
+ return "Resolver host is unknown:: " + e.getMessage();
}
}
diff --git a/app/src/main/java/com/aaronjwood/portauthority/async/DownloadAsyncTask.java b/app/src/main/java/com/aaronjwood/portauthority/async/DownloadAsyncTask.java
index 55433b50..bf8b0369 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/async/DownloadAsyncTask.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/async/DownloadAsyncTask.java
@@ -2,7 +2,6 @@
import android.app.ProgressDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.os.AsyncTask;
import com.aaronjwood.portauthority.R;
@@ -15,6 +14,7 @@
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.net.URL;
+import java.nio.charset.StandardCharsets;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.HttpsURLConnection;
@@ -42,12 +42,9 @@ protected void onPreExecute() {
dialog = new ProgressDialog(ctx, R.style.DialogTheme);
dialog.setMessage(ctx.getResources().getString(R.string.downloadingData));
dialog.setCanceledOnTouchOutside(false);
- dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialogInterface) {
- dialogInterface.cancel();
- cancel(true);
- }
+ dialog.setOnCancelListener(dialogInterface -> {
+ dialogInterface.cancel();
+ cancel(true);
});
dialog.show();
}
@@ -73,7 +70,7 @@ final void doInBackground(String service, Parser parser) {
return;
}
- in = new BufferedReader(new InputStreamReader(new GZIPInputStream(connection.getInputStream()), "UTF-8"));
+ in = new BufferedReader(new InputStreamReader(new GZIPInputStream(connection.getInputStream()), StandardCharsets.UTF_8));
String line;
while ((line = in.readLine()) != null) {
diff --git a/app/src/main/java/com/aaronjwood/portauthority/async/ScanHostsAsyncTask.java b/app/src/main/java/com/aaronjwood/portauthority/async/ScanHostsAsyncTask.java
index f648a11b..8412a769 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/async/ScanHostsAsyncTask.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/async/ScanHostsAsyncTask.java
@@ -1,6 +1,8 @@
package com.aaronjwood.portauthority.async;
import android.os.AsyncTask;
+import android.os.Build;
+import android.util.Pair;
import com.aaronjwood.portauthority.db.Database;
import com.aaronjwood.portauthority.network.Host;
@@ -16,6 +18,9 @@
import java.lang.ref.WeakReference;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@@ -27,6 +32,9 @@ public class ScanHostsAsyncTask extends AsyncTask {
private final WeakReference delegate;
private Database db;
private static final String ARP_TABLE = "/proc/net/arp";
+ private static final String IP_CMD = "ip neighbor";
+ private static final String NEIGHBOR_INCOMPLETE = "INCOMPLETE";
+ private static final String NEIGHBOR_FAILED = "FAILED";
private static final String ARP_INCOMPLETE = "0x0";
private static final String ARP_INACTIVE = "00:00:00:00:00:00";
private static final int NETBIOS_FILE_SERVER = 0x20;
@@ -51,14 +59,38 @@ protected Void doInBackground(Integer... params) {
int ipv4 = params[0];
int cidr = params[1];
int timeout = params[2];
-
MainAsyncResponse activity = delegate.get();
- File file = new File(ARP_TABLE);
- if (!file.exists() || !file.canRead()) {
- activity.processFinish(new FileNotFoundException("Unable to access device ARP table"));
- activity.processFinish(true);
- return null;
+ // Android 10+ doesn't let us access the ARP table.
+ // Do an early check to see if we can get what we need from the system.
+ // https://developer.android.com/about/versions/10/privacy/changes#proc-net-filesystem
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ try {
+ Process ipProc = Runtime.getRuntime().exec(IP_CMD);
+ ipProc.waitFor();
+ if (ipProc.exitValue() != 0) {
+ activity.processFinish(new IOException("Unable to access ARP entries"));
+ activity.processFinish(true);
+
+ return null;
+ }
+ } catch (IOException | InterruptedException e) {
+ activity.processFinish(new IOException("Unable to parse ARP entries"));
+ activity.processFinish(true);
+ }
+ } else {
+ File file = new File(ARP_TABLE);
+ if (!file.exists()) {
+ activity.processFinish(new FileNotFoundException("Unable to find ARP table"));
+ activity.processFinish(true);
+
+ return null;
+ }
+
+ if (!file.canRead()) {
+ activity.processFinish(new IOException("Unable to read ARP table"));
+ activity.processFinish(true);
+ }
}
ExecutorService executor = Executors.newCachedThreadPool();
@@ -107,63 +139,99 @@ protected final void onProgressUpdate(Void... params) {
final MainAsyncResponse activity = delegate.get();
ExecutorService executor = Executors.newCachedThreadPool();
final AtomicInteger numHosts = new AtomicInteger(0);
+ List> pairs = new ArrayList<>();
try {
- reader = new BufferedReader(new InputStreamReader(new FileInputStream(ARP_TABLE), "UTF-8"));
- reader.readLine(); // Skip header.
- String line;
-
- while ((line = reader.readLine()) != null) {
- String[] arpLine = line.split("\\s+");
-
- final String ip = arpLine[0];
- final String flag = arpLine[2];
- final String macAddress = arpLine[3];
-
- if (!ARP_INCOMPLETE.equals(flag) && !ARP_INACTIVE.equals(macAddress)) {
- numHosts.incrementAndGet();
- executor.execute(new Runnable() {
-
- @Override
- public void run() {
- Host host;
- try {
- host = new Host(ip, macAddress, db);
- } catch (IOException e) {
- host = new Host(ip, macAddress);
- }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ Process ipProc = Runtime.getRuntime().exec(IP_CMD);
+ ipProc.waitFor();
+ if (ipProc.exitValue() != 0) {
+ throw new Exception("Unable to access ARP entries");
+ }
- MainAsyncResponse activity = delegate.get();
- try {
- InetAddress add = InetAddress.getByName(ip);
- String hostname = add.getCanonicalHostName();
- host.setHostname(hostname);
-
- if (activity != null) {
- activity.processFinish(host, numHosts);
- }
- } catch (UnknownHostException e) {
- numHosts.decrementAndGet();
- activity.processFinish(e);
- return;
- }
+ reader = new BufferedReader(new InputStreamReader(ipProc.getInputStream()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] neighborLine = line.split("\\s+");
+
+ // We don't have a validated ARP entry for this case.
+ if (neighborLine.length <= 4) {
+ continue;
+ }
+
+ String ip = neighborLine[0];
+ InetAddress addr = InetAddress.getByName(ip);
+ if (addr.isLinkLocalAddress() || addr.isLoopbackAddress()) {
+ continue;
+ }
+
+ String macAddress = neighborLine[4];
+ String state = neighborLine[neighborLine.length - 1];
+
+ // Determine if the ARP entry is valid.
+ // https://github.com/sivasankariit/iproute2/blob/master/ip/ipneigh.c
+ if (!NEIGHBOR_FAILED.equals(state) && !NEIGHBOR_INCOMPLETE.equals(state)) {
+ pairs.add(new Pair<>(ip, macAddress));
+ }
+ }
+ } else {
+ reader = new BufferedReader(new InputStreamReader(new FileInputStream(ARP_TABLE), StandardCharsets.UTF_8));
+ reader.readLine(); // Skip header.
+ String line;
+
+ while ((line = reader.readLine()) != null) {
+ String[] arpLine = line.split("\\s+");
+ String ip = arpLine[0];
+ String flag = arpLine[2];
+ String macAddress = arpLine[3];
+
+ if (!ARP_INCOMPLETE.equals(flag) && !ARP_INACTIVE.equals(macAddress)) {
+ pairs.add(new Pair<>(ip, macAddress));
+ }
+ }
+ }
- try {
- NbtAddress[] netbios = NbtAddress.getAllByAddress(ip);
- for (NbtAddress addr : netbios) {
- if (addr.getNameType() == NETBIOS_FILE_SERVER) {
- host.setHostname(addr.getHostName());
- return;
- }
- }
- } catch (UnknownHostException e) {
- // It's common that many discovered hosts won't have a NetBIOS entry.
+ numHosts.addAndGet(pairs.size());
+ for (Pair pair : pairs) {
+ String ip = pair.first;
+ String macAddress = pair.second;
+ executor.execute(() -> {
+ Host host;
+ try {
+ host = new Host(ip, macAddress, db);
+ } catch (IOException e) {
+ host = new Host(ip, macAddress);
+ }
+
+ MainAsyncResponse activity1 = delegate.get();
+ try {
+ InetAddress add = InetAddress.getByName(ip);
+ String hostname = add.getCanonicalHostName();
+ host.setHostname(hostname);
+
+ if (activity1 != null) {
+ activity1.processFinish(host, numHosts);
+ }
+ } catch (UnknownHostException e) {
+ numHosts.decrementAndGet();
+ activity1.processFinish(e);
+ return;
+ }
+
+ try {
+ NbtAddress[] netbios = NbtAddress.getAllByAddress(ip);
+ for (NbtAddress addr : netbios) {
+ if (addr.getNameType() == NETBIOS_FILE_SERVER) {
+ host.setHostname(addr.getHostName());
+ return;
}
}
- });
- }
+ } catch (UnknownHostException e) {
+ // It's common that many discovered hosts won't have a NetBIOS entry.
+ }
+ });
}
- } catch (IOException e) {
+ } catch (Exception e) {
if (activity != null) {
activity.processFinish(e);
}
diff --git a/app/src/main/java/com/aaronjwood/portauthority/async/WolAsyncTask.java b/app/src/main/java/com/aaronjwood/portauthority/async/WolAsyncTask.java
index 2e5e34de..ad24b639 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/async/WolAsyncTask.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/async/WolAsyncTask.java
@@ -21,7 +21,7 @@ protected Void doInBackground(String... params) {
String ip = params[1];
byte[] macBytes = new byte[6];
- String[] macHex = mac.split("(:|-)");
+ String[] macHex = mac.split("([:\\-])");
for (int i = 0; i < 6; i++) {
macBytes[i] = (byte) Integer.parseInt(macHex[i], 16);
}
diff --git a/app/src/main/java/com/aaronjwood/portauthority/runnable/ScanPortsRunnable.java b/app/src/main/java/com/aaronjwood/portauthority/runnable/ScanPortsRunnable.java
index c11a507b..e1c52b20 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/runnable/ScanPortsRunnable.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/runnable/ScanPortsRunnable.java
@@ -13,6 +13,7 @@
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.IllegalBlockingModeException;
+import java.nio.charset.StandardCharsets;
public class ScanPortsRunnable implements Runnable {
private String ip;
@@ -65,12 +66,12 @@ public void run() {
SparseArray portData = new SparseArray<>();
String data = null;
try {
- InputStreamReader input = new InputStreamReader(socket.getInputStream(), "UTF-8");
+ InputStreamReader input = new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8);
BufferedReader buffered = new BufferedReader(input);
if (i == 22) {
data = parseSSH(buffered);
} else if (i == 80 || i == 443 || i == 8080) {
- PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"), true);
+ PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8), true);
data = parseHTTP(buffered, out);
}
} catch (IOException e) {
diff --git a/app/src/main/java/com/aaronjwood/portauthority/utils/UserPreference.java b/app/src/main/java/com/aaronjwood/portauthority/utils/UserPreference.java
index e9f7a0b8..c1c5f912 100644
--- a/app/src/main/java/com/aaronjwood/portauthority/utils/UserPreference.java
+++ b/app/src/main/java/com/aaronjwood/portauthority/utils/UserPreference.java
@@ -22,6 +22,8 @@ public class UserPreference {
private static final String LAN_SOCKET_TIMEOUT = "lanTimeout";
private static final String WAN_SOCKET_TIMEOUT = "wanTimeout";
private static final String HOST_SOCKET_TIMEOUT = "hostTimeout";
+ private static final String COARSE_LOCATION_PERM_DIAG = "COARSE_LOCATION";
+ private static final String FINE_LOCATION_PERM_DIAG = "FINE_LOCATION";
/**
* Saves the last used host address for later use.
@@ -37,6 +39,38 @@ public static void saveLastUsedHostAddress(@NonNull Context context, @Nullable S
}
}
+ /**
+ * Saves the state of the coarse location permission dialog.
+ */
+ public static void saveCoarseLocationPermDiag(@NonNull Context context) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ preferences.edit().putBoolean(COARSE_LOCATION_PERM_DIAG, true).apply();
+ }
+
+ /**
+ * Saves the state of the fine location permission dialog.
+ */
+ public static void saveFineLocationPermDiag(@NonNull Context context) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ preferences.edit().putBoolean(FINE_LOCATION_PERM_DIAG, true).apply();
+ }
+
+ /**
+ * Saves the state of the coarse location permission dialog.
+ */
+ public static boolean getCoarseLocationPermDiag(@NonNull Context context) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ return preferences.getBoolean(COARSE_LOCATION_PERM_DIAG, false);
+ }
+
+ /**
+ * Saves the state of the fine location permission dialog.
+ */
+ public static boolean getFineLocationPermDiag(@NonNull Context context) {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ return preferences.getBoolean(FINE_LOCATION_PERM_DIAG, false);
+ }
+
/**
* Gets the last used host address or an empty string if there isn't one.
*/
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index ab340775..179f57f3 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -197,7 +197,7 @@
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
new file mode 100644
index 00000000..6f7a6111
--- /dev/null
+++ b/app/src/main/res/values-nl/strings.xml
@@ -0,0 +1,59 @@
+
+
+
+ Port Authority
+ Instellingen
+ MAC
+ Leverancier
+ LAN IP
+ WAN IP
+ Ophalen…
+ Signaal/Snelheid
+ Ontdek hosts
+ SSID
+ BSSID
+ Scan welbekende poorten
+ Scan poortbereik
+ Reset poortbereik
+ Open poorten
+ Beginpoort
+ Eindpoort
+ Wifi is niet ingeschakeld
+ Geen wifiverbinding
+ URL/IP
+ WAARSCHUWING
+ Domeinnaam
+ DNS-lookup
+ Wakker maken
+ Wakker aan het maken %1$s…
+ IP-adres
+ Ophalen van DNS-records…
+ Voer een domein in en selecteer een recordtype
+ Je bent niet verbonden met een lokaal netwerk
+ Je bent niet verbonden met een wifinetwerk!
+ %1$ddBm/%2$dMbps
+ Scannen naar hosts
+ %1$d hosts in je subnet
+ Sorteer
+ Op hostnaam
+ Op leverancier
+ Kopieer hostnaam
+ Kopieer IP
+ Kopieer MAC
+ Opvragen MAC-leverancier is mislukt
+ Openen database is mislukt
+ Gegevens aan het downloaden
+ Opgraven van aantal hosts in dit subnet is mislukt
+ Opvragen signaalsterkte is mislukt
+ Opvragen verbindingssnelheid is mislukt
+ Opvragen SSID is mislukt
+ Opvragen BSSID is mislukt
+ Opvragen WifiManager van dit apparaat is mislukt
+ Geen wifi-interface gevonden
+
+ Houd er rekening mee dat het uitvoeren van poortscans tegen openbare servers normaal gesproken als kwaadaardig wordt beschouwd.
+ Bovendien zal het uitvoeren van dit soort scans over het netwerk van je provider in plaats van je eigen wifiverbinding trager verlopen en soms onnauwkeurige resultaten opleveren als gevolg van traffic shaping.
+ Je provider kan ook het verkeer dat door deze scan wordt gegenereerd als kwaadaardig beschouwen.
+
+
+
diff --git a/build.gradle b/build.gradle
index 197b6f29..87039655 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.0.1'
+ classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:2.4'
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index d47e3bac..e52b72fa 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Tue Oct 17 17:47:38 EDT 2017
+#Wed Jan 01 19:13:52 PST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.2.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip