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.LocationManager;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import com.google.android.gms.location.ILocationListener;
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.content.pm.PackageManager.PERMISSION_GRANTED;
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_NO_POWER;
public class GoogleLocationManager implements LocationChangeListener {
private static final String TAG = "GmsLocManager";
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 String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
private final Context context;
private final Handler handler;
private final Runnable verifyCurrentRequestsRunnable = this::verifyCurrentRequests;
private final RealLocationProvider gpsProvider;
private final UnifiedLocationProvider networkProvider;
private final MockLocationProvider mockProvider;
@ -69,6 +74,7 @@ public class GoogleLocationManager implements LocationChangeListener {
this.networkProvider = null;
}
mockProvider = new MockLocationProvider(this);
handler = new Handler(Looper.getMainLooper());
}
public void invokeOnceReady(Runnable runnable) {
@ -203,6 +209,7 @@ public class GoogleLocationManager implements LocationChangeListener {
} catch (RemoteException ignored) {
}
}
verifyCurrentRequests();
}
public void setMockMode(boolean mockMode) {
@ -217,6 +224,22 @@ public class GoogleLocationManager implements LocationChangeListener {
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
public void onLocationChanged() {
for (int i = 0; i < currentRequests.size(); i++) {

View File

@ -89,12 +89,33 @@ public class LocationRequestHelper {
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}
*/
public boolean report(Location location) {
if (location == null) return true;
if (!hasCoarsePermission()) return false;
if (!isActive()) return false;
if (lastReport != null) {
if (location.getTime() - lastReport.getTime() < locationRequest.getFastestInterval()) {
return true;

View File

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