Skip to content
Snippets Groups Projects
Commit 68d98b50 authored by Torsten Grote's avatar Torsten Grote
Browse files

Merge branch '938-ignore-play-services-overlay-permission' into 'master'

When checking for overlay apps, ignore Play Services

Closes #938

See merge request !521
parents 46920f3b 57d4d654
No related branches found
No related tags found
No related merge requests found
...@@ -6,10 +6,10 @@ import android.content.Context; ...@@ -6,10 +6,10 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.support.annotation.NonNull; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.preference.PreferenceManager; import android.support.v7.preference.PreferenceManager;
...@@ -19,11 +19,16 @@ import org.briarproject.bramble.api.lifecycle.ServiceException; ...@@ -19,11 +19,16 @@ import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
...@@ -31,32 +36,57 @@ import java.util.concurrent.Callable; ...@@ -31,32 +36,57 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class ScreenFilterMonitorImpl extends BroadcastReceiver public class ScreenFilterMonitorImpl extends BroadcastReceiver
implements Service, implements Service, ScreenFilterMonitor {
ScreenFilterMonitor {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ScreenFilterMonitorImpl.class.getName()); Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
private static final String PREF_SCREEN_FILTER_APPS = private static final String PREF_SCREEN_FILTER_APPS =
"shownScreenFilterApps"; "shownScreenFilterApps";
/*
* Ignore Play Services if it uses this package name and public key - it's
* effectively a system app, but not flagged as such on older systems
*/
private static final String PLAY_SERVICES_PACKAGE =
"com.google.android.gms";
private static final String PLAY_SERVICES_PUBLIC_KEY =
"30820120300D06092A864886F70D01010105000382010D0030820108" +
"0282010100AB562E00D83BA208AE0A966F124E29DA11F2AB56D08F58" +
"E2CCA91303E9B754D372F640A71B1DCB130967624E4656A7776A9219" +
"3DB2E5BFB724A91E77188B0E6A47A43B33D9609B77183145CCDF7B2E" +
"586674C9E1565B1F4C6A5955BFF251A63DABF9C55C27222252E875E4" +
"F8154A645F897168C0B1BFC612EABF785769BB34AA7984DC7E2EA276" +
"4CAE8307D8C17154D7EE5F64A51A44A602C249054157DC02CD5F5C0E" +
"55FBEF8519FBE327F0B1511692C5A06F19D18385F5C4DBC2D6B93F68" +
"CC2979C70E18AB93866B3BD5DB8999552A0E3B4C99DF58FB918BEDC1" +
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
"0B145B6AA192858E79020103";
private final Context appContext; private final Context appContext;
private final AndroidExecutor androidExecutor; private final AndroidExecutor androidExecutor;
private final LinkedList<String> appNames = new LinkedList<>();
private final PackageManager pm; private final PackageManager pm;
private final SharedPreferences prefs; private final SharedPreferences prefs;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the UI thread
private final Set<String> apps = new HashSet<>(); private final Set<String> apps = new HashSet<>();
private final Set<String> shownApps; private final Set<String> shownApps;
// Used solely for the UiThread
private boolean serviceStarted = false; private boolean serviceStarted = false;
@Inject @Inject
...@@ -75,7 +105,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver ...@@ -75,7 +105,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
@Override @Override
public Void call() { public Void call() {
IntentFilter intentFilter = new IntentFilter(); IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED); intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addDataScheme("package"); intentFilter.addDataScheme("package");
appContext.registerReceiver(ScreenFilterMonitorImpl.this, appContext.registerReceiver(ScreenFilterMonitorImpl.this,
intentFilter); intentFilter);
...@@ -109,9 +139,8 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver ...@@ -109,9 +139,8 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
} }
private Set<String> getShownScreenFilterApps() { private Set<String> getShownScreenFilterApps() {
// res must not be modified // Result must not be modified
Set<String> s = Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
HashSet<String> result = new HashSet<>(); HashSet<String> result = new HashSet<>();
if (s != null) { if (s != null) {
result.addAll(s); result.addAll(s);
...@@ -121,7 +150,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver ...@@ -121,7 +150,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
final String packageName = final String packageName =
intent.getData().getEncodedSchemeSpecificPart(); intent.getData().getEncodedSchemeSpecificPart();
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
...@@ -155,7 +184,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver ...@@ -155,7 +184,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
@Override @Override
@UiThread @UiThread
public void storeAppsAsShown(Collection<String> s, boolean persistent) { public void storeAppsAsShown(Collection<String> s, boolean persistent) {
HashSet<String> buf = new HashSet(s); HashSet<String> buf = new HashSet<>(s);
shownApps.addAll(buf); shownApps.addAll(buf);
if (persistent && !s.isEmpty()) { if (persistent && !s.isEmpty()) {
buf.addAll(getShownScreenFilterApps()); buf.addAll(getShownScreenFilterApps());
...@@ -168,7 +197,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver ...@@ -168,7 +197,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
private Set<String> getInstalledScreenFilterApps() { private Set<String> getInstalledScreenFilterApps() {
HashSet<String> screenFilterApps = new HashSet<>(); HashSet<String> screenFilterApps = new HashSet<>();
List<PackageInfo> packageInfos = List<PackageInfo> packageInfos =
pm.getInstalledPackages(PackageManager.GET_PERMISSIONS); pm.getInstalledPackages(GET_PERMISSIONS);
for (PackageInfo packageInfo : packageInfos) { for (PackageInfo packageInfo : packageInfos) {
if (isOverlayApp(packageInfo)) { if (isOverlayApp(packageInfo)) {
String name = pkgToString(packageInfo); String name = pkgToString(packageInfo);
...@@ -180,25 +209,22 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver ...@@ -180,25 +209,22 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
return screenFilterApps; return screenFilterApps;
} }
// Checks if pkg uses the SYSTEM_ALERT_WINDOW permission and if so // Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
// returns the app name. // returns the app name.
@Nullable @Nullable
private String isOverlayApp(String pkg) { private String isOverlayApp(String pkg) {
try { try {
PackageInfo pkgInfo = PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
if (isOverlayApp(pkgInfo)) { if (isOverlayApp(pkgInfo)) {
return pkgToString(pkgInfo); return pkgToString(pkgInfo);
} }
} catch (PackageManager.NameNotFoundException ignored) { } catch (NameNotFoundException e) {
if (LOG.isLoggable(Level.WARNING)) { if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
LOG.warning("Package name not found: " + pkg);
}
} }
return null; return null;
} }
// Fetch the application name for a given package. // Fetches the application name for a given package.
@Nullable @Nullable
private String pkgToString(PackageInfo pkgInfo) { private String pkgToString(PackageInfo pkgInfo) {
CharSequence seq = pm.getApplicationLabel(pkgInfo.applicationInfo); CharSequence seq = pm.getApplicationLabel(pkgInfo.applicationInfo);
...@@ -208,25 +234,49 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver ...@@ -208,25 +234,49 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
return null; return null;
} }
// Checks if an installed pkg is a user app using the permission. // Checks if an installed package is a user app using the permission.
private boolean isOverlayApp(PackageInfo packageInfo) { private boolean isOverlayApp(PackageInfo packageInfo) {
int mask = ApplicationInfo.FLAG_SYSTEM | int mask = FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP;
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
// Ignore system apps // Ignore system apps
if ((packageInfo.applicationInfo.flags & mask) != 0) { if ((packageInfo.applicationInfo.flags & mask) != 0) {
return false; return false;
} }
//Get Permissions // Ignore Play Services, it's effectively a system app
String[] requestedPermissions = if (isPlayServices(packageInfo.packageName)) {
packageInfo.requestedPermissions; return false;
}
// Get permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) { if (requestedPermissions != null) {
for (String requestedPermission : requestedPermissions) { for (String requestedPermission : requestedPermissions) {
if (requestedPermission if (requestedPermission.equals(SYSTEM_ALERT_WINDOW)) {
.equals(SYSTEM_ALERT_WINDOW)) {
return true; return true;
} }
} }
} }
return false; return false;
} }
private boolean isPlayServices(String pkg) {
if (!PLAY_SERVICES_PACKAGE.equals(pkg)) return false;
try {
PackageInfo sigs = pm.getPackageInfo(pkg, GET_SIGNATURES);
// The genuine Play Services app should have a single signature
Signature[] signatures = sigs.signatures;
if (signatures == null || signatures.length != 1) return false;
// Extract the public key from the signature
CertificateFactory certFactory =
CertificateFactory.getInstance("X509");
byte[] signatureBytes = signatures[0].toByteArray();
InputStream in = new ByteArrayInputStream(signatureBytes);
X509Certificate cert =
(X509Certificate) certFactory.generateCertificate(in);
byte[] publicKeyBytes = cert.getPublicKey().getEncoded();
String publicKey = StringUtils.toHexString(publicKeyBytes);
return PLAY_SERVICES_PUBLIC_KEY.equals(publicKey);
} catch (NameNotFoundException | CertificateException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return false;
}
}
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment