mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 21:29:54 +01:00
Collect dev report data on a thread with a looper. #328
This commit is contained in:
@@ -45,7 +45,7 @@
|
|||||||
</service>
|
</service>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.DevReportActivity"
|
android:name=".android.report.DevReportActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:finishOnTaskLaunch="true"
|
android:finishOnTaskLaunch="true"
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import org.briarproject.CoreModule;
|
|||||||
import org.briarproject.android.api.AndroidExecutor;
|
import org.briarproject.android.api.AndroidExecutor;
|
||||||
import org.briarproject.android.api.AndroidNotificationManager;
|
import org.briarproject.android.api.AndroidNotificationManager;
|
||||||
import org.briarproject.android.api.ReferenceManager;
|
import org.briarproject.android.api.ReferenceManager;
|
||||||
import org.briarproject.android.util.BriarReportSender;
|
import org.briarproject.android.report.BriarReportSender;
|
||||||
import org.briarproject.api.contact.ContactExchangeTask;
|
import org.briarproject.api.contact.ContactExchangeTask;
|
||||||
import org.briarproject.api.contact.ContactManager;
|
import org.briarproject.api.contact.ContactManager;
|
||||||
import org.briarproject.api.crypto.CryptoComponent;
|
import org.briarproject.api.crypto.CryptoComponent;
|
||||||
@@ -112,12 +112,10 @@ public interface AndroidComponent extends CoreEagerSingletons {
|
|||||||
@IoExecutor
|
@IoExecutor
|
||||||
Executor ioExecutor();
|
Executor ioExecutor();
|
||||||
|
|
||||||
void inject(DevReportActivity devReportActivity);
|
|
||||||
|
|
||||||
void inject(BriarService activity);
|
void inject(BriarService activity);
|
||||||
|
|
||||||
|
void inject(BriarReportSender briarReportSender);
|
||||||
|
|
||||||
// Eager singleton load
|
// Eager singleton load
|
||||||
void inject(AppModule.EagerSingletons init);
|
void inject(AppModule.EagerSingletons init);
|
||||||
|
|
||||||
void inject(BriarReportSender briarReportSender);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import org.acra.ReportingInteractionMode;
|
|||||||
import org.acra.annotation.ReportsCrashes;
|
import org.acra.annotation.ReportsCrashes;
|
||||||
import org.briarproject.CoreModule;
|
import org.briarproject.CoreModule;
|
||||||
import org.briarproject.R;
|
import org.briarproject.R;
|
||||||
import org.briarproject.android.util.BriarReportPrimer;
|
import org.briarproject.android.report.BriarReportPrimer;
|
||||||
import org.briarproject.android.util.BriarReportSenderFactory;
|
import org.briarproject.android.report.BriarReportSenderFactory;
|
||||||
|
import org.briarproject.android.report.DevReportActivity;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,240 @@
|
|||||||
|
package org.briarproject.android.report;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.NetworkInfo;
|
||||||
|
import android.net.wifi.WifiInfo;
|
||||||
|
import android.net.wifi.WifiManager;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import org.acra.builder.ReportBuilder;
|
||||||
|
import org.acra.builder.ReportPrimer;
|
||||||
|
import org.briarproject.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.FutureTask;
|
||||||
|
|
||||||
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||||
|
import static android.content.Context.ACTIVITY_SERVICE;
|
||||||
|
import static android.content.Context.CONNECTIVITY_SERVICE;
|
||||||
|
import static android.content.Context.WIFI_SERVICE;
|
||||||
|
import static android.net.ConnectivityManager.TYPE_MOBILE;
|
||||||
|
import static android.net.ConnectivityManager.TYPE_WIFI;
|
||||||
|
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
|
||||||
|
|
||||||
|
public class BriarReportPrimer implements ReportPrimer {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void primeReport(@NonNull Context ctx,
|
||||||
|
@NonNull ReportBuilder builder) {
|
||||||
|
CustomDataTask task = new CustomDataTask(ctx);
|
||||||
|
FutureTask<Map<String, String>> futureTask = new FutureTask<>(task);
|
||||||
|
// Use a new thread as the Android executor thread may have died
|
||||||
|
new SingleShotAndroidExecutor(futureTask).start();
|
||||||
|
try {
|
||||||
|
builder.customData(futureTask.get());
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
builder.customData("Custom data exception", e.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CustomDataTask
|
||||||
|
implements Callable<Map<String, String>> {
|
||||||
|
|
||||||
|
private final Context ctx;
|
||||||
|
|
||||||
|
private CustomDataTask(Context ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> call() {
|
||||||
|
Map<String, String> customData = new LinkedHashMap<>();
|
||||||
|
|
||||||
|
// System memory
|
||||||
|
Object o = ctx.getSystemService(ACTIVITY_SERVICE);
|
||||||
|
ActivityManager am = (ActivityManager) o;
|
||||||
|
ActivityManager.MemoryInfo mem = new ActivityManager.MemoryInfo();
|
||||||
|
am.getMemoryInfo(mem);
|
||||||
|
String systemMemory;
|
||||||
|
if (Build.VERSION.SDK_INT >= 16) {
|
||||||
|
systemMemory = (mem.totalMem / 1024 / 1024) + " MiB total, "
|
||||||
|
+ (mem.availMem / 1024 / 1204) + " MiB free, "
|
||||||
|
+ (mem.threshold / 1024 / 1024) + " MiB threshold";
|
||||||
|
} else {
|
||||||
|
systemMemory = (mem.availMem / 1024 / 1204) + " MiB free, "
|
||||||
|
+ (mem.threshold / 1024 / 1024) + " MiB threshold";
|
||||||
|
}
|
||||||
|
customData.put("System memory", systemMemory);
|
||||||
|
|
||||||
|
// Virtual machine memory
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
long heap = runtime.totalMemory();
|
||||||
|
long heapFree = runtime.freeMemory();
|
||||||
|
long heapMax = runtime.maxMemory();
|
||||||
|
String vmMemory = (heap / 1024 / 1024) + " MiB allocated, "
|
||||||
|
+ (heapFree / 1024 / 1024) + " MiB free, "
|
||||||
|
+ (heapMax / 1024 / 1024) + " MiB maximum";
|
||||||
|
customData.put("Virtual machine memory", vmMemory);
|
||||||
|
|
||||||
|
// Internal storage
|
||||||
|
File root = Environment.getRootDirectory();
|
||||||
|
long rootTotal = root.getTotalSpace();
|
||||||
|
long rootFree = root.getFreeSpace();
|
||||||
|
String internal = (rootTotal / 1024 / 1024) + " MiB total, "
|
||||||
|
+ (rootFree / 1024 / 1024) + " MiB free";
|
||||||
|
customData.put("Internal storage", internal);
|
||||||
|
|
||||||
|
// External storage (SD card)
|
||||||
|
File sd = Environment.getExternalStorageDirectory();
|
||||||
|
long sdTotal = sd.getTotalSpace();
|
||||||
|
long sdFree = sd.getFreeSpace();
|
||||||
|
String external = (sdTotal / 1024 / 1024) + " MiB total, "
|
||||||
|
+ (sdFree / 1024 / 1024) + " MiB free";
|
||||||
|
customData.put("External storage", external);
|
||||||
|
|
||||||
|
// Is mobile data available?
|
||||||
|
o = ctx.getSystemService(CONNECTIVITY_SERVICE);
|
||||||
|
ConnectivityManager cm = (ConnectivityManager) o;
|
||||||
|
NetworkInfo mobile = cm.getNetworkInfo(TYPE_MOBILE);
|
||||||
|
boolean mobileAvailable = mobile != null && mobile.isAvailable();
|
||||||
|
// Is mobile data enabled?
|
||||||
|
boolean mobileEnabled = false;
|
||||||
|
try {
|
||||||
|
Class<?> clazz = Class.forName(cm.getClass().getName());
|
||||||
|
Method method = clazz.getDeclaredMethod("getMobileDataEnabled");
|
||||||
|
method.setAccessible(true);
|
||||||
|
mobileEnabled = (Boolean) method.invoke(cm);
|
||||||
|
} catch (ClassNotFoundException
|
||||||
|
| NoSuchMethodException
|
||||||
|
| IllegalArgumentException
|
||||||
|
| InvocationTargetException
|
||||||
|
| IllegalAccessException e) {
|
||||||
|
customData.put("Mobile data reflection exception",
|
||||||
|
e.toString());
|
||||||
|
}
|
||||||
|
// Is mobile data connected ?
|
||||||
|
boolean mobileConnected = mobile != null && mobile.isConnected();
|
||||||
|
|
||||||
|
String mobileStatus;
|
||||||
|
if (mobileAvailable) mobileStatus = "Available, ";
|
||||||
|
else mobileStatus = "Not available, ";
|
||||||
|
if (mobileEnabled) mobileStatus += "enabled, ";
|
||||||
|
else mobileStatus += "not enabled, ";
|
||||||
|
if (mobileConnected) mobileStatus += "connected";
|
||||||
|
else mobileStatus += "not connected";
|
||||||
|
customData.put("Mobile data status", mobileStatus);
|
||||||
|
|
||||||
|
// Is wifi available?
|
||||||
|
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
|
||||||
|
boolean wifiAvailable = wifi != null && wifi.isAvailable();
|
||||||
|
// Is wifi enabled?
|
||||||
|
o = ctx.getSystemService(WIFI_SERVICE);
|
||||||
|
WifiManager wm = (WifiManager) o;
|
||||||
|
boolean wifiEnabled = wm != null &&
|
||||||
|
wm.getWifiState() == WIFI_STATE_ENABLED;
|
||||||
|
// Is wifi connected?
|
||||||
|
boolean wifiConnected = wifi != null && wifi.isConnected();
|
||||||
|
|
||||||
|
String wifiStatus;
|
||||||
|
if (wifiAvailable) wifiStatus = "Available, ";
|
||||||
|
else wifiStatus = "Not available, ";
|
||||||
|
if (wifiEnabled) wifiStatus += "enabled, ";
|
||||||
|
else wifiStatus += "not enabled, ";
|
||||||
|
if (wifiConnected) wifiStatus += "connected";
|
||||||
|
else wifiStatus += "not connected";
|
||||||
|
customData.put("Wi-Fi status", wifiStatus);
|
||||||
|
|
||||||
|
if (wm != null) {
|
||||||
|
WifiInfo wifiInfo = wm.getConnectionInfo();
|
||||||
|
if (wifiInfo != null) {
|
||||||
|
int ip = wifiInfo.getIpAddress(); // Nice API, Google
|
||||||
|
int ip1 = ip & 0xFF;
|
||||||
|
int ip2 = (ip >> 8) & 0xFF;
|
||||||
|
int ip3 = (ip >> 16) & 0xFF;
|
||||||
|
int ip4 = (ip >> 24) & 0xFF;
|
||||||
|
String address = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
|
||||||
|
customData.put("Wi-Fi address", address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is Bluetooth available?
|
||||||
|
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
|
boolean btAvailable = bt != null;
|
||||||
|
// Is Bluetooth enabled?
|
||||||
|
boolean btEnabled = bt != null && bt.isEnabled() &&
|
||||||
|
!StringUtils.isNullOrEmpty(bt.getAddress());
|
||||||
|
// Is Bluetooth connectable?
|
||||||
|
boolean btConnectable = bt != null &&
|
||||||
|
(bt.getScanMode() == SCAN_MODE_CONNECTABLE ||
|
||||||
|
bt.getScanMode() ==
|
||||||
|
SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||||
|
// Is Bluetooth discoverable?
|
||||||
|
boolean btDiscoverable = bt != null &&
|
||||||
|
bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||||
|
|
||||||
|
String btStatus;
|
||||||
|
if (btAvailable) btStatus = "Available, ";
|
||||||
|
else btStatus = "Not available, ";
|
||||||
|
if (btEnabled) btStatus += "enabled, ";
|
||||||
|
else btStatus += "not enabled, ";
|
||||||
|
if (btConnectable) btStatus += "connectable, ";
|
||||||
|
else btStatus += "not connectable, ";
|
||||||
|
if (btDiscoverable) btStatus += "discoverable";
|
||||||
|
else btStatus += "not discoverable";
|
||||||
|
customData.put("Bluetooth status", btStatus);
|
||||||
|
|
||||||
|
if (bt != null)
|
||||||
|
customData.put("Bluetooth address", bt.getAddress());
|
||||||
|
String btSettingsAddr;
|
||||||
|
try {
|
||||||
|
btSettingsAddr = Settings.Secure.getString(
|
||||||
|
ctx.getContentResolver(), "bluetooth_address");
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
btSettingsAddr = "Could not get address from settings";
|
||||||
|
}
|
||||||
|
customData.put("Bluetooth address from settings", btSettingsAddr);
|
||||||
|
|
||||||
|
return Collections.unmodifiableMap(customData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingleShotAndroidExecutor extends Thread {
|
||||||
|
|
||||||
|
private final Runnable runnable;
|
||||||
|
|
||||||
|
private SingleShotAndroidExecutor(Runnable runnable) {
|
||||||
|
this.runnable = runnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Looper.prepare();
|
||||||
|
Handler handler = new Handler();
|
||||||
|
handler.post(runnable);
|
||||||
|
handler.post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Looper looper = Looper.myLooper();
|
||||||
|
if (looper != null) looper.quit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Looper.loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
package org.briarproject.android.util;
|
package org.briarproject.android.report;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import org.acra.ReportField;
|
|
||||||
import org.acra.collector.CrashReportData;
|
import org.acra.collector.CrashReportData;
|
||||||
import org.acra.sender.ReportSender;
|
import org.acra.sender.ReportSender;
|
||||||
import org.acra.sender.ReportSenderException;
|
import org.acra.sender.ReportSenderException;
|
||||||
import org.acra.util.JSONReportBuilder;
|
import org.acra.util.JSONReportBuilder;
|
||||||
import org.briarproject.android.AndroidComponent;
|
import org.briarproject.android.AndroidComponent;
|
||||||
|
import org.briarproject.android.util.AndroidUtils;
|
||||||
import org.briarproject.api.reporting.DevReporter;
|
import org.briarproject.api.reporting.DevReporter;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.acra.ReportField.REPORT_ID;
|
||||||
|
|
||||||
public class BriarReportSender implements ReportSender {
|
public class BriarReportSender implements ReportSender {
|
||||||
|
|
||||||
private final AndroidComponent component;
|
private final AndroidComponent component;
|
||||||
@@ -27,11 +30,10 @@ public class BriarReportSender implements ReportSender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void send(@NonNull Context context,
|
public void send(@NonNull Context ctx,
|
||||||
@NonNull CrashReportData errorContent)
|
@NonNull CrashReportData errorContent)
|
||||||
throws ReportSenderException {
|
throws ReportSenderException {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
|
|
||||||
String crashReport;
|
String crashReport;
|
||||||
try {
|
try {
|
||||||
crashReport = errorContent.toJSON().toString();
|
crashReport = errorContent.toJSON().toString();
|
||||||
@@ -39,10 +41,9 @@ public class BriarReportSender implements ReportSender {
|
|||||||
throw new ReportSenderException("Couldn't create JSON", e);
|
throw new ReportSenderException("Couldn't create JSON", e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
reporter.encryptReportToFile(
|
File reportDir = AndroidUtils.getReportDir(ctx);
|
||||||
AndroidUtils.getReportDir(context),
|
String reportId = errorContent.getProperty(REPORT_ID);
|
||||||
errorContent.getProperty(ReportField.REPORT_ID),
|
reporter.encryptReportToFile(reportDir, reportId, crashReport);
|
||||||
crashReport);
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
throw new ReportSenderException("Failed to encrypt report", e);
|
throw new ReportSenderException("Failed to encrypt report", e);
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.android.util;
|
package org.briarproject.android.report;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
@@ -9,12 +9,13 @@ import org.acra.sender.ReportSenderFactory;
|
|||||||
import org.briarproject.android.BriarApplication;
|
import org.briarproject.android.BriarApplication;
|
||||||
|
|
||||||
public class BriarReportSenderFactory implements ReportSenderFactory {
|
public class BriarReportSenderFactory implements ReportSenderFactory {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ReportSender create(@NonNull Context context,
|
public ReportSender create(@NonNull Context ctx,
|
||||||
@NonNull ACRAConfiguration config) {
|
@NonNull ACRAConfiguration config) {
|
||||||
// ACRA passes in the Application as context
|
// ACRA passes in the Application as context
|
||||||
return new BriarReportSender(
|
BriarApplication app = (BriarApplication) ctx;
|
||||||
((BriarApplication) context).getApplicationComponent());
|
return new BriarReportSender(app.getApplicationComponent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.briarproject.android;
|
package org.briarproject.android.report;
|
||||||
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
@@ -79,15 +79,13 @@ public class DevReportActivity extends BaseCrashReportDialog
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle state) {
|
public void onCreate(Bundle state) {
|
||||||
super.onCreate(state);
|
super.onCreate(state);
|
||||||
setContentView(R.layout.activity_dev_report);
|
|
||||||
|
|
||||||
BriarApplication app = (BriarApplication) getApplication();
|
setContentView(R.layout.activity_dev_report);
|
||||||
app.getApplicationComponent().inject(this);
|
|
||||||
|
|
||||||
sharedPreferencesFactory = new SharedPreferencesFactory(
|
sharedPreferencesFactory = new SharedPreferencesFactory(
|
||||||
getApplicationContext(), getConfig());
|
getApplicationContext(), getConfig());
|
||||||
|
|
||||||
final SharedPreferences prefs = sharedPreferencesFactory.create();
|
SharedPreferences prefs = sharedPreferencesFactory.create();
|
||||||
excludedFields = new HashSet<>();
|
excludedFields = new HashSet<>();
|
||||||
if (Build.VERSION.SDK_INT >= 11) {
|
if (Build.VERSION.SDK_INT >= 11) {
|
||||||
for (String name : prefs.getStringSet(PREF_EXCLUDED_FIELDS,
|
for (String name : prefs.getStringSet(PREF_EXCLUDED_FIELDS,
|
||||||
@@ -121,13 +119,12 @@ public class DevReportActivity extends BaseCrashReportDialog
|
|||||||
report.setVisibility(GONE);
|
report.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
share.setOnClickListener(
|
share.setOnClickListener(new OnClickListener() {
|
||||||
new OnClickListener() {
|
@Override
|
||||||
@Override
|
public void onClick(View v) {
|
||||||
public void onClick(View v) {
|
processReport();
|
||||||
processReport();
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
String userEmail = prefs.getString(ACRA.PREF_USER_EMAIL_ADDRESS, "");
|
String userEmail = prefs.getString(ACRA.PREF_USER_EMAIL_ADDRESS, "");
|
||||||
userEmailView.setText(userEmail);
|
userEmailView.setText(userEmail);
|
||||||
@@ -1,184 +0,0 @@
|
|||||||
package org.briarproject.android.util;
|
|
||||||
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.bluetooth.BluetoothAdapter;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.ConnectivityManager;
|
|
||||||
import android.net.NetworkInfo;
|
|
||||||
import android.net.wifi.WifiInfo;
|
|
||||||
import android.net.wifi.WifiManager;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.Settings;
|
|
||||||
|
|
||||||
import org.acra.builder.ReportBuilder;
|
|
||||||
import org.acra.builder.ReportPrimer;
|
|
||||||
import org.briarproject.util.StringUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE;
|
|
||||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
|
||||||
import static android.content.Context.ACTIVITY_SERVICE;
|
|
||||||
import static android.content.Context.CONNECTIVITY_SERVICE;
|
|
||||||
import static android.content.Context.WIFI_SERVICE;
|
|
||||||
import static android.net.ConnectivityManager.TYPE_MOBILE;
|
|
||||||
import static android.net.ConnectivityManager.TYPE_WIFI;
|
|
||||||
import static android.net.wifi.WifiManager.WIFI_STATE_ENABLED;
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
public class BriarReportPrimer implements ReportPrimer {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(BriarReportPrimer.class.getName());
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void primeReport(Context context, ReportBuilder builder) {
|
|
||||||
// System memory
|
|
||||||
Object o = context.getSystemService(ACTIVITY_SERVICE);
|
|
||||||
ActivityManager am = (ActivityManager) o;
|
|
||||||
ActivityManager.MemoryInfo mem = new ActivityManager.MemoryInfo();
|
|
||||||
am.getMemoryInfo(mem);
|
|
||||||
String systemMemory;
|
|
||||||
if (Build.VERSION.SDK_INT >= 16) {
|
|
||||||
systemMemory = (mem.totalMem / 1024 / 1024) + " MiB total, "
|
|
||||||
+ (mem.availMem / 1024 / 1204) + " MiB free, "
|
|
||||||
+ (mem.threshold / 1024 / 1024) + " MiB threshold";
|
|
||||||
} else {
|
|
||||||
systemMemory = (mem.availMem / 1024 / 1204) + " MiB free, "
|
|
||||||
+ (mem.threshold / 1024 / 1024) + " MiB threshold";
|
|
||||||
}
|
|
||||||
builder.customData("System memory", systemMemory);
|
|
||||||
|
|
||||||
// Virtual machine memory
|
|
||||||
Runtime runtime = Runtime.getRuntime();
|
|
||||||
long heap = runtime.totalMemory();
|
|
||||||
long heapFree = runtime.freeMemory();
|
|
||||||
long heapMax = runtime.maxMemory();
|
|
||||||
String vmMemory = (heap / 1024 / 1024) + " MiB allocated, "
|
|
||||||
+ (heapFree / 1024 / 1024) + " MiB free, "
|
|
||||||
+ (heapMax / 1024 / 1024) + " MiB maximum";
|
|
||||||
builder.customData("Virtual machine memory", vmMemory);
|
|
||||||
|
|
||||||
// Internal storage
|
|
||||||
File root = Environment.getRootDirectory();
|
|
||||||
long rootTotal = root.getTotalSpace();
|
|
||||||
long rootFree = root.getFreeSpace();
|
|
||||||
String internal = (rootTotal / 1024 / 1024) + " MiB total, "
|
|
||||||
+ (rootFree / 1024 / 1024) + " MiB free";
|
|
||||||
builder.customData("Internal storage", internal);
|
|
||||||
|
|
||||||
// External storage (SD card)
|
|
||||||
File sd = Environment.getExternalStorageDirectory();
|
|
||||||
long sdTotal = sd.getTotalSpace();
|
|
||||||
long sdFree = sd.getFreeSpace();
|
|
||||||
String external = (sdTotal / 1024 / 1024) + " MiB total, "
|
|
||||||
+ (sdFree / 1024 / 1024) + " MiB free";
|
|
||||||
builder.customData("External storage", external);
|
|
||||||
|
|
||||||
// Is mobile data available?
|
|
||||||
o = context.getSystemService(CONNECTIVITY_SERVICE);
|
|
||||||
ConnectivityManager cm = (ConnectivityManager) o;
|
|
||||||
NetworkInfo mobile = cm.getNetworkInfo(TYPE_MOBILE);
|
|
||||||
boolean mobileAvailable = mobile != null && mobile.isAvailable();
|
|
||||||
// Is mobile data enabled?
|
|
||||||
boolean mobileEnabled = false;
|
|
||||||
try {
|
|
||||||
Class<?> clazz = Class.forName(cm.getClass().getName());
|
|
||||||
Method method = clazz.getDeclaredMethod("getMobileDataEnabled");
|
|
||||||
method.setAccessible(true);
|
|
||||||
mobileEnabled = (Boolean) method.invoke(cm);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
// Is mobile data connected ?
|
|
||||||
boolean mobileConnected = mobile != null && mobile.isConnected();
|
|
||||||
|
|
||||||
String mobileStatus;
|
|
||||||
if (mobileAvailable) mobileStatus = "Available, ";
|
|
||||||
else mobileStatus = "Not available, ";
|
|
||||||
if (mobileEnabled) mobileStatus += "enabled, ";
|
|
||||||
else mobileStatus += "not enabled, ";
|
|
||||||
if (mobileConnected) mobileStatus += "connected";
|
|
||||||
else mobileStatus += "not connected";
|
|
||||||
builder.customData("Mobile data status", mobileStatus);
|
|
||||||
|
|
||||||
// Is wifi available?
|
|
||||||
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
|
|
||||||
boolean wifiAvailable = wifi != null && wifi.isAvailable();
|
|
||||||
// Is wifi enabled?
|
|
||||||
WifiManager wm = (WifiManager) context.getSystemService(WIFI_SERVICE);
|
|
||||||
boolean wifiEnabled = wm != null &&
|
|
||||||
wm.getWifiState() == WIFI_STATE_ENABLED;
|
|
||||||
// Is wifi connected?
|
|
||||||
boolean wifiConnected = wifi != null && wifi.isConnected();
|
|
||||||
|
|
||||||
String wifiStatus;
|
|
||||||
if (wifiAvailable) wifiStatus = "Available, ";
|
|
||||||
else wifiStatus = "Not available, ";
|
|
||||||
if (wifiEnabled) wifiStatus += "enabled, ";
|
|
||||||
else wifiStatus += "not enabled, ";
|
|
||||||
if (wifiConnected) wifiStatus += "connected";
|
|
||||||
else wifiStatus += "not connected";
|
|
||||||
builder.customData("Wi-Fi status", wifiStatus);
|
|
||||||
|
|
||||||
if (wm != null) {
|
|
||||||
WifiInfo wifiInfo = wm.getConnectionInfo();
|
|
||||||
if (wifiInfo != null) {
|
|
||||||
int ip = wifiInfo.getIpAddress(); // Nice API, Google
|
|
||||||
int ip1 = ip & 0xFF;
|
|
||||||
int ip2 = (ip >> 8) & 0xFF;
|
|
||||||
int ip3 = (ip >> 16) & 0xFF;
|
|
||||||
int ip4 = (ip >> 24) & 0xFF;
|
|
||||||
String address = ip1 + "." + ip2 + "." + ip3 + "." + ip4;
|
|
||||||
builder.customData("Wi-Fi address", address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is Bluetooth available?
|
|
||||||
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
|
|
||||||
boolean btAvailable = bt != null;
|
|
||||||
// Is Bluetooth enabled?
|
|
||||||
boolean btEnabled = bt != null && bt.isEnabled() &&
|
|
||||||
!StringUtils.isNullOrEmpty(bt.getAddress());
|
|
||||||
// Is Bluetooth connectable?
|
|
||||||
boolean btConnectable = bt != null &&
|
|
||||||
(bt.getScanMode() == SCAN_MODE_CONNECTABLE ||
|
|
||||||
bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
|
||||||
// Is Bluetooth discoverable?
|
|
||||||
boolean btDiscoverable = bt != null &&
|
|
||||||
bt.getScanMode() == SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
|
||||||
|
|
||||||
String btStatus;
|
|
||||||
if (btAvailable) btStatus = "Available, ";
|
|
||||||
else btStatus = "Not available, ";
|
|
||||||
if (btEnabled) btStatus += "enabled, ";
|
|
||||||
else btStatus += "not enabled, ";
|
|
||||||
if (btConnectable) btStatus += "connectable, ";
|
|
||||||
else btStatus += "not connectable, ";
|
|
||||||
if (btDiscoverable) btStatus += "discoverable";
|
|
||||||
else btStatus += "not discoverable";
|
|
||||||
builder.customData("Bluetooth status", btStatus);
|
|
||||||
|
|
||||||
if (bt != null) builder.customData("Bluetooth address", bt.getAddress());
|
|
||||||
String btSettingsAddr;
|
|
||||||
try {
|
|
||||||
btSettingsAddr = Settings.Secure.getString(context.getContentResolver(),
|
|
||||||
"bluetooth_address");
|
|
||||||
} catch (SecurityException e) {
|
|
||||||
btSettingsAddr = "Could not get address from settings";
|
|
||||||
}
|
|
||||||
builder.customData("Bluetooth address from settings", btSettingsAddr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user