Use network callback on recent Android versions

- no need to request phone permission anymore
- might solve DNS problems (link properties changed)
- preparation for Android O
This commit is contained in:
M66B 2017-07-22 16:06:32 +02:00
parent a1bb7c0358
commit 05b3b0376d
4 changed files with 115 additions and 260 deletions

View File

@ -47,14 +47,10 @@ import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.TwoStatePreference;
import android.support.annotation.NonNull;
import android.support.v4.app.NavUtils;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@ -98,17 +94,10 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
private static final String TAG = "NetGuard.Settings";
private boolean running = false;
private boolean phone_state = false;
private static final int REQUEST_EXPORT = 1;
private static final int REQUEST_IMPORT = 2;
private static final int REQUEST_METERED2 = 3;
private static final int REQUEST_METERED3 = 4;
private static final int REQUEST_METERED4 = 5;
private static final int REQUEST_ROAMING_EU = 6;
private static final int REQUEST_ROAMING_NATIONAL = 7;
private static final int REQUEST_ROAMING_INTERNATIONAL = 8;
private static final int REQUEST_HOSTS = 9;
private static final int REQUEST_HOSTS = 3;
private AlertDialog dialogFilter = null;
@ -401,12 +390,10 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
// Technical info
Preference pref_technical_info = screen.findPreference("technical_info");
Preference pref_technical_network = screen.findPreference("technical_network");
Preference pref_technical_subscription = screen.findPreference("technical_subscription");
pref_technical_info.setEnabled(INTENT_VPN_SETTINGS.resolveActivity(this.getPackageManager()) != null);
pref_technical_info.setIntent(INTENT_VPN_SETTINGS);
pref_technical_info.setOnPreferenceClickListener(listener);
pref_technical_network.setOnPreferenceClickListener(listener);
pref_technical_subscription.setOnPreferenceClickListener(listener);
updateTechnicalInfo();
markPro(screen.findPreference("theme"), ActivityPro.SKU_THEME);
@ -418,9 +405,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
protected void onResume() {
super.onResume();
// Check if permissions were revoked
checkPermissions();
// Listen for preference changes
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
prefs.registerOnSharedPreferenceChangeListener(this);
@ -435,12 +419,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
IntentFilter ifConnectivity = new IntentFilter();
ifConnectivity.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(connectivityChangedReceiver, ifConnectivity);
if (Util.hasPhoneStatePermission(this)) {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_SERVICE_STATE);
phone_state = true;
}
}
@Override
@ -452,12 +430,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
unregisterReceiver(interactiveStateReceiver);
unregisterReceiver(connectivityChangedReceiver);
if (phone_state) {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
phone_state = false;
}
}
@Override
@ -525,16 +497,10 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
"screen_other".equals(name))
ServiceSinkhole.reload("changed " + name, this, false);
else if ("whitelist_roaming".equals(name)) {
if (prefs.getBoolean(name, false)) {
if (Util.hasPhoneStatePermission(this))
ServiceSinkhole.reload("changed " + name, this, false);
else
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_ROAMING_INTERNATIONAL);
} else
ServiceSinkhole.reload("changed " + name, this, false);
else if ("whitelist_roaming".equals(name))
ServiceSinkhole.reload("changed " + name, this, false);
} else if ("auto_enable".equals(name))
else if ("auto_enable".equals(name))
getPreferenceScreen().findPreference(name).setTitle(getString(R.string.setting_auto, prefs.getString(name, "0")));
else if ("screen_delay".equals(name))
@ -569,40 +535,16 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
else if ("unmetered_2g".equals(name) ||
"unmetered_3g".equals(name) ||
"unmetered_4g".equals(name)) {
if (prefs.getBoolean(name, false)) {
if (Util.hasPhoneStatePermission(this))
ServiceSinkhole.reload("changed " + name, this, false);
else {
if ("unmetered_2g".equals(name))
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_METERED2);
else if ("unmetered_3g".equals(name))
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_METERED3);
else if ("unmetered_4g".equals(name))
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_METERED4);
}
} else
ServiceSinkhole.reload("changed " + name, this, false);
"unmetered_4g".equals(name))
ServiceSinkhole.reload("changed " + name, this, false);
} else if ("eu_roaming".equals(name)) {
if (prefs.getBoolean(name, false)) {
if (Util.hasPhoneStatePermission(this))
ServiceSinkhole.reload("changed " + name, this, false);
else
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_ROAMING_EU);
} else
ServiceSinkhole.reload("changed " + name, this, false);
else if ("national_roaming".equals(name))
ServiceSinkhole.reload("changed " + name, this, false);
} else if ("national_roaming".equals(name)) {
if (prefs.getBoolean(name, false)) {
if (Util.hasPhoneStatePermission(this))
ServiceSinkhole.reload("changed " + name, this, false);
else
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_ROAMING_NATIONAL);
} else
ServiceSinkhole.reload("changed " + name, this, false);
else if ("eu_roaming".equals(name))
ServiceSinkhole.reload("changed " + name, this, false);
} else if ("lockdown_wifi".equals(name) || "lockdown_other".equals(name))
else if ("lockdown_wifi".equals(name) || "lockdown_other".equals(name))
ServiceSinkhole.reload("changed " + name, this, false);
else if ("manage_system".equals(name)) {
@ -777,99 +719,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
throw new IllegalArgumentException("Bad address");
}
@TargetApi(Build.VERSION_CODES.M)
private void checkPermissions() {
PreferenceScreen screen = getPreferenceScreen();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
// Check if permission was revoked
if (prefs.getBoolean("whitelist_roaming", false))
if (!Util.hasPhoneStatePermission(this)) {
prefs.edit().putBoolean("whitelist_roaming", false).apply();
((TwoStatePreference) screen.findPreference("whitelist_roaming")).setChecked(false);
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_ROAMING_INTERNATIONAL);
}
// Check if permission was revoked
if (prefs.getBoolean("unmetered_2g", false))
if (!Util.hasPhoneStatePermission(this)) {
prefs.edit().putBoolean("unmetered_2g", false).apply();
((TwoStatePreference) screen.findPreference("unmetered_2g")).setChecked(false);
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_METERED2);
}
if (prefs.getBoolean("unmetered_3g", false))
if (!Util.hasPhoneStatePermission(this)) {
prefs.edit().putBoolean("unmetered_3g", false).apply();
((TwoStatePreference) screen.findPreference("unmetered_3g")).setChecked(false);
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_METERED3);
}
if (prefs.getBoolean("unmetered_4g", false))
if (!Util.hasPhoneStatePermission(this)) {
prefs.edit().putBoolean("unmetered_4g", false).apply();
((TwoStatePreference) screen.findPreference("unmetered_4g")).setChecked(false);
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_METERED4);
}
// Check if permission was revoked
if (prefs.getBoolean("eu_roaming", false))
if (!Util.hasPhoneStatePermission(this)) {
prefs.edit().putBoolean("eu_roaming", false).apply();
((TwoStatePreference) screen.findPreference("eu_roaming")).setChecked(false);
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_ROAMING_EU);
}
if (prefs.getBoolean("national_roaming", false))
if (!Util.hasPhoneStatePermission(this)) {
prefs.edit().putBoolean("national_roaming", false).apply();
((TwoStatePreference) screen.findPreference("national_roaming")).setChecked(false);
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, REQUEST_ROAMING_NATIONAL);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
PreferenceScreen screen = getPreferenceScreen();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean granted = (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED);
if (requestCode == REQUEST_METERED2) {
prefs.edit().putBoolean("unmetered_2g", granted).apply();
((TwoStatePreference) screen.findPreference("unmetered_2g")).setChecked(granted);
} else if (requestCode == REQUEST_METERED3) {
prefs.edit().putBoolean("unmetered_3g", granted).apply();
((TwoStatePreference) screen.findPreference("unmetered_3g")).setChecked(granted);
} else if (requestCode == REQUEST_METERED4) {
prefs.edit().putBoolean("unmetered_4g", granted).apply();
((TwoStatePreference) screen.findPreference("unmetered_4g")).setChecked(granted);
} else if (requestCode == REQUEST_ROAMING_EU) {
prefs.edit().putBoolean("eu_roaming", granted).apply();
((TwoStatePreference) screen.findPreference("eu_roaming")).setChecked(granted);
} else if (requestCode == REQUEST_ROAMING_NATIONAL) {
prefs.edit().putBoolean("national_roaming", granted).apply();
((TwoStatePreference) screen.findPreference("national_roaming")).setChecked(granted);
} else if (requestCode == REQUEST_ROAMING_INTERNATIONAL) {
prefs.edit().putBoolean("whitelist_roaming", granted).apply();
((TwoStatePreference) screen.findPreference("whitelist_roaming")).setChecked(granted);
}
if (granted)
ServiceSinkhole.reload("permission granted", this, false);
}
private BroadcastReceiver interactiveStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@ -886,18 +735,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
}
};
private PhoneStateListener phoneStateListener = new PhoneStateListener() {
@Override
public void onDataConnectionStateChanged(int state) {
updateTechnicalInfo();
}
@Override
public void onServiceStateChanged(ServiceState serviceState) {
updateTechnicalInfo();
}
};
private void markPro(Preference pref, String sku) {
if (sku == null || !IAB.isPurchased(sku, this)) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
@ -912,7 +749,6 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
PreferenceScreen screen = getPreferenceScreen();
Preference pref_technical_info = screen.findPreference("technical_info");
Preference pref_technical_network = screen.findPreference("technical_network");
Preference pref_technical_subscription = screen.findPreference("technical_subscription");
pref_technical_info.setSummary(Util.getGeneralInfo(this));
pref_technical_network.setSummary(Util.getNetworkInfo(this));

View File

@ -531,10 +531,6 @@ public class AdapterRule extends RecyclerView.Adapter<AdapterRule.ViewHolder> im
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
rule.roaming = isChecked;
updateRule(rule, true, listAll);
// Request permissions
if (isChecked && !Util.hasPhoneStatePermission(context))
context.requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE}, ActivityMain.REQUEST_ROAMING);
}
});

View File

@ -40,7 +40,11 @@ import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Typeface;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.TrafficStats;
import android.net.Uri;
import android.net.VpnService;
@ -59,7 +63,6 @@ import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.Spannable;
import android.text.SpannableString;
@ -114,13 +117,15 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
private boolean registeredConnectivityChanged = false;
private boolean registeredPackageChanged = false;
private boolean phone_state = false;
private Object networkCallback = null;
private State state = State.none;
private boolean user_foreground = true;
private boolean last_connected = false;
private boolean last_metered = true;
private boolean last_interactive = false;
private boolean powersaving = false;
private boolean phone_state = false;
private ServiceSinkhole.Builder last_builder = null;
private ParcelFileDescriptor vpn = null;
@ -315,14 +320,6 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
}
}
// Listen for phone state changes
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (tm != null && !phone_state && Util.hasPhoneStatePermission(ServiceSinkhole.this)) {
Log.i(TAG, "Starting listening to service state changes");
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE | PhoneStateListener.LISTEN_SERVICE_STATE);
phone_state = true;
}
// Watchdog
if (cmd == Command.start || cmd == Command.reload) {
Intent watchdogIntent = new Intent(ServiceSinkhole.this, ServiceSinkhole.class);
@ -1900,6 +1897,29 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
}
};
private PhoneStateListener phoneStateListener = new PhoneStateListener() {
private String last_generation = null;
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
if (state == TelephonyManager.DATA_CONNECTED) {
String current_generation = Util.getNetworkGeneration(ServiceSinkhole.this);
Log.i(TAG, "Data connected generation=" + current_generation);
if (last_generation == null || !last_generation.equals(current_generation)) {
Log.i(TAG, "New network generation=" + current_generation);
last_generation = current_generation;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ServiceSinkhole.this);
if (prefs.getBoolean("unmetered_2g", false) ||
prefs.getBoolean("unmetered_3g", false) ||
prefs.getBoolean("unmetered_4g", false))
reload("data connection state changed", ServiceSinkhole.this, false);
}
}
}
};
private BroadcastReceiver packageChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@ -2054,60 +2074,6 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
}
}
private PhoneStateListener phoneStateListener = new PhoneStateListener() {
private String last_generation = null;
private int last_eu = -1;
private int last_national = -1;
@Override
public void onDataConnectionStateChanged(int state, int networkType) {
if (state == TelephonyManager.DATA_CONNECTED) {
String current_generation = Util.getNetworkGeneration(ServiceSinkhole.this);
Log.i(TAG, "Data connected generation=" + current_generation);
if (last_generation == null || !last_generation.equals(current_generation)) {
Log.i(TAG, "New network generation=" + current_generation);
last_generation = current_generation;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ServiceSinkhole.this);
if (prefs.getBoolean("unmetered_2g", false) ||
prefs.getBoolean("unmetered_3g", false) ||
prefs.getBoolean("unmetered_4g", false))
reload("data connection state changed", ServiceSinkhole.this, false);
}
}
}
@Override
public void onServiceStateChanged(ServiceState serviceState) {
if (serviceState.getState() == ServiceState.STATE_IN_SERVICE) {
int current_eu = (Util.isEU(ServiceSinkhole.this) ? 1 : 0);
int current_national = (Util.isNational(ServiceSinkhole.this) ? 1 : 0);
Log.i(TAG, "In service eu=" + current_eu + " national=" + current_national);
boolean reload = false;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ServiceSinkhole.this);
if (last_eu != current_eu) {
Log.i(TAG, "New eu=" + current_eu);
last_eu = current_eu;
if (prefs.getBoolean("eu_roaming", false))
reload = true;
}
if (last_national != current_national) {
Log.i(TAG, "New national=" + current_national);
last_national = current_national;
if (prefs.getBoolean("national_roaming", false))
reload = true;
}
if (reload)
reload("service state changed", ServiceSinkhole.this, false);
}
}
};
@Override
public void onCreate() {
Log.i(TAG, "Create version=" + Util.getSelfVersionName(this) + "/" + Util.getSelfVersionCode(this));
@ -2158,21 +2124,15 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
registeredUser = true;
}
// Listen for idle mode state changes
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// Listen for idle mode state changes
IntentFilter ifIdle = new IntentFilter();
ifIdle.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
registerReceiver(idleStateReceiver, ifIdle);
registeredIdleState = true;
}
// Listen for connectivity updates
IntentFilter ifConnectivity = new IntentFilter();
ifConnectivity.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(connectivityChangedReceiver, ifConnectivity);
registeredConnectivityChanged = true;
// Listen for added applications
// Listen for added/removed applications
IntentFilter ifPackage = new IntentFilter();
ifPackage.addAction(Intent.ACTION_PACKAGE_ADDED);
ifPackage.addAction(Intent.ACTION_PACKAGE_REMOVED);
@ -2180,6 +2140,67 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
registerReceiver(packageChangedReceiver, ifPackage);
registeredPackageChanged = true;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
// Listen for network changes
Log.i(TAG, "Starting listening to network changes");
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
ConnectivityManager.NetworkCallback nc = new ConnectivityManager.NetworkCallback() {
private String last_generation = null;
@Override
public void onAvailable(Network network) {
reload("network available", ServiceSinkhole.this, false);
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {
reload("link properties changed", ServiceSinkhole.this, false);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
String current_generation = Util.getNetworkGeneration(ServiceSinkhole.this);
Log.i(TAG, "Capabilities changed generation=" + current_generation);
if (last_generation == null || !last_generation.equals(current_generation)) {
Log.i(TAG, "New network generation=" + current_generation);
last_generation = current_generation;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ServiceSinkhole.this);
if (prefs.getBoolean("unmetered_2g", false) ||
prefs.getBoolean("unmetered_3g", false) ||
prefs.getBoolean("unmetered_4g", false))
reload("data connection state changed", ServiceSinkhole.this, false);
}
}
@Override
public void onLost(Network network) {
reload("network lost", ServiceSinkhole.this, false);
}
};
cm.registerNetworkCallback(builder.build(), nc);
networkCallback = nc;
} else {
// Listen for connectivity updates
Log.i(TAG, "Starting listening to connectivity changes");
IntentFilter ifConnectivity = new IntentFilter();
ifConnectivity.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(connectivityChangedReceiver, ifConnectivity);
registeredConnectivityChanged = true;
// Listen for phone state changes
Log.i(TAG, "Starting listening to service state changes");
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (tm != null) {
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_DATA_CONNECTION_STATE);
phone_state = true;
}
}
// Setup house holding
Intent alarmIntent = new Intent(this, ServiceSinkhole.class);
alarmIntent.setAction(ACTION_HOUSE_HOLDING);
@ -2320,21 +2341,24 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
unregisterReceiver(idleStateReceiver);
registeredIdleState = false;
}
if (registeredConnectivityChanged) {
unregisterReceiver(connectivityChangedReceiver);
registeredConnectivityChanged = false;
}
if (registeredPackageChanged) {
unregisterReceiver(packageChangedReceiver);
registeredPackageChanged = false;
}
if (networkCallback != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
cm.unregisterNetworkCallback((ConnectivityManager.NetworkCallback) networkCallback);
networkCallback = null;
}
if (registeredConnectivityChanged) {
unregisterReceiver(connectivityChangedReceiver);
registeredConnectivityChanged = false;
}
if (phone_state) {
TelephonyManager tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (tm != null) {
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
phone_state = false;
}
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
phone_state = false;
}
try {

View File

@ -773,7 +773,6 @@ public class Util {
} catch (Throwable ex) {
sb.append("Prepared: ").append((ex.toString())).append("\r\n").append(Log.getStackTraceString(ex));
}
sb.append(String.format("Permission: %B\r\n", hasPhoneStatePermission(context)));
sb.append("\r\n");
sb.append(getGeneralInfo(context));