mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-07-03 21:21:12 +08:00
parent
f4fe45a442
commit
7238f7ea4a
@ -140,6 +140,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
private int mSocks5Port;
|
||||
private String mSocks5Auth;
|
||||
private CaptureStats mLastStats;
|
||||
private boolean mLowMemory;
|
||||
|
||||
/* The maximum connections to log into the ConnectionsRegister. Older connections are dropped.
|
||||
* Max estimated memory usage: less than 4 MB (+8 MB with payload mode minimal). */
|
||||
@ -280,6 +281,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
vpn_ipv4 = VPN_IP_ADDRESS;
|
||||
last_bytes = 0;
|
||||
last_connections = 0;
|
||||
mLowMemory = false;
|
||||
conn_reg = new ConnectionsRegister(this, CONNECTIONS_LOG_SIZE);
|
||||
mPcapUri = null;
|
||||
mDumper = null;
|
||||
@ -731,6 +733,10 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
return((INSTANCE != null) && (INSTANCE.mMitmReceiver != null) && INSTANCE.mMitmReceiver.isProxyRunning());
|
||||
}
|
||||
|
||||
public static boolean isLowMemory() {
|
||||
return((INSTANCE != null) && (INSTANCE.mLowMemory));
|
||||
}
|
||||
|
||||
public static boolean isAlwaysOnVPN() {
|
||||
return((INSTANCE != null) && INSTANCE.mIsAlwaysOnVPN);
|
||||
}
|
||||
@ -936,6 +942,9 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
|
||||
checkBlacklistsUpdates();
|
||||
|
||||
if(!mLowMemory)
|
||||
checkAvailableHeap();
|
||||
|
||||
// synchronize the conn_reg to ensure that newConnections and connectionsUpdates run atomically
|
||||
// thus preventing the ConnectionsAdapter from interleaving other operations
|
||||
synchronized (conn_reg) {
|
||||
@ -978,6 +987,61 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkAvailableHeap() {
|
||||
// This does not account per-app jvm limits
|
||||
long availableHeap = Utils.getAvailableHeap();
|
||||
|
||||
if(availableHeap <= Utils.LOW_HEAP_THRESHOLD) {
|
||||
Log.w(TAG, "Detected low HEAP memory: " + Utils.formatBytes(availableHeap));
|
||||
handleLowMemory();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: this is only called on low system memory (e.g. obtained via getMemoryInfo). The app
|
||||
// may still run out of heap memory, whose monitoring requires polling (see checkAvailableHeap)
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
String lvlStr = Utils.trimlvl2str(level);
|
||||
boolean lowMemory = (level != TRIM_MEMORY_UI_HIDDEN) && (level >= TRIM_MEMORY_RUNNING_LOW);
|
||||
boolean critical = lowMemory && (level >= TRIM_MEMORY_RUNNING_CRITICAL);
|
||||
|
||||
Log.w(TAG, "onTrimMemory: " + lvlStr + " - low= " + lowMemory + ", critical=" + critical);
|
||||
|
||||
if(lowMemory)
|
||||
handleLowMemory();
|
||||
}
|
||||
|
||||
private void handleLowMemory() {
|
||||
Log.w(TAG, "handleLowMemory called");
|
||||
mLowMemory = true;
|
||||
|
||||
if(getCurPayloadMode() == Prefs.PayloadMode.FULL) {
|
||||
Log.w(TAG, "Releasing full payload memory");
|
||||
|
||||
// Disable full payload for new connections
|
||||
mSettings.full_payload = false;
|
||||
setPayloadMode(Prefs.PayloadMode.NONE.ordinal());
|
||||
|
||||
// Release memory for existing connections
|
||||
if(conn_reg != null) {
|
||||
conn_reg.releasePayloadMemory();
|
||||
|
||||
// Reclaim released memory
|
||||
System.gc();
|
||||
|
||||
Log.i(TAG, "Memory stats after GC:\n" + Utils.getMemoryStats(this));
|
||||
}
|
||||
|
||||
if(mSettings.tls_decryption) {
|
||||
// TLS decryption without payload has little use, stop the capture all together
|
||||
stopService();
|
||||
mHandler.post(() -> Utils.showToastLong(this, R.string.capture_stopped_low_memory));
|
||||
} else
|
||||
mHandler.post(() -> Utils.showToastLong(this, R.string.full_payload_memory_released));
|
||||
} else // TODO lower memory consumption (e.g. reduce connections register size)
|
||||
Log.w(TAG, "low memory detected, expect crashes");
|
||||
}
|
||||
|
||||
/* The following methods are called from native code */
|
||||
|
||||
public String getVpnIPv4() {
|
||||
@ -1231,4 +1295,5 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
public static native int getNumCheckedMalwareConnections();
|
||||
public static native int getNumCheckedFirewallConnections();
|
||||
public static native int rootCmd(String prog, String args);
|
||||
public static native void setPayloadMode(int mode);
|
||||
}
|
||||
|
||||
@ -391,4 +391,13 @@ public class ConnectionsRegister {
|
||||
Collections.sort(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
public synchronized void releasePayloadMemory() {
|
||||
Log.d(TAG, "releaseFullPayloadMemory called");
|
||||
|
||||
for(int i=0; i<mCurItems; i++) {
|
||||
ConnectionDescriptor conn = mItemsRing[i];
|
||||
conn.dropPayload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,12 +22,14 @@ package com.emanuelef.remote_capture;
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Dialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.UiModeManager;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.ComponentCallbacks2;
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
@ -132,6 +134,7 @@ public class Utils {
|
||||
public static final int PER_USER_RANGE = 100000;
|
||||
public static final int UID_UNKNOWN = -1;
|
||||
public static final int UID_NO_FILTER = -2;
|
||||
public static final int LOW_HEAP_THRESHOLD = 10485760 /* 10 MB */;
|
||||
private static Boolean rootAvailable = null;
|
||||
private static Locale primaryLocale = null;
|
||||
|
||||
@ -1208,4 +1211,49 @@ public class Utils {
|
||||
public static boolean rootGrantPermission(Context context, String perm) {
|
||||
return CaptureService.rootCmd("pm", String.format("grant --user %d %s %s", getUserId(getPCAPdroidUid(context)), BuildConfig.APPLICATION_ID, perm)) == 0;
|
||||
}
|
||||
|
||||
// Returns the available dalvik vm heap size for this app. Exceeding this size will result into
|
||||
// an OOM exception
|
||||
public static long getAvailableHeap() {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
// maxMemory: max memory which can be allocated on this app vm (should correspond to getMemoryClass)
|
||||
// totalMemory: currently allocated memory (used/unused) by the vm
|
||||
// freeMemory: free portion of the totalMemory
|
||||
long unallocated = runtime.maxMemory() - runtime.totalMemory();
|
||||
return unallocated + runtime.freeMemory();
|
||||
}
|
||||
|
||||
public static String trimlvl2str(int lvl) {
|
||||
switch (lvl) {
|
||||
case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN: return "TRIM_MEMORY_UI_HIDDEN";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE: return "TRIM_MEMORY_RUNNING_MODERATE";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW: return "TRIM_MEMORY_RUNNING_LOW";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL: return "TRIM_MEMORY_RUNNING_CRITICAL";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND: return "TRIM_MEMORY_BACKGROUND";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_MODERATE: return "TRIM_MEMORY_MODERATE";
|
||||
case ComponentCallbacks2.TRIM_MEMORY_COMPLETE: return "TRIM_MEMORY_COMPLETE";
|
||||
default: return "TRIM_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMemoryStats(Context context) {
|
||||
// This accounts system-wide limits
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
|
||||
activityManager.getMemoryInfo(memoryInfo);
|
||||
|
||||
ActivityManager.RunningAppProcessInfo memState = new ActivityManager.RunningAppProcessInfo();
|
||||
ActivityManager.getMyMemoryState(memState);
|
||||
|
||||
// This accounts app-specific limits (dalvik heap)
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
long heapAvailable = getAvailableHeap();
|
||||
boolean heapLow = heapAvailable <= LOW_HEAP_THRESHOLD;
|
||||
|
||||
return "[Runtime] free: " + Utils.formatBytes(runtime.freeMemory()) + ", max: " + Utils.formatBytes(runtime.maxMemory()) + ", allocated: " + Utils.formatBytes(runtime.totalMemory()) + ", available: " + Utils.formatBytes(heapAvailable) + ", low=" + heapLow +
|
||||
"\n[MemoryState] pid: " + memState.pid + ", trimlevel: " + trimlvl2str(memState.lastTrimLevel) +
|
||||
"\n[MemoryInfo] available: " + Utils.formatBytes(memoryInfo.availMem) + ", total: " + Utils.formatBytes(memoryInfo.totalMem) + ", lowthresh: " + Utils.formatBytes(memoryInfo.threshold) + ", low=" + memoryInfo.lowMemory +
|
||||
"\n[MemoryClass] standard: " + activityManager.getMemoryClass() + " MB, large: " + activityManager.getLargeMemoryClass() + " MB";
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,8 +169,12 @@ public class ConnectionDescriptor {
|
||||
// Payload for decryptable connections should be received via the MitmReceiver
|
||||
assert(isNotDecryptable());
|
||||
|
||||
payload_chunks = update.payload_chunks;
|
||||
payload_truncated = update.payload_truncated;
|
||||
// Some pending updates with payload may still be received after low memory has been
|
||||
// triggered and payload disabled
|
||||
if(!CaptureService.isLowMemory()) {
|
||||
payload_chunks = update.payload_chunks;
|
||||
payload_truncated = update.payload_truncated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -298,6 +302,10 @@ public class ConnectionDescriptor {
|
||||
payload_length += chunk.payload.length;
|
||||
}
|
||||
|
||||
public void dropPayload() {
|
||||
payload_chunks = null;
|
||||
}
|
||||
|
||||
private boolean hasHttp(boolean is_sent) {
|
||||
if(getNumPayloadChunks() == 0)
|
||||
return false;
|
||||
|
||||
@ -806,6 +806,21 @@ Java_com_emanuelef_remote_1capture_CaptureService_rootCmd(JNIEnv *env, jclass cl
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_emanuelef_remote_1capture_CaptureService_setPayloadMode(JNIEnv *env, jclass clazz, jint mode) {
|
||||
pcapdroid_t *pd = global_pd;
|
||||
if(!pd) {
|
||||
log_e("NULL pd instance");
|
||||
return;
|
||||
}
|
||||
|
||||
pd->payload_mode = mode;
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
char* getStringPref(pcapdroid_t *pd, const char *key, char *buf, int bufsize) {
|
||||
JNIEnv *env = pd->env;
|
||||
|
||||
|
||||
@ -362,4 +362,6 @@
|
||||
<string name="permission_granted">%1$s permission was granted</string>
|
||||
<string name="permission_grant_fail">%1$s permission could not be granted</string>
|
||||
<string name="connection_not_found">Could not find the given connection</string>
|
||||
<string name="full_payload_memory_released">The app ran out of memory. Connections payload is now disabled</string>
|
||||
<string name="capture_stopped_low_memory">Capture stopped due to low memory</string>
|
||||
</resources>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user