summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--location/java/android/location/GpsMeasurementListenerTransport.java17
-rw-r--r--location/java/android/location/GpsMeasurementsEvent.java31
-rw-r--r--location/java/android/location/GpsNavigationMessageEvent.java30
-rw-r--r--location/java/android/location/GpsNavigationMessageListenerTransport.java18
-rw-r--r--location/java/android/location/IGpsMeasurementsListener.aidl1
-rw-r--r--location/java/android/location/IGpsNavigationMessageListener.aidl1
-rw-r--r--location/java/android/location/ILocationManager.aidl4
-rw-r--r--location/java/android/location/LocalListenerHelper.java25
-rw-r--r--location/java/android/location/LocationManager.java4
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java10
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java120
-rw-r--r--services/core/java/com/android/server/location/GpsMeasurementsProvider.java59
-rw-r--r--services/core/java/com/android/server/location/GpsNavigationMessageProvider.java60
-rw-r--r--services/core/java/com/android/server/location/GpsStatusListenerHelper.java57
-rw-r--r--services/core/java/com/android/server/location/RemoteListenerHelper.java164
15 files changed, 453 insertions, 148 deletions
diff --git a/location/java/android/location/GpsMeasurementListenerTransport.java b/location/java/android/location/GpsMeasurementListenerTransport.java
index 2d9a372..610da96 100644
--- a/location/java/android/location/GpsMeasurementListenerTransport.java
+++ b/location/java/android/location/GpsMeasurementListenerTransport.java
@@ -26,14 +26,12 @@ import android.os.RemoteException;
*/
class GpsMeasurementListenerTransport
extends LocalListenerHelper<GpsMeasurementsEvent.Listener> {
- private final Context mContext;
private final ILocationManager mLocationManager;
private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport();
public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) {
- super("GpsMeasurementListenerTransport");
- mContext = context;
+ super(context, "GpsMeasurementListenerTransport");
mLocationManager = locationManager;
}
@@ -41,7 +39,7 @@ class GpsMeasurementListenerTransport
protected boolean registerWithServer() throws RemoteException {
return mLocationManager.addGpsMeasurementsListener(
mListenerTransport,
- mContext.getPackageName());
+ getContext().getPackageName());
}
@Override
@@ -59,7 +57,18 @@ class GpsMeasurementListenerTransport
listener.onGpsMeasurementsReceived(event);
}
};
+ foreach(operation);
+ }
+ @Override
+ public void onStatusChanged(final int status) {
+ ListenerOperation<GpsMeasurementsEvent.Listener> operation =
+ new ListenerOperation<GpsMeasurementsEvent.Listener>() {
+ @Override
+ public void execute(GpsMeasurementsEvent.Listener listener) throws RemoteException {
+ listener.onStatusChanged(status);
+ }
+ };
foreach(operation);
}
}
diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java
index e04ed81..94ca920 100644
--- a/location/java/android/location/GpsMeasurementsEvent.java
+++ b/location/java/android/location/GpsMeasurementsEvent.java
@@ -32,6 +32,24 @@ import java.util.Collections;
* @hide
*/
public class GpsMeasurementsEvent implements Parcelable {
+
+ /**
+ * The system does not support tracking of GPS Measurements. This status will not change in the
+ * future.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GPS Measurements are successfully being tracked, it will receive updates once they are
+ * available.
+ */
+ public static final int STATUS_READY = 1;
+
+ /**
+ * GPS provider or Location is disabled, updates will not be received until they are enabled.
+ */
+ public static final int STATUS_GPS_LOCATION_DISABLED = 2;
+
private final GpsClock mClock;
private final Collection<GpsMeasurement> mReadOnlyMeasurements;
@@ -43,7 +61,16 @@ public class GpsMeasurementsEvent implements Parcelable {
* @hide
*/
public interface Listener {
+
+ /**
+ * Returns the latest collected GPS Measurements.
+ */
void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs);
+
+ /**
+ * Returns the latest status of the GPS Measurements sub-system.
+ */
+ void onStatusChanged(int status);
}
public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) {
@@ -103,7 +130,9 @@ public class GpsMeasurementsEvent implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mClock, flags);
- GpsMeasurement[] measurementsArray = mReadOnlyMeasurements.toArray(new GpsMeasurement[0]);
+ int measurementsCount = mReadOnlyMeasurements.size();
+ GpsMeasurement[] measurementsArray =
+ mReadOnlyMeasurements.toArray(new GpsMeasurement[measurementsCount]);
parcel.writeInt(measurementsArray.length);
parcel.writeTypedArray(measurementsArray, flags);
}
diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java
index 50ffa75..b61dac0 100644
--- a/location/java/android/location/GpsNavigationMessageEvent.java
+++ b/location/java/android/location/GpsNavigationMessageEvent.java
@@ -21,9 +21,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import java.security.InvalidParameterException;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
/**
* A class implementing a container for data associated with a navigation message event.
@@ -32,6 +29,24 @@ import java.util.Collections;
* @hide
*/
public class GpsNavigationMessageEvent implements Parcelable {
+
+ /**
+ * The system does not support tracking of GPS Navigation Messages. This status will not change
+ * in the future.
+ */
+ public static int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GPS Navigation Messages are successfully being tracked, it will receive updates once they are
+ * available.
+ */
+ public static int STATUS_READY = 1;
+
+ /**
+ * GPS provider or Location is disabled, updated will not be received until they are enabled.
+ */
+ public static int STATUS_GPS_LOCATION_DISABLED = 2;
+
private final GpsNavigationMessage mNavigationMessage;
/**
@@ -42,7 +57,16 @@ public class GpsNavigationMessageEvent implements Parcelable {
* @hide
*/
public interface Listener {
+
+ /**
+ * Returns the latest collected GPS Navigation Message.
+ */
void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event);
+
+ /**
+ * Returns the latest status of the GPS Navigation Messages sub-system.
+ */
+ void onStatusChanged(int status);
}
public GpsNavigationMessageEvent(GpsNavigationMessage message) {
diff --git a/location/java/android/location/GpsNavigationMessageListenerTransport.java b/location/java/android/location/GpsNavigationMessageListenerTransport.java
index ec4812b..f6ba407 100644
--- a/location/java/android/location/GpsNavigationMessageListenerTransport.java
+++ b/location/java/android/location/GpsNavigationMessageListenerTransport.java
@@ -26,7 +26,6 @@ import android.os.RemoteException;
*/
class GpsNavigationMessageListenerTransport
extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> {
- private final Context mContext;
private final ILocationManager mLocationManager;
private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport();
@@ -34,8 +33,7 @@ class GpsNavigationMessageListenerTransport
public GpsNavigationMessageListenerTransport(
Context context,
ILocationManager locationManager) {
- super("GpsNavigationMessageListenerTransport");
- mContext = context;
+ super(context, "GpsNavigationMessageListenerTransport");
mLocationManager = locationManager;
}
@@ -43,7 +41,7 @@ class GpsNavigationMessageListenerTransport
protected boolean registerWithServer() throws RemoteException {
return mLocationManager.addGpsNavigationMessageListener(
mListenerTransport,
- mContext.getPackageName());
+ getContext().getPackageName());
}
@Override
@@ -62,7 +60,19 @@ class GpsNavigationMessageListenerTransport
listener.onGpsNavigationMessageReceived(event);
}
};
+ foreach(operation);
+ }
+ @Override
+ public void onStatusChanged(final int status) {
+ ListenerOperation<GpsNavigationMessageEvent.Listener> operation =
+ new ListenerOperation<GpsNavigationMessageEvent.Listener>() {
+ @Override
+ public void execute(GpsNavigationMessageEvent.Listener listener)
+ throws RemoteException {
+ listener.onStatusChanged(status);
+ }
+ };
foreach(operation);
}
}
diff --git a/location/java/android/location/IGpsMeasurementsListener.aidl b/location/java/android/location/IGpsMeasurementsListener.aidl
index b34bb6c..cbd3100 100644
--- a/location/java/android/location/IGpsMeasurementsListener.aidl
+++ b/location/java/android/location/IGpsMeasurementsListener.aidl
@@ -23,4 +23,5 @@ import android.location.GpsMeasurementsEvent;
*/
oneway interface IGpsMeasurementsListener {
void onGpsMeasurementsReceived(in GpsMeasurementsEvent event);
+ void onStatusChanged(in int status);
}
diff --git a/location/java/android/location/IGpsNavigationMessageListener.aidl b/location/java/android/location/IGpsNavigationMessageListener.aidl
index 18603fe..a708ea6 100644
--- a/location/java/android/location/IGpsNavigationMessageListener.aidl
+++ b/location/java/android/location/IGpsNavigationMessageListener.aidl
@@ -23,4 +23,5 @@ import android.location.GpsNavigationMessageEvent;
*/
oneway interface IGpsNavigationMessageListener {
void onGpsNavigationMessageReceived(in GpsNavigationMessageEvent event);
+ void onStatusChanged(in int status);
}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 1501710..af76175 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -62,12 +62,12 @@ interface ILocationManager
boolean sendNiResponse(int notifId, int userResponse);
boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName);
- boolean removeGpsMeasurementsListener(in IGpsMeasurementsListener listener);
+ void removeGpsMeasurementsListener(in IGpsMeasurementsListener listener);
boolean addGpsNavigationMessageListener(
in IGpsNavigationMessageListener listener,
in String packageName);
- boolean removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener);
+ void removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener);
// --- deprecated ---
List<String> getAllProviders();
diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java
index 1f3bf67..458c451 100644
--- a/location/java/android/location/LocalListenerHelper.java
+++ b/location/java/android/location/LocalListenerHelper.java
@@ -19,6 +19,7 @@ package android.location;
import com.android.internal.util.Preconditions;
import android.annotation.NonNull;
+import android.content.Context;
import android.os.RemoteException;
import android.util.Log;
@@ -32,17 +33,19 @@ import java.util.HashSet;
* @hide
*/
abstract class LocalListenerHelper<TListener> {
- private final HashSet<TListener> mListeners = new HashSet<TListener>();
+ private final HashSet<TListener> mListeners = new HashSet<>();
+
private final String mTag;
+ private final Context mContext;
- protected LocalListenerHelper(String name) {
+ protected LocalListenerHelper(Context context, String name) {
Preconditions.checkNotNull(name);
+ mContext = context;
mTag = name;
}
public boolean add(@NonNull TListener listener) {
Preconditions.checkNotNull(listener);
-
synchronized (mListeners) {
// we need to register with the service first, because we need to find out if the
// service will actually support the request before we attempt anything
@@ -59,18 +62,15 @@ abstract class LocalListenerHelper<TListener> {
return false;
}
}
-
if (mListeners.contains(listener)) {
return true;
}
- mListeners.add(listener);
+ return mListeners.add(listener);
}
- return true;
}
public void remove(@NonNull TListener listener) {
Preconditions.checkNotNull(listener);
-
synchronized (mListeners) {
boolean removed = mListeners.remove(listener);
boolean isLastRemoved = removed && mListeners.isEmpty();
@@ -78,7 +78,7 @@ abstract class LocalListenerHelper<TListener> {
try {
unregisterFromServer();
} catch (RemoteException e) {
-
+ Log.v(mTag, "Error handling last listener removal", e);
}
}
}
@@ -91,12 +91,15 @@ abstract class LocalListenerHelper<TListener> {
void execute(TListener listener) throws RemoteException;
}
- protected void foreach(ListenerOperation operation) {
+ protected Context getContext() {
+ return mContext;
+ }
+
+ protected void foreach(ListenerOperation<TListener> operation) {
Collection<TListener> listeners;
synchronized (mListeners) {
- listeners = new ArrayList<TListener>(mListeners);
+ listeners = new ArrayList<>(mListeners);
}
-
for (TListener listener : listeners) {
try {
operation.execute(listener);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ed408e0..513a627 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1579,7 +1579,7 @@ public class LocationManager {
* Adds a GPS Measurement listener.
*
* @param listener a {@link GpsMeasurementsEvent.Listener} object to register.
- * @return {@code true} if the listener was successfully registered, {@code false} otherwise.
+ * @return {@code true} if the listener was added successfully, {@code false} otherwise.
*
* @hide
*/
@@ -1602,7 +1602,7 @@ public class LocationManager {
* Adds a GPS Navigation Message listener.
*
* @param listener a {@link GpsNavigationMessageEvent.Listener} object to register.
- * @return {@code true} if the listener was successfully registered, {@code false} otherwise.
+ * @return {@code true} if the listener was added successfully, {@code false} otherwise.
*
* @hide
*/
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index d9c96e4..be83b9b 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -60,6 +60,8 @@ import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
+import android.location.GpsMeasurementsEvent;
+import android.location.GpsNavigationMessageEvent;
import android.location.IGpsMeasurementsListener;
import android.location.IGpsNavigationMessageListener;
import android.location.IGpsStatusListener;
@@ -1859,8 +1861,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean removeGpsMeasurementsListener(IGpsMeasurementsListener listener) {
- return mGpsMeasurementsProvider.removeListener(listener);
+ public void removeGpsMeasurementsListener(IGpsMeasurementsListener listener) {
+ mGpsMeasurementsProvider.removeListener(listener);
}
@Override
@@ -1888,8 +1890,8 @@ public class LocationManagerService extends ILocationManager.Stub {
}
@Override
- public boolean removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) {
- return mGpsNavigationMessageProvider.removeListener(listener);
+ public void removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) {
+ mGpsNavigationMessageProvider.removeListener(listener);
}
@Override
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index 46c8221..7354137 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -162,6 +162,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final int GPS_CAPABILITY_MSA = 0x0000004;
private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
+ private static final int GPS_CAPABILITY_GEOFENCING = 0x0000020;
+ private static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
+ private static final int GPS_CAPABILITY_NAV_MESSAGES = 0x0000080;
// The AGPS SUPL mode
private static final int AGPS_SUPL_MODE_MSA = 0x02;
@@ -348,20 +351,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final ILocationManager mILocationManager;
private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle();
- private GpsStatusListenerHelper mListenerHelper = new GpsStatusListenerHelper() {
- @Override
- protected boolean isSupported() {
- return GpsLocationProvider.isSupported();
- }
-
- @Override
- protected boolean registerWithService() {
- return true;
- }
-
- @Override
- protected void unregisterFromService() {}
- };
+ private final GpsStatusListenerHelper mListenerHelper;
+ private final GpsMeasurementsProvider mGpsMeasurementsProvider;
+ private final GpsNavigationMessageProvider mGpsNavigationMessageProvider;
// Handler for processing events
private Handler mHandler;
@@ -409,41 +401,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
};
- private final GpsMeasurementsProvider mGpsMeasurementsProvider = new GpsMeasurementsProvider() {
- @Override
- public boolean isSupported() {
- return native_is_measurement_supported();
- }
-
- @Override
- protected boolean registerWithService() {
- return native_start_measurement_collection();
- }
-
- @Override
- protected void unregisterFromService() {
- native_stop_measurement_collection();
- }
- };
-
- private final GpsNavigationMessageProvider mGpsNavigationMessageProvider =
- new GpsNavigationMessageProvider() {
- @Override
- protected boolean isSupported() {
- return native_is_navigation_message_supported();
- }
-
- @Override
- protected boolean registerWithService() {
- return native_start_navigation_message_collection();
- }
-
- @Override
- protected void unregisterFromService() {
- native_stop_navigation_message_collection();
- }
- };
-
public IGpsStatusProvider getGpsStatusProvider() {
return mGpsStatusProvider;
}
@@ -696,6 +653,62 @@ public class GpsLocationProvider implements LocationProviderInterface {
mHandler.getLooper());
}
});
+
+ mListenerHelper = new GpsStatusListenerHelper(mHandler) {
+ @Override
+ protected boolean isAvailableInPlatform() {
+ return GpsLocationProvider.isSupported();
+ }
+
+ @Override
+ protected boolean isGpsEnabled() {
+ return isEnabled();
+ }
+ };
+
+ mGpsMeasurementsProvider = new GpsMeasurementsProvider(mHandler) {
+ @Override
+ public boolean isAvailableInPlatform() {
+ return native_is_measurement_supported();
+ }
+
+ @Override
+ protected boolean registerWithService() {
+ return native_start_measurement_collection();
+ }
+
+ @Override
+ protected void unregisterFromService() {
+ native_stop_measurement_collection();
+ }
+
+ @Override
+ protected boolean isGpsEnabled() {
+ return isEnabled();
+ }
+ };
+
+ mGpsNavigationMessageProvider = new GpsNavigationMessageProvider(mHandler) {
+ @Override
+ protected boolean isAvailableInPlatform() {
+ return native_is_navigation_message_supported();
+ }
+
+ @Override
+ protected boolean registerWithService() {
+ return native_start_navigation_message_collection();
+ }
+
+ @Override
+ protected void unregisterFromService() {
+ native_stop_navigation_message_collection();
+ }
+
+ @Override
+ protected boolean isGpsEnabled() {
+ return isEnabled();
+ }
+ };
}
private void listenForBroadcasts() {
@@ -1445,7 +1458,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
if (wasNavigating != mNavigating) {
- mListenerHelper.onStatusChanged(mNavigating);
+ mListenerHelper.onGpsEnabledChanged(mNavigating);
+ mGpsMeasurementsProvider.onGpsEnabledChanged(mNavigating);
+ mGpsNavigationMessageProvider.onGpsEnabledChanged(mNavigating);
// send an intent to notify that the GPS has been enabled or disabled
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -1598,6 +1613,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
mPeriodicTimeInjection = true;
requestUtcTime();
}
+
+ mGpsMeasurementsProvider.onCapabilitiesUpdated(
+ (capabilities & GPS_CAPABILITY_MEASUREMENTS) == GPS_CAPABILITY_MEASUREMENTS);
+ mGpsNavigationMessageProvider.onCapabilitiesUpdated(
+ (capabilities & GPS_CAPABILITY_NAV_MESSAGES) == GPS_CAPABILITY_NAV_MESSAGES);
}
/**
diff --git a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
index 1c48257..0514e0c 100644
--- a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java
@@ -18,7 +18,9 @@ package com.android.server.location;
import android.location.GpsMeasurementsEvent;
import android.location.IGpsMeasurementsListener;
+import android.os.Handler;
import android.os.RemoteException;
+import android.util.Log;
/**
* An base implementation for GPS measurements provider.
@@ -29,8 +31,10 @@ import android.os.RemoteException;
*/
public abstract class GpsMeasurementsProvider
extends RemoteListenerHelper<IGpsMeasurementsListener> {
- public GpsMeasurementsProvider() {
- super("GpsMeasurementsProvider");
+ private static final String TAG = "GpsMeasurementsProvider";
+
+ public GpsMeasurementsProvider(Handler handler) {
+ super(handler, TAG);
}
public void onMeasurementsAvailable(final GpsMeasurementsEvent event) {
@@ -41,7 +45,56 @@ public abstract class GpsMeasurementsProvider
listener.onGpsMeasurementsReceived(event);
}
};
-
foreach(operation);
}
+
+ public void onCapabilitiesUpdated(boolean isGpsMeasurementsSupported) {
+ int status = isGpsMeasurementsSupported ?
+ GpsMeasurementsEvent.STATUS_READY :
+ GpsMeasurementsEvent.STATUS_NOT_SUPPORTED;
+ setSupported(isGpsMeasurementsSupported, new StatusChangedOperation(status));
+ }
+
+ @Override
+ protected ListenerOperation<IGpsMeasurementsListener> getHandlerOperation(int result) {
+ final int status;
+ switch (result) {
+ case RESULT_SUCCESS:
+ status = GpsMeasurementsEvent.STATUS_READY;
+ break;
+ case RESULT_NOT_AVAILABLE:
+ case RESULT_NOT_SUPPORTED:
+ case RESULT_INTERNAL_ERROR:
+ status = GpsMeasurementsEvent.STATUS_NOT_SUPPORTED;
+ break;
+ case RESULT_GPS_LOCATION_DISABLED:
+ status = GpsMeasurementsEvent.STATUS_GPS_LOCATION_DISABLED;
+ break;
+ default:
+ Log.v(TAG, "Unhandled addListener result: " + result);
+ return null;
+ }
+ return new StatusChangedOperation(status);
+ }
+
+ @Override
+ protected void handleGpsEnabledChanged(boolean enabled) {
+ int status = enabled ?
+ GpsMeasurementsEvent.STATUS_READY :
+ GpsMeasurementsEvent.STATUS_GPS_LOCATION_DISABLED;
+ foreach(new StatusChangedOperation(status));
+ }
+
+ private class StatusChangedOperation implements ListenerOperation<IGpsMeasurementsListener> {
+ private final int mStatus;
+
+ public StatusChangedOperation(int status) {
+ mStatus = status;
+ }
+
+ @Override
+ public void execute(IGpsMeasurementsListener listener) throws RemoteException {
+ listener.onStatusChanged(mStatus);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
index fca7378..13d22fc 100644
--- a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java
@@ -18,7 +18,9 @@ package com.android.server.location;
import android.location.GpsNavigationMessageEvent;
import android.location.IGpsNavigationMessageListener;
+import android.os.Handler;
import android.os.RemoteException;
+import android.util.Log;
/**
* An base implementation for GPS navigation messages provider.
@@ -29,8 +31,10 @@ import android.os.RemoteException;
*/
public abstract class GpsNavigationMessageProvider
extends RemoteListenerHelper<IGpsNavigationMessageListener> {
- public GpsNavigationMessageProvider() {
- super("GpsNavigationMessageProvider");
+ private static final String TAG = "GpsNavigationMessageProvider";
+
+ public GpsNavigationMessageProvider(Handler handler) {
+ super(handler, TAG);
}
public void onNavigationMessageAvailable(final GpsNavigationMessageEvent event) {
@@ -42,7 +46,57 @@ public abstract class GpsNavigationMessageProvider
listener.onGpsNavigationMessageReceived(event);
}
};
-
foreach(operation);
}
+
+ public void onCapabilitiesUpdated(boolean isGpsNavigationMessageSupported) {
+ int status = isGpsNavigationMessageSupported ?
+ GpsNavigationMessageEvent.STATUS_READY :
+ GpsNavigationMessageEvent.STATUS_NOT_SUPPORTED;
+ setSupported(isGpsNavigationMessageSupported, new StatusChangedOperation(status));
+ }
+
+ @Override
+ protected ListenerOperation<IGpsNavigationMessageListener> getHandlerOperation(int result) {
+ final int status;
+ switch (result) {
+ case RESULT_SUCCESS:
+ status = GpsNavigationMessageEvent.STATUS_READY;
+ break;
+ case RESULT_NOT_AVAILABLE:
+ case RESULT_NOT_SUPPORTED:
+ case RESULT_INTERNAL_ERROR:
+ status = GpsNavigationMessageEvent.STATUS_NOT_SUPPORTED;
+ break;
+ case RESULT_GPS_LOCATION_DISABLED:
+ status = GpsNavigationMessageEvent.STATUS_GPS_LOCATION_DISABLED;
+ break;
+ default:
+ Log.v(TAG, "Unhandled addListener result: " + result);
+ return null;
+ }
+ return new StatusChangedOperation(status);
+ }
+
+ @Override
+ protected void handleGpsEnabledChanged(boolean enabled) {
+ int status = enabled ?
+ GpsNavigationMessageEvent.STATUS_READY :
+ GpsNavigationMessageEvent.STATUS_GPS_LOCATION_DISABLED;
+ foreach(new StatusChangedOperation(status));
+ }
+
+ private class StatusChangedOperation
+ implements ListenerOperation<IGpsNavigationMessageListener> {
+ private final int mStatus;
+
+ public StatusChangedOperation(int status) {
+ mStatus = status;
+ }
+
+ @Override
+ public void execute(IGpsNavigationMessageListener listener) throws RemoteException {
+ listener.onStatusChanged(mStatus);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
index 27cf3d8..376b4a5 100644
--- a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
+++ b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java
@@ -17,39 +17,64 @@
package com.android.server.location;
import android.location.IGpsStatusListener;
+import android.os.Handler;
import android.os.RemoteException;
/**
* Implementation of a handler for {@link IGpsStatusListener}.
*/
abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> {
- public GpsStatusListenerHelper() {
- super("GpsStatusListenerHelper");
- }
+ public GpsStatusListenerHelper(Handler handler) {
+ super(handler, "GpsStatusListenerHelper");
- public void onFirstFix(final int timeToFirstFix) {
- Operation operation = new Operation() {
+ Operation nullOperation = new Operation() {
@Override
- public void execute(IGpsStatusListener listener) throws RemoteException {
- listener.onFirstFix(timeToFirstFix);
- }
+ public void execute(IGpsStatusListener iGpsStatusListener) throws RemoteException {}
};
+ setSupported(GpsLocationProvider.isSupported(), nullOperation);
+ }
+
+ @Override
+ protected boolean registerWithService() {
+ return true;
+ }
+
+ @Override
+ protected void unregisterFromService() {}
+
+ @Override
+ protected ListenerOperation<IGpsStatusListener> getHandlerOperation(int result) {
+ return null;
+ }
+ @Override
+ protected void handleGpsEnabledChanged(boolean enabled) {
+ Operation operation;
+ if (enabled) {
+ operation = new Operation() {
+ @Override
+ public void execute(IGpsStatusListener listener) throws RemoteException {
+ listener.onGpsStarted();
+ }
+ };
+ } else {
+ operation = new Operation() {
+ @Override
+ public void execute(IGpsStatusListener listener) throws RemoteException {
+ listener.onGpsStopped();
+ }
+ };
+ }
foreach(operation);
}
- public void onStatusChanged(final boolean isNavigating) {
+ public void onFirstFix(final int timeToFirstFix) {
Operation operation = new Operation() {
@Override
public void execute(IGpsStatusListener listener) throws RemoteException {
- if (isNavigating) {
- listener.onGpsStarted();
- } else {
- listener.onGpsStopped();
- }
+ listener.onFirstFix(timeToFirstFix);
}
};
-
foreach(operation);
}
@@ -76,7 +101,6 @@ abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusLi
usedInFixMask);
}
};
-
foreach(operation);
}
@@ -87,7 +111,6 @@ abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusLi
listener.onNmeaReceived(timestamp, nmea);
}
};
-
foreach(operation);
}
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index 451af18..402b601 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -19,35 +19,41 @@ package com.android.server.location;
import com.android.internal.util.Preconditions;
import android.annotation.NonNull;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import android.util.Log;
-import java.util.ArrayList;
-import java.util.Collection;
import java.util.HashMap;
/**
* A helper class, that handles operations in remote listeners, and tracks for remote process death.
*/
abstract class RemoteListenerHelper<TListener extends IInterface> {
+ protected static final int RESULT_SUCCESS = 0;
+ protected static final int RESULT_NOT_AVAILABLE = 1;
+ protected static final int RESULT_NOT_SUPPORTED = 2;
+ protected static final int RESULT_GPS_LOCATION_DISABLED = 3;
+ protected static final int RESULT_INTERNAL_ERROR = 4;
+
+ private final Handler mHandler;
private final String mTag;
- private final HashMap<IBinder, LinkedListener> mListenerMap =
- new HashMap<IBinder, LinkedListener>();
- protected RemoteListenerHelper(String name) {
+ private final HashMap<IBinder, LinkedListener> mListenerMap = new HashMap<>();
+
+ private boolean mIsRegistered;
+ private boolean mHasIsSupported;
+ private boolean mIsSupported;
+
+ protected RemoteListenerHelper(Handler handler, String name) {
Preconditions.checkNotNull(name);
+ mHandler = handler;
mTag = name;
}
public boolean addListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener.");
- if (!isSupported()) {
- Log.e(mTag, "Refused to add listener, the feature is not supported.");
- return false;
- }
-
IBinder binder = listener.asBinder();
LinkedListener deathListener = new LinkedListener(listener);
synchronized (mListenerMap) {
@@ -55,75 +61,126 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
// listener already added
return true;
}
-
try {
binder.linkToDeath(deathListener, 0 /* flags */);
} catch (RemoteException e) {
// if the remote process registering the listener is already death, just swallow the
- // exception and continue
- Log.e(mTag, "Remote listener already died.", e);
+ // exception and return
+ Log.v(mTag, "Remote listener already died.", e);
return false;
}
-
mListenerMap.put(binder, deathListener);
- if (mListenerMap.size() == 1) {
- if (!registerWithService()) {
- Log.e(mTag, "RegisterWithService failed, listener will be removed.");
- removeListener(listener);
- return false;
- }
+
+ // update statuses we already know about, starting from the ones that will never change
+ int result;
+ if (!isAvailableInPlatform()) {
+ result = RESULT_NOT_AVAILABLE;
+ } else if (mHasIsSupported && !mIsSupported) {
+ result = RESULT_NOT_SUPPORTED;
+ } else if (!isGpsEnabled()) {
+ result = RESULT_GPS_LOCATION_DISABLED;
+ } else if (!tryRegister()) {
+ // only attempt to register if GPS is enabled, otherwise we will register once GPS
+ // becomes available
+ result = RESULT_INTERNAL_ERROR;
+ } else if (mHasIsSupported && mIsSupported) {
+ result = RESULT_SUCCESS;
+ } else {
+ // at this point if the supported flag is not set, the notification will be sent
+ // asynchronously in the future
+ return true;
}
+ post(listener, getHandlerOperation(result));
}
-
return true;
}
- public boolean removeListener(@NonNull TListener listener) {
+ public void removeListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener.");
- if (!isSupported()) {
- Log.e(mTag, "Refused to remove listener, the feature is not supported.");
- return false;
- }
-
IBinder binder = listener.asBinder();
LinkedListener linkedListener;
synchronized (mListenerMap) {
linkedListener = mListenerMap.remove(binder);
- if (mListenerMap.isEmpty() && linkedListener != null) {
- unregisterFromService();
+ if (mListenerMap.isEmpty()) {
+ tryUnregister();
}
}
-
if (linkedListener != null) {
binder.unlinkToDeath(linkedListener, 0 /* flags */);
}
- return true;
}
- protected abstract boolean isSupported();
+ public void onGpsEnabledChanged(boolean enabled) {
+ // handle first the sub-class implementation, so any error in registration can take
+ // precedence
+ handleGpsEnabledChanged(enabled);
+ synchronized (mListenerMap) {
+ if (!enabled) {
+ tryUnregister();
+ return;
+ }
+ if (mListenerMap.isEmpty()) {
+ return;
+ }
+ if (tryRegister()) {
+ // registration was successful, there is no need to update the state
+ return;
+ }
+ ListenerOperation<TListener> operation = getHandlerOperation(RESULT_INTERNAL_ERROR);
+ foreachUnsafe(operation);
+ }
+ }
+
+ protected abstract boolean isAvailableInPlatform();
+ protected abstract boolean isGpsEnabled();
protected abstract boolean registerWithService();
protected abstract void unregisterFromService();
+ protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
+ protected abstract void handleGpsEnabledChanged(boolean enabled);
protected interface ListenerOperation<TListener extends IInterface> {
void execute(TListener listener) throws RemoteException;
}
- protected void foreach(ListenerOperation operation) {
- Collection<LinkedListener> linkedListeners;
+ protected void foreach(ListenerOperation<TListener> operation) {
synchronized (mListenerMap) {
- Collection<LinkedListener> values = mListenerMap.values();
- linkedListeners = new ArrayList<LinkedListener>(values);
+ foreachUnsafe(operation);
}
+ }
- for (LinkedListener linkedListener : linkedListeners) {
- TListener listener = linkedListener.getUnderlyingListener();
- try {
- operation.execute(listener);
- } catch (RemoteException e) {
- Log.e(mTag, "Error in monitored listener.", e);
- removeListener(listener);
- }
+ protected void setSupported(boolean value, ListenerOperation<TListener> notifier) {
+ synchronized (mListenerMap) {
+ mHasIsSupported = true;
+ mIsSupported = value;
+ foreachUnsafe(notifier);
+ }
+ }
+
+ private void foreachUnsafe(ListenerOperation<TListener> operation) {
+ for (LinkedListener linkedListener : mListenerMap.values()) {
+ post(linkedListener.getUnderlyingListener(), operation);
+ }
+ }
+
+ private void post(TListener listener, ListenerOperation<TListener> operation) {
+ if (operation != null) {
+ mHandler.post(new HandlerRunnable(listener, operation));
+ }
+ }
+
+ private boolean tryRegister() {
+ if (!mIsRegistered) {
+ mIsRegistered = registerWithService();
+ }
+ return mIsRegistered;
+ }
+
+ private void tryUnregister() {
+ if (!mIsRegistered) {
+ return;
}
+ unregisterFromService();
+ mIsRegistered = false;
}
private class LinkedListener implements IBinder.DeathRecipient {
@@ -144,4 +201,23 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
removeListener(mListener);
}
}
+
+ private class HandlerRunnable implements Runnable {
+ private final TListener mListener;
+ private final ListenerOperation<TListener> mOperation;
+
+ public HandlerRunnable(TListener listener, ListenerOperation<TListener> operation) {
+ mListener = listener;
+ mOperation = operation;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mOperation.execute(mListener);
+ } catch (RemoteException e) {
+ Log.v(mTag, "Error in monitored listener.", e);
+ }
+ }
+ }
}