mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Ability to manually update the blacklists
The update status is now also shown
This commit is contained in:
parent
0edba60206
commit
ebfcea7217
@ -107,6 +107,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
private ConnectivityManager.NetworkCallback mNetworkCallback;
|
||||
private AppsResolver appsResolver;
|
||||
private boolean mMalwareDetectionEnabled;
|
||||
private boolean mBlacklistsUpdateRequested;
|
||||
private Blacklists mBlacklists;
|
||||
|
||||
/* The maximum connections to log into the ConnectionsRegister. Older connections are dropped.
|
||||
@ -544,13 +545,14 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
if(!mMalwareDetectionEnabled || (mBlacklistsUpdateThread != null))
|
||||
return;
|
||||
|
||||
if(mBlacklists.needsUpdate()) {
|
||||
if(mBlacklistsUpdateRequested || mBlacklists.needsUpdate()) {
|
||||
mBlacklistsUpdateThread = new Thread(this::updateBlacklistsWork, "Blacklists Update");
|
||||
mBlacklistsUpdateThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateBlacklistsWork() {
|
||||
mBlacklistsUpdateRequested = false;
|
||||
mBlacklists.update();
|
||||
reloadBlacklists();
|
||||
mBlacklistsUpdateThread = null;
|
||||
@ -617,6 +619,15 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
(INSTANCE.isRootCapture() == 1));
|
||||
}
|
||||
|
||||
public static void requestBlacklistsUpdate() {
|
||||
if(INSTANCE != null) {
|
||||
INSTANCE.mBlacklistsUpdateRequested = true;
|
||||
|
||||
// Wake the update thread to run the blacklist thread
|
||||
INSTANCE.mPendingUpdates.push(new Pair<>(new ConnectionDescriptor[0], new ConnectionUpdate[0]));
|
||||
}
|
||||
}
|
||||
|
||||
// Inside the mCaptureThread
|
||||
@Override
|
||||
public void run() {
|
||||
@ -652,6 +663,8 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
ConnectionDescriptor[] new_conns = item.first;
|
||||
ConnectionUpdate[] conns_updates = item.second;
|
||||
|
||||
checkBlacklistsUpdates();
|
||||
|
||||
// synchronize the conn_reg to ensure that newConnections and connectionsUpdates run atomically
|
||||
// thus preventing the ConnectionsAdapter from interleaving other operations
|
||||
synchronized (conn_reg) {
|
||||
@ -744,8 +757,6 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
}
|
||||
|
||||
public void updateConnections(ConnectionDescriptor[] new_conns, ConnectionUpdate[] conns_updates) {
|
||||
checkBlacklistsUpdates();
|
||||
|
||||
// Put the update into a queue to avoid performing much work on the capture thread.
|
||||
// This will be processed by mConnUpdateThread.
|
||||
mPendingUpdates.push(new Pair<>(new_conns, conns_updates));
|
||||
|
||||
@ -30,8 +30,8 @@ import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.fragments.EditListFragment;
|
||||
import com.emanuelef.remote_capture.fragments.MalwareBlacklists;
|
||||
import com.emanuelef.remote_capture.fragments.MalwareStatus;
|
||||
import com.emanuelef.remote_capture.fragments.BlacklistsFragment;
|
||||
import com.emanuelef.remote_capture.fragments.MalwareStatusFragment;
|
||||
import com.emanuelef.remote_capture.model.ListInfo;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
@ -65,9 +65,9 @@ public class MalwareDetection extends BaseActivity {
|
||||
switch (position) {
|
||||
default: // Deliberate fall-through to status tab
|
||||
case POS_STATUS:
|
||||
return new MalwareStatus();
|
||||
return new MalwareStatusFragment();
|
||||
case POS_BLACKLISTS:
|
||||
return new MalwareBlacklists();
|
||||
return new BlacklistsFragment();
|
||||
case POS_WHITELIST:
|
||||
return EditListFragment.newInstance(ListInfo.Type.MALWARE_WHITELIST);
|
||||
}
|
||||
|
||||
@ -19,8 +19,18 @@
|
||||
|
||||
package com.emanuelef.remote_capture.fragments;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ListView;
|
||||
@ -28,20 +38,27 @@ import android.widget.ListView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.emanuelef.remote_capture.CaptureService;
|
||||
import com.emanuelef.remote_capture.PCAPdroid;
|
||||
import com.emanuelef.remote_capture.R;
|
||||
import com.emanuelef.remote_capture.adapters.BlacklistsAdapter;
|
||||
import com.emanuelef.remote_capture.interfaces.BlacklistsStateListener;
|
||||
import com.emanuelef.remote_capture.model.Blacklists;
|
||||
|
||||
public class MalwareBlacklists extends Fragment implements BlacklistsStateListener {
|
||||
BlacklistsAdapter mAdapter;
|
||||
Blacklists mBlacklists;
|
||||
public class BlacklistsFragment extends Fragment implements BlacklistsStateListener {
|
||||
private static final String TAG = "BlacklistsFragment";
|
||||
private BlacklistsAdapter mAdapter;
|
||||
private Blacklists mBlacklists;
|
||||
private MenuItem mUpdateItem;
|
||||
private BroadcastReceiver mReceiver;
|
||||
private Handler mHandler;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
setHasOptionsMenu(true);
|
||||
return inflater.inflate(R.layout.malware_detection_blacklists, container, false);
|
||||
}
|
||||
|
||||
@ -51,23 +68,70 @@ public class MalwareBlacklists extends Fragment implements BlacklistsStateListen
|
||||
mAdapter = new BlacklistsAdapter(view.getContext(), PCAPdroid.getInstance().getBlacklists().iter());
|
||||
ListView listView = view.findViewById(R.id.listview);
|
||||
listView.setAdapter(mAdapter);
|
||||
|
||||
mHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String status = intent.getStringExtra(CaptureService.SERVICE_STATUS_KEY);
|
||||
|
||||
if(status != null)
|
||||
refreshStatus();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mBlacklists.addOnChangeListener(this);
|
||||
|
||||
LocalBroadcastManager.getInstance(requireContext())
|
||||
.registerReceiver(mReceiver, new IntentFilter(CaptureService.ACTION_SERVICE_STATUS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mBlacklists.removeOnChangeListener(this);
|
||||
|
||||
LocalBroadcastManager.getInstance(requireContext())
|
||||
.unregisterReceiver(mReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.blacklists_menu, menu);
|
||||
mUpdateItem = menu.findItem(R.id.update);
|
||||
refreshStatus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
||||
if(id == R.id.update) {
|
||||
CaptureService.requestBlacklistsUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void refreshStatus() {
|
||||
if(mAdapter != null)
|
||||
mAdapter.notifyDataSetChanged();
|
||||
|
||||
if(mUpdateItem != null) {
|
||||
mUpdateItem.setVisible(CaptureService.isServiceActive());
|
||||
mUpdateItem.setEnabled(!mBlacklists.isUpdateInProgress());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBlacklistsStateChanged() {
|
||||
if(mAdapter != null)
|
||||
mAdapter.notifyDataSetChanged();
|
||||
Log.d(TAG, "onBlacklistsStateChanged");
|
||||
mHandler.post(this::refreshStatus);
|
||||
}
|
||||
}
|
||||
@ -192,7 +192,7 @@ public class EditListFragment extends Fragment {
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
return false;
|
||||
}
|
||||
|
||||
private void recheckListSize() {
|
||||
|
||||
@ -45,7 +45,7 @@ import com.emanuelef.remote_capture.activities.ConnectionsActivity;
|
||||
import com.emanuelef.remote_capture.model.Blacklists;
|
||||
import com.emanuelef.remote_capture.model.FilterDescriptor;
|
||||
|
||||
public class MalwareStatus extends Fragment {
|
||||
public class MalwareStatusFragment extends Fragment {
|
||||
private static final String TAG = "MalwareStatus";
|
||||
private Blacklists mBlacklists;
|
||||
private Handler mHandler;
|
||||
@ -32,6 +32,7 @@ public class BlacklistDescriptor {
|
||||
public final String url;
|
||||
long mLastUpdate = 0;
|
||||
boolean mUpToDate = false;
|
||||
boolean mUpdating = false;
|
||||
public boolean loaded = false;
|
||||
public int num_rules = 0;
|
||||
|
||||
@ -44,6 +45,7 @@ public class BlacklistDescriptor {
|
||||
public enum Status {
|
||||
NOT_LOADED,
|
||||
OUTDATED,
|
||||
UPDATING,
|
||||
UP_TO_DATE
|
||||
}
|
||||
|
||||
@ -54,11 +56,18 @@ public class BlacklistDescriptor {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public void setUpdating() {
|
||||
mUpdating = true;
|
||||
mUpToDate = false;
|
||||
}
|
||||
|
||||
public void setOutdated() {
|
||||
mUpdating = false;
|
||||
mUpToDate = false;
|
||||
}
|
||||
|
||||
public void setUpdated(long now) {
|
||||
mUpdating = false;
|
||||
mLastUpdate = now;
|
||||
mUpToDate = (mLastUpdate != 0);
|
||||
}
|
||||
@ -72,6 +81,8 @@ public class BlacklistDescriptor {
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
if(mUpdating)
|
||||
return Status.UPDATING;
|
||||
if(!loaded)
|
||||
return Status.NOT_LOADED;
|
||||
if(!mUpToDate)
|
||||
@ -89,6 +100,9 @@ public class BlacklistDescriptor {
|
||||
case OUTDATED:
|
||||
id = R.string.status_outdated;
|
||||
break;
|
||||
case UPDATING:
|
||||
id = R.string.status_updating;
|
||||
break;
|
||||
case UP_TO_DATE:
|
||||
id = R.string.status_uptodate;
|
||||
break;
|
||||
@ -107,6 +121,9 @@ public class BlacklistDescriptor {
|
||||
case OUTDATED:
|
||||
id = R.color.warning;
|
||||
break;
|
||||
case UPDATING:
|
||||
id = R.color.in_progress;
|
||||
break;
|
||||
case UP_TO_DATE:
|
||||
id = R.color.ok;
|
||||
break;
|
||||
|
||||
@ -64,17 +64,19 @@ public class Blacklists {
|
||||
private final ArrayList<BlacklistsStateListener> mListeners = new ArrayList<>();
|
||||
private final SharedPreferences mPrefs;
|
||||
private final Context mContext;
|
||||
long last_update;
|
||||
boolean first_update;
|
||||
int num_domain_rules;
|
||||
int num_ip_rules;
|
||||
private boolean mFirstUpdate;
|
||||
private boolean mUpdateInProgress;
|
||||
private long mLastUpdate;
|
||||
private int mNumDomainRules;
|
||||
private int mNumIPRules;
|
||||
|
||||
public Blacklists(Context ctx) {
|
||||
last_update = 0;
|
||||
num_domain_rules = 0;
|
||||
num_ip_rules = 0;
|
||||
mLastUpdate = 0;
|
||||
mNumDomainRules = 0;
|
||||
mNumIPRules = 0;
|
||||
mContext = ctx;
|
||||
first_update = true;
|
||||
mFirstUpdate = true;
|
||||
mUpdateInProgress = false;
|
||||
mPrefs = PreferenceManager.getDefaultSharedPreferences(ctx);
|
||||
|
||||
// Domains
|
||||
@ -111,9 +113,9 @@ public class Blacklists {
|
||||
if(!serialized.isEmpty()) {
|
||||
JsonObject obj = JsonParser.parseString(serialized).getAsJsonObject();
|
||||
|
||||
last_update = obj.getAsJsonPrimitive("last_update").getAsLong();
|
||||
num_domain_rules = obj.getAsJsonPrimitive("num_domain_rules").getAsInt();
|
||||
num_ip_rules = obj.getAsJsonPrimitive("num_ip_rules").getAsInt();
|
||||
mLastUpdate = obj.getAsJsonPrimitive("last_update").getAsLong();
|
||||
mNumDomainRules = obj.getAsJsonPrimitive("num_domain_rules").getAsInt();
|
||||
mNumIPRules = obj.getAsJsonPrimitive("num_ip_rules").getAsInt();
|
||||
|
||||
JsonObject blacklists_obj = obj.getAsJsonObject("blacklists");
|
||||
if(blacklists_obj != null) { // support old format
|
||||
@ -144,9 +146,9 @@ public class Blacklists {
|
||||
}
|
||||
|
||||
JsonObject rv = new JsonObject();
|
||||
rv.add("last_update", new JsonPrimitive(src.last_update));
|
||||
rv.add("num_domain_rules", new JsonPrimitive(src.num_domain_rules));
|
||||
rv.add("num_ip_rules", new JsonPrimitive(src.num_ip_rules));
|
||||
rv.add("last_update", new JsonPrimitive(src.mLastUpdate));
|
||||
rv.add("num_domain_rules", new JsonPrimitive(src.mNumDomainRules));
|
||||
rv.add("num_ip_rules", new JsonPrimitive(src.mNumIPRules));
|
||||
rv.add("blacklists", blacklists_obj);
|
||||
|
||||
return rv;
|
||||
@ -179,7 +181,7 @@ public class Blacklists {
|
||||
|
||||
if(!f.exists()) {
|
||||
// must update
|
||||
last_update = 0;
|
||||
mLastUpdate = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,17 +201,19 @@ public class Blacklists {
|
||||
|
||||
public boolean needsUpdate() {
|
||||
long now = System.currentTimeMillis();
|
||||
return((now - last_update) >= BLACKLISTS_UPDATE_SECONDS * 1000)
|
||||
|| (first_update && (getNumUpdatedBlacklists() < getNumBlacklists()));
|
||||
return((now - mLastUpdate) >= BLACKLISTS_UPDATE_SECONDS * 1000)
|
||||
|| (mFirstUpdate && (getNumUpdatedBlacklists() < getNumBlacklists()));
|
||||
}
|
||||
|
||||
// NOTE: invoked in a separate thread (CaptureService.mBlacklistsUpdateThread)
|
||||
public void update() {
|
||||
// NOTE: invoked in a separate thread (CaptureService.mBlacklistsUpdateThread)
|
||||
if(!needsUpdate())
|
||||
return;
|
||||
mUpdateInProgress = true;
|
||||
for(BlacklistDescriptor bl: mLists)
|
||||
bl.setUpdating();
|
||||
notifyListeners();
|
||||
|
||||
Log.d(TAG, "Updating " + mLists.size() + " blacklists...");
|
||||
first_update = false;
|
||||
mFirstUpdate = false;
|
||||
|
||||
for(BlacklistDescriptor bl: mLists) {
|
||||
Log.d(TAG, "\tupdating " + bl.fname + "...");
|
||||
@ -220,7 +224,7 @@ public class Blacklists {
|
||||
bl.setOutdated();
|
||||
}
|
||||
|
||||
last_update = System.currentTimeMillis();
|
||||
mLastUpdate = System.currentTimeMillis();
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -239,6 +243,7 @@ public class Blacklists {
|
||||
int num_loaded = 0;
|
||||
int num_domains = 0;
|
||||
int num_ips = 0;
|
||||
HashSet<String> loaded = new HashSet<>();
|
||||
|
||||
for(NativeBlacklistStatus bl_status: loaded_blacklists) {
|
||||
if(bl_status == null)
|
||||
@ -249,6 +254,7 @@ public class Blacklists {
|
||||
// Update the number of rules
|
||||
bl.num_rules = bl_status.num_rules;
|
||||
bl.loaded = true;
|
||||
loaded.add(bl.fname);
|
||||
|
||||
if(bl.type == BlacklistDescriptor.Type.DOMAIN_BLACKLIST)
|
||||
num_domains += bl_status.num_rules;
|
||||
@ -260,9 +266,17 @@ public class Blacklists {
|
||||
Log.w(TAG, "Loaded unknown blacklist " + bl_status.fname);
|
||||
}
|
||||
|
||||
for(BlacklistDescriptor bl: mLists) {
|
||||
if(!loaded.contains(bl.fname)) {
|
||||
Log.w(TAG, "Blacklist not loaded: " + bl.fname);
|
||||
bl.loaded = false;
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Blacklists loaded: " + num_loaded + " lists, " + num_domains + " domains, " + num_ips + " IPs");
|
||||
num_domain_rules = num_domains;
|
||||
num_ip_rules = num_ips;
|
||||
mNumDomainRules = num_domains;
|
||||
mNumIPRules = num_ips;
|
||||
mUpdateInProgress = false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@ -271,15 +285,15 @@ public class Blacklists {
|
||||
}
|
||||
|
||||
public int getNumLoadedDomainRules() {
|
||||
return num_domain_rules;
|
||||
return mNumDomainRules;
|
||||
}
|
||||
|
||||
public int getNumLoadedIPRules() {
|
||||
return num_ip_rules;
|
||||
return mNumIPRules;
|
||||
}
|
||||
|
||||
public long getLastUpdate() {
|
||||
return last_update;
|
||||
return mLastUpdate;
|
||||
}
|
||||
|
||||
public int getNumBlacklists() {
|
||||
@ -309,5 +323,9 @@ public class Blacklists {
|
||||
public void removeOnChangeListener(BlacklistsStateListener listener) {
|
||||
mListeners.remove(listener);
|
||||
}
|
||||
|
||||
public boolean isUpdateInProgress() {
|
||||
return mUpdateInProgress;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
6
app/src/main/res/drawable/ic_refresh.xml
Normal file
6
app/src/main/res/drawable/ic_refresh.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<vector android:height="24dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
|
||||
</vector>
|
||||
12
app/src/main/res/menu/blacklists_menu.xml
Normal file
12
app/src/main/res/menu/blacklists_menu.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/update"
|
||||
android:title="@string/update_now"
|
||||
android:orderInCategory="10"
|
||||
android:icon="@drawable/ic_refresh"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@ -17,5 +17,6 @@
|
||||
<color name="ok">#0CB350</color>
|
||||
<color name="warning">#FF5722</color>
|
||||
<color name="danger">#F80013</color>
|
||||
<color name="in_progress">#29b6f6</color>
|
||||
<color name="highContrast">#000000</color>
|
||||
</resources>
|
||||
|
||||
@ -231,5 +231,7 @@
|
||||
<string name="domain_rules">Domain rules</string>
|
||||
<string name="ip_rules">IP rules</string>
|
||||
<string name="malware_status_update_failed">Some blacklists are outdated</string>
|
||||
<string name="update_now">Update now</string>
|
||||
<string name="status_updating">Updating…</string>
|
||||
</resources>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user