Location: Ensure we don't keep GPS active indoors when requesting app is already gone

This commit is contained in:
Marvin W 2021-12-08 12:05:31 +01:00
parent fba6fbdfd0
commit d16d438350
No known key found for this signature in database
GPG Key ID: 072E9235DB996F2A
3 changed files with 53 additions and 6 deletions

View File

@ -22,7 +22,10 @@ import android.content.Context;
import android.location.Location; import android.location.Location;
import android.location.LocationManager; import android.location.LocationManager;
import android.os.Binder; import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log;
import com.google.android.gms.location.ILocationListener; import com.google.android.gms.location.ILocationListener;
import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationRequest;
@ -39,17 +42,19 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.location.LocationManager.GPS_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY; import static com.google.android.gms.location.LocationRequest.PRIORITY_HIGH_ACCURACY;
import static com.google.android.gms.location.LocationRequest.PRIORITY_NO_POWER; import static com.google.android.gms.location.LocationRequest.PRIORITY_NO_POWER;
public class GoogleLocationManager implements LocationChangeListener { public class GoogleLocationManager implements LocationChangeListener {
private static final String TAG = "GmsLocManager"; private static final String TAG = "GmsLocManager";
private static final String MOCK_PROVIDER = "mock"; private static final String MOCK_PROVIDER = "mock";
private static final long VERIFY_CURRENT_REQUESTS_INTERVAL_MS = 5000; // 5 seconds
private static final long SWITCH_ON_FRESHNESS_CLIFF_MS = 30000; // 30 seconds private static final long SWITCH_ON_FRESHNESS_CLIFF_MS = 30000; // 30 seconds
private static final String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION"; private static final String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
private final Context context; private final Context context;
private final Handler handler;
private final Runnable verifyCurrentRequestsRunnable = this::verifyCurrentRequests;
private final RealLocationProvider gpsProvider; private final RealLocationProvider gpsProvider;
private final UnifiedLocationProvider networkProvider; private final UnifiedLocationProvider networkProvider;
private final MockLocationProvider mockProvider; private final MockLocationProvider mockProvider;
@ -69,6 +74,7 @@ public class GoogleLocationManager implements LocationChangeListener {
this.networkProvider = null; this.networkProvider = null;
} }
mockProvider = new MockLocationProvider(this); mockProvider = new MockLocationProvider(this);
handler = new Handler(Looper.getMainLooper());
} }
public void invokeOnceReady(Runnable runnable) { public void invokeOnceReady(Runnable runnable) {
@ -203,6 +209,7 @@ public class GoogleLocationManager implements LocationChangeListener {
} catch (RemoteException ignored) { } catch (RemoteException ignored) {
} }
} }
verifyCurrentRequests();
} }
public void setMockMode(boolean mockMode) { public void setMockMode(boolean mockMode) {
@ -217,6 +224,22 @@ public class GoogleLocationManager implements LocationChangeListener {
mockProvider.setLocation(mockLocation); mockProvider.setLocation(mockLocation);
} }
private void verifyCurrentRequests() {
handler.removeCallbacks(verifyCurrentRequestsRunnable);
try {
for (int i = 0; i < currentRequests.size(); i++) {
LocationRequestHelper request = currentRequests.get(i);
if (!request.isActive()) {
removeLocationUpdates(request);
i--;
}
}
} catch (Exception e) {
Log.w(TAG, e);
}
handler.postDelayed(verifyCurrentRequestsRunnable, VERIFY_CURRENT_REQUESTS_INTERVAL_MS);
}
@Override @Override
public void onLocationChanged() { public void onLocationChanged() {
for (int i = 0; i < currentRequests.size(); i++) { for (int i = 0; i < currentRequests.size(); i++) {

View File

@ -89,12 +89,33 @@ public class LocationRequestHelper {
this.callback = data.callback; this.callback = data.callback;
} }
public boolean isActive() {
if (!hasCoarsePermission()) return false;
if (listener != null) {
try {
return listener.asBinder().isBinderAlive();
} catch (Exception e) {
return false;
}
} else if (pendingIntent != null) {
return true;
} else if (callback != null) {
try {
return callback.asBinder().isBinderAlive();
} catch (Exception e) {
return false;
}
} else {
return false;
}
}
/** /**
* @return whether to continue sending reports to this {@link LocationRequestHelper} * @return whether to continue sending reports to this {@link LocationRequestHelper}
*/ */
public boolean report(Location location) { public boolean report(Location location) {
if (location == null) return true; if (location == null) return true;
if (!hasCoarsePermission()) return false; if (!isActive()) return false;
if (lastReport != null) { if (lastReport != null) {
if (location.getTime() - lastReport.getTime() < locationRequest.getFastestInterval()) { if (location.getTime() - lastReport.getTime() < locationRequest.getFastestInterval()) {
return true; return true;

View File

@ -82,23 +82,26 @@ class UnifiedLocationProvider(context: Context?, changeListener: LocationChangeL
Log.d(TAG, "unified network: no longer requesting location update") Log.d(TAG, "unified network: no longer requesting location update")
client.removeLocationUpdates(listener) client.removeLocationUpdates(listener)
connected.set(false) connected.set(false)
} else if (!requests.isEmpty()) { } else if (requests.isNotEmpty()) {
var minTime = Long.MAX_VALUE var minTime = Long.MAX_VALUE
var maxUpdates = Int.MAX_VALUE
val sb = StringBuilder() val sb = StringBuilder()
var opPackageName: String? = null var opPackageName: String? = null
for (request in requests) { for (request in requests) {
if (request.locationRequest.interval < minTime) { if (request.locationRequest.interval < minTime) {
opPackageName = request.packageName opPackageName = request.packageName
minTime = request.locationRequest.interval minTime = request.locationRequest.interval
maxUpdates = request.locationRequest.numUpdates
} }
if (sb.isNotEmpty()) sb.append(", ") if (sb.isNotEmpty()) sb.append(", ")
sb.append("${request.packageName}:${request.locationRequest.interval}ms") sb.append("${request.packageName}:${request.locationRequest.interval}ms")
} }
client.opPackageName = opPackageName client.opPackageName = opPackageName
Log.d(TAG, "unified network: requesting location updates with interval ${minTime}ms ($sb)") if (minTime <= 0) {
if (!connected.get() || connectedMinTime != minTime) { client.forceNextUpdate = true
client.requestLocationUpdates(listener, minTime)
} }
Log.d(TAG, "unified network: requesting location updates with interval ${minTime}ms ($sb)")
client.requestLocationUpdates(listener, minTime, maxUpdates)
connected.set(true) connected.set(true)
connectedMinTime = minTime connectedMinTime = minTime
} }