Skip to content
This repository has been archived by the owner on Apr 23, 2023. It is now read-only.

Commit

Permalink
Multiple process support (#185)
Browse files Browse the repository at this point in the history
* Adds AIDL interface for FusedLocationProviderService

* Use service context looper when registering for location manager updates

* Delivers location updates via AIDL callback interface

* Notifies location availability changes via AIDL callback

* Shutdown location updates if all listeners have been removed

* Move fused location provider service into its own process

* Migrates remaining logic for tracking location updates from service to client manager
  • Loading branch information
ecgreb authored and sarahsnow1 committed May 1, 2017
1 parent 482adae commit 422e770
Show file tree
Hide file tree
Showing 19 changed files with 580 additions and 1,097 deletions.
1 change: 1 addition & 0 deletions lost-sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,6 @@
</intent-filter>
</service>


</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,7 @@ private boolean isMockModePrefEnabled() {

if (prefs.getBoolean(getString(R.string.mock_mode_gpx_key), false)) {
String filename = prefs.getString(getString(R.string.mock_gpx_file_key), null);
File file = new File(getExternalFilesDir(null), filename);
FusedLocationApi.setMockTrace(client, file);
FusedLocationApi.setMockTrace(client, getExternalFilesDir(null).getPath(), filename);
}

final Resources res = getResources();
Expand Down Expand Up @@ -357,4 +356,3 @@ public void clear() {
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ private void setupDisconnectBtn() {

private void requestLocationUpdates() {
LocationRequest request = LocationRequest.create();
request.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
request.setInterval(100);

Intent intent = new Intent(PendingIntentService.ACTION);
Expand Down
4 changes: 3 additions & 1 deletion lost/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

<application>

<service android:name="com.mapzen.android.lost.internal.FusedLocationProviderService"/>
<service
android:name="com.mapzen.android.lost.internal.FusedLocationProviderService"
android:process=":lost"/>
<service android:name="com.mapzen.android.lost.internal.GeofencingIntentService">
<intent-filter>
<action android:name="com.mapzen.lost.action.ACTION_GEOFENCING_SERVICE"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.mapzen.android.lost.api;

parcelable LocationAvailability;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.mapzen.android.lost.api;

parcelable LocationRequest;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.mapzen.android.lost.internal;

import com.mapzen.android.lost.api.LocationAvailability;

interface IFusedLocationProviderCallback {

void onLocationChanged(in Location location);

void onLocationAvailabilityChanged(in LocationAvailability locationAvailability);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.mapzen.android.lost.internal;

import com.mapzen.android.lost.api.LocationAvailability;
import com.mapzen.android.lost.api.LocationRequest;
import com.mapzen.android.lost.internal.IFusedLocationProviderCallback;

interface IFusedLocationProviderService {

void init(in IFusedLocationProviderCallback callback);

Location getLastLocation();

LocationAvailability getLocationAvailability();

void requestLocationUpdates(in LocationRequest request);

void removeLocationUpdates();

void setMockMode(boolean isMockMode);

void setMockLocation(in Location mockLocation);

void setMockTrace(String path, String filename);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import android.os.Looper;
import android.support.annotation.RequiresPermission;

import java.io.File;

import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;

Expand Down Expand Up @@ -169,7 +167,7 @@ PendingResult<Status> requestLocationUpdates(LostApiClient client, LocationReque
* This effects all {@link LostApiClient}s connected to the {@link FusedLocationProviderApi}.
* Mock mode must be enabled before clients are able to call
* {@link FusedLocationProviderApi#setMockLocation(LostApiClient, Location)} and
* {@link FusedLocationProviderApi#setMockTrace(LostApiClient, File)}
* {@link FusedLocationProviderApi#setMockTrace(LostApiClient, String, String)}
*
* @param client Connected client.
* @param isMockMode Whether mock mode should be enabled or not.
Expand All @@ -196,9 +194,11 @@ PendingResult<Status> requestLocationUpdates(LostApiClient client, LocationReque
* Mock mode must be enabled before calling this method.
*
* @param client Connected client.
* @param file GPX file to be used to report location.
* @param path storage directory containing GPX trace to be used to report location.
* @param filename filename of GPX trace to be used to report location.
* @return a {@link PendingResult} for the call to check whether call was successful.
* @throws IllegalStateException if the client is not connected at the time of this call.
*/
PendingResult<Status> setMockTrace(LostApiClient client, final File file);
PendingResult<Status> setMockTrace(LostApiClient client, final String path,
final String filename);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.mapzen.android.lost.api.LocationCallback;
import com.mapzen.android.lost.api.LocationListener;
import com.mapzen.android.lost.api.LocationRequest;
import com.mapzen.android.lost.api.LocationResult;
import com.mapzen.android.lost.api.LostApiClient;
import com.mapzen.android.lost.api.PendingResult;
import com.mapzen.android.lost.api.Status;
Expand All @@ -16,10 +17,12 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.location.Location;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;

import java.io.File;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;

Expand All @@ -30,22 +33,64 @@ public class FusedLocationProviderApiImpl
implements FusedLocationProviderApi, EventCallbacks, ServiceConnection {

private Context context;
private FusedLocationProviderService service;
private FusedLocationServiceConnectionManager serviceConnectionManager;
private boolean isBound;

IFusedLocationProviderService service;

private IFusedLocationProviderCallback.Stub remoteCallback
= new IFusedLocationProviderCallback.Stub() {
public void onLocationChanged(final Location location) throws RemoteException {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override public void run() {
final LostClientManager clientManager = LostClientManager.shared();
ReportedChanges changes = clientManager.reportLocationChanged(location);

LocationAvailability availability;
try {
availability = service.getLocationAvailability();
} catch (RemoteException e) {
throw new RuntimeException(e);
}

ArrayList<Location> locations = new ArrayList<>();
locations.add(location);
final LocationResult result = LocationResult.create(locations);
ReportedChanges pendingIntentChanges = clientManager.sendPendingIntent(
context, location, availability, result);

ReportedChanges callbackChanges = clientManager.reportLocationResult(location, result);

changes.putAll(pendingIntentChanges);
changes.putAll(callbackChanges);
clientManager.updateReportedValues(changes);
}
});
}

@Override public void onLocationAvailabilityChanged(LocationAvailability locationAvailability)
throws RemoteException {
LostClientManager.shared().notifyLocationAvailability(locationAvailability);
}
};

@Override public void onConnect(Context context) {
this.context = context;
final Intent intent = new Intent(context, FusedLocationProviderService.class);
context.bindService(intent, this, Context.BIND_AUTO_CREATE);
}

@Override public void onServiceConnected(IBinder binder) {
FusedLocationProviderService.FusedLocationProviderBinder fusedBinder =
(FusedLocationProviderService.FusedLocationProviderBinder) binder;
if (fusedBinder != null) {
service = fusedBinder.getService();
isBound = true;
service = IFusedLocationProviderService.Stub.asInterface(binder);
isBound = true;

// Register remote callback
if (service != null) {
try {
service.init(remoteCallback);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}

Expand Down Expand Up @@ -95,19 +140,27 @@ public boolean isConnected() {

@Override public Location getLastLocation(LostApiClient client) {
throwIfNotConnected(client);
return service.getLastLocation();
try {
return service.getLastLocation();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}

@Override public LocationAvailability getLocationAvailability(LostApiClient client) {
throwIfNotConnected(client);
return service.getLocationAvailability();
try {
return service.getLocationAvailability();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}

@Override public PendingResult<Status> requestLocationUpdates(LostApiClient client,
LocationRequest request, LocationListener listener) {
throwIfNotConnected(client);
LostClientManager.shared().addListener(client, request, listener);
service.requestLocationUpdates(request);
requestLocationUpdatesInternal(request);
return new SimplePendingResult(true);
}

Expand All @@ -120,69 +173,104 @@ public boolean isConnected() {
LocationRequest request, LocationCallback callback, Looper looper) {
throwIfNotConnected(client);
LostClientManager.shared().addLocationCallback(client, request, callback, looper);
service.requestLocationUpdates(request);
requestLocationUpdatesInternal(request);
return new SimplePendingResult(true);
}

@Override public PendingResult<Status> requestLocationUpdates(LostApiClient client,
LocationRequest request, PendingIntent callbackIntent) {
throwIfNotConnected(client);
LostClientManager.shared().addPendingIntent(client, request, callbackIntent);
service.requestLocationUpdates(request);
requestLocationUpdatesInternal(request);
return new SimplePendingResult(true);
}

/**
* Forwards location update request to the {@link FusedLocationProviderService} to initiate
* and/or update request params.
*/
private void requestLocationUpdatesInternal(LocationRequest request) {
try {
service.requestLocationUpdates(request);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}

@Override public PendingResult<Status> removeLocationUpdates(LostApiClient client,
LocationListener listener) {
throwIfNotConnected(client);
boolean hasResult = LostClientManager.shared().removeListener(client, listener);
service.removeLocationUpdates();
checkAllListenersPendingIntentsAndCallbacks();
return new SimplePendingResult(hasResult);
}

@Override public PendingResult<Status> removeLocationUpdates(LostApiClient client,
PendingIntent callbackIntent) {
throwIfNotConnected(client);
boolean hasResult = LostClientManager.shared().removePendingIntent(client, callbackIntent);
service.removeLocationUpdates();
checkAllListenersPendingIntentsAndCallbacks();
return new SimplePendingResult(hasResult);
}

@Override public PendingResult<Status> removeLocationUpdates(LostApiClient client,
LocationCallback callback) {
throwIfNotConnected(client);
boolean hasResult = LostClientManager.shared().removeLocationCallback(client, callback);
service.removeLocationUpdates();
checkAllListenersPendingIntentsAndCallbacks();
return new SimplePendingResult(hasResult);
}

/**
* Checks if any listeners or pending intents are still registered for location updates. If not,
* then shutdown the location engines.
*/
private void checkAllListenersPendingIntentsAndCallbacks() {
if (LostClientManager.shared().hasNoListeners()) {
try {
service.removeLocationUpdates();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
}

@Override public PendingResult<Status> setMockMode(LostApiClient client, boolean isMockMode) {
throwIfNotConnected(client);
service.setMockMode(isMockMode);
try {
service.setMockMode(isMockMode);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
return new SimplePendingResult(true);
}

@Override public PendingResult<Status> setMockLocation(LostApiClient client,
Location mockLocation) {
throwIfNotConnected(client);
service.setMockLocation(mockLocation);
try {
service.setMockLocation(mockLocation);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
return new SimplePendingResult(true);
}

@Override public PendingResult<Status> setMockTrace(LostApiClient client, File file) {
@Override public PendingResult<Status> setMockTrace(LostApiClient client, String path,
String filename) {
throwIfNotConnected(client);
service.setMockTrace(file);
try {
service.setMockTrace(path, filename);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
return new SimplePendingResult(true);
}

public Map<LostApiClient, Set<LocationListener>> getLocationListeners() {
return LostClientManager.shared().getLocationListeners();
}

public FusedLocationProviderService getService() {
return service;
}

void removeConnectionCallbacks(LostApiClient.ConnectionCallbacks callbacks) {
serviceConnectionManager.removeCallbacks(callbacks);
}
Expand Down

0 comments on commit 422e770

Please sign in to comment.