am 9ed36c42: Merge "Add support for GPS measurement/navigation message capabilities. b/16727892 b/16815124" into lmp-mr1-dev automerge: 992b9aa

* commit '9ed36c42d8c3731b8ca631292881110eb8897cec':
  Add support for GPS measurement/navigation message capabilities. b/16727892 b/16815124
This commit is contained in:
destradaa
2014-11-07 00:07:30 +00:00
committed by Android Git Automerger
15 changed files with 459 additions and 154 deletions

View File

@@ -26,14 +26,12 @@ import android.os.RemoteException;
*/ */
class GpsMeasurementListenerTransport class GpsMeasurementListenerTransport
extends LocalListenerHelper<GpsMeasurementsEvent.Listener> { extends LocalListenerHelper<GpsMeasurementsEvent.Listener> {
private final Context mContext;
private final ILocationManager mLocationManager; private final ILocationManager mLocationManager;
private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport(); private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport();
public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) { public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) {
super("GpsMeasurementListenerTransport"); super(context, "GpsMeasurementListenerTransport");
mContext = context;
mLocationManager = locationManager; mLocationManager = locationManager;
} }
@@ -41,7 +39,7 @@ class GpsMeasurementListenerTransport
protected boolean registerWithServer() throws RemoteException { protected boolean registerWithServer() throws RemoteException {
return mLocationManager.addGpsMeasurementsListener( return mLocationManager.addGpsMeasurementsListener(
mListenerTransport, mListenerTransport,
mContext.getPackageName()); getContext().getPackageName());
} }
@Override @Override
@@ -59,7 +57,18 @@ class GpsMeasurementListenerTransport
listener.onGpsMeasurementsReceived(event); 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); foreach(operation);
} }
} }

View File

@@ -32,6 +32,24 @@ import java.util.Collections;
* @hide * @hide
*/ */
public class GpsMeasurementsEvent implements Parcelable { 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 GpsClock mClock;
private final Collection<GpsMeasurement> mReadOnlyMeasurements; private final Collection<GpsMeasurement> mReadOnlyMeasurements;
@@ -43,7 +61,16 @@ public class GpsMeasurementsEvent implements Parcelable {
* @hide * @hide
*/ */
public interface Listener { public interface Listener {
/**
* Returns the latest collected GPS Measurements.
*/
void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs); void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs);
/**
* Returns the latest status of the GPS Measurements sub-system.
*/
void onStatusChanged(int status);
} }
public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) { public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) {
@@ -103,7 +130,9 @@ public class GpsMeasurementsEvent implements Parcelable {
public void writeToParcel(Parcel parcel, int flags) { public void writeToParcel(Parcel parcel, int flags) {
parcel.writeParcelable(mClock, 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.writeInt(measurementsArray.length);
parcel.writeTypedArray(measurementsArray, flags); parcel.writeTypedArray(measurementsArray, flags);
} }

View File

@@ -21,9 +21,6 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import java.security.InvalidParameterException; 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. * A class implementing a container for data associated with a navigation message event.
@@ -32,6 +29,24 @@ import java.util.Collections;
* @hide * @hide
*/ */
public class GpsNavigationMessageEvent implements Parcelable { 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; private final GpsNavigationMessage mNavigationMessage;
/** /**
@@ -42,7 +57,16 @@ public class GpsNavigationMessageEvent implements Parcelable {
* @hide * @hide
*/ */
public interface Listener { public interface Listener {
/**
* Returns the latest collected GPS Navigation Message.
*/
void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event); void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event);
/**
* Returns the latest status of the GPS Navigation Messages sub-system.
*/
void onStatusChanged(int status);
} }
public GpsNavigationMessageEvent(GpsNavigationMessage message) { public GpsNavigationMessageEvent(GpsNavigationMessage message) {

View File

@@ -26,7 +26,6 @@ import android.os.RemoteException;
*/ */
class GpsNavigationMessageListenerTransport class GpsNavigationMessageListenerTransport
extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> { extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> {
private final Context mContext;
private final ILocationManager mLocationManager; private final ILocationManager mLocationManager;
private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport(); private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport();
@@ -34,8 +33,7 @@ class GpsNavigationMessageListenerTransport
public GpsNavigationMessageListenerTransport( public GpsNavigationMessageListenerTransport(
Context context, Context context,
ILocationManager locationManager) { ILocationManager locationManager) {
super("GpsNavigationMessageListenerTransport"); super(context, "GpsNavigationMessageListenerTransport");
mContext = context;
mLocationManager = locationManager; mLocationManager = locationManager;
} }
@@ -43,7 +41,7 @@ class GpsNavigationMessageListenerTransport
protected boolean registerWithServer() throws RemoteException { protected boolean registerWithServer() throws RemoteException {
return mLocationManager.addGpsNavigationMessageListener( return mLocationManager.addGpsNavigationMessageListener(
mListenerTransport, mListenerTransport,
mContext.getPackageName()); getContext().getPackageName());
} }
@Override @Override
@@ -62,7 +60,19 @@ class GpsNavigationMessageListenerTransport
listener.onGpsNavigationMessageReceived(event); 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); foreach(operation);
} }
} }

View File

@@ -23,4 +23,5 @@ import android.location.GpsMeasurementsEvent;
*/ */
oneway interface IGpsMeasurementsListener { oneway interface IGpsMeasurementsListener {
void onGpsMeasurementsReceived(in GpsMeasurementsEvent event); void onGpsMeasurementsReceived(in GpsMeasurementsEvent event);
void onStatusChanged(in int status);
} }

View File

@@ -23,4 +23,5 @@ import android.location.GpsNavigationMessageEvent;
*/ */
oneway interface IGpsNavigationMessageListener { oneway interface IGpsNavigationMessageListener {
void onGpsNavigationMessageReceived(in GpsNavigationMessageEvent event); void onGpsNavigationMessageReceived(in GpsNavigationMessageEvent event);
void onStatusChanged(in int status);
} }

View File

@@ -62,12 +62,12 @@ interface ILocationManager
boolean sendNiResponse(int notifId, int userResponse); boolean sendNiResponse(int notifId, int userResponse);
boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName); boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName);
boolean removeGpsMeasurementsListener(in IGpsMeasurementsListener listener); void removeGpsMeasurementsListener(in IGpsMeasurementsListener listener);
boolean addGpsNavigationMessageListener( boolean addGpsNavigationMessageListener(
in IGpsNavigationMessageListener listener, in IGpsNavigationMessageListener listener,
in String packageName); in String packageName);
boolean removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener); void removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener);
// --- deprecated --- // --- deprecated ---
List<String> getAllProviders(); List<String> getAllProviders();

View File

@@ -19,6 +19,7 @@ package android.location;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.content.Context;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
@@ -32,17 +33,19 @@ import java.util.HashSet;
* @hide * @hide
*/ */
abstract class LocalListenerHelper<TListener> { abstract class LocalListenerHelper<TListener> {
private final HashSet<TListener> mListeners = new HashSet<TListener>(); private final HashSet<TListener> mListeners = new HashSet<>();
private final String mTag;
protected LocalListenerHelper(String name) { private final String mTag;
private final Context mContext;
protected LocalListenerHelper(Context context, String name) {
Preconditions.checkNotNull(name); Preconditions.checkNotNull(name);
mContext = context;
mTag = name; mTag = name;
} }
public boolean add(@NonNull TListener listener) { public boolean add(@NonNull TListener listener) {
Preconditions.checkNotNull(listener); Preconditions.checkNotNull(listener);
synchronized (mListeners) { synchronized (mListeners) {
// we need to register with the service first, because we need to find out if the // 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 // service will actually support the request before we attempt anything
@@ -59,18 +62,15 @@ abstract class LocalListenerHelper<TListener> {
return false; return false;
} }
} }
if (mListeners.contains(listener)) { if (mListeners.contains(listener)) {
return true; return true;
} }
mListeners.add(listener); return mListeners.add(listener);
} }
return true;
} }
public void remove(@NonNull TListener listener) { public void remove(@NonNull TListener listener) {
Preconditions.checkNotNull(listener); Preconditions.checkNotNull(listener);
synchronized (mListeners) { synchronized (mListeners) {
boolean removed = mListeners.remove(listener); boolean removed = mListeners.remove(listener);
boolean isLastRemoved = removed && mListeners.isEmpty(); boolean isLastRemoved = removed && mListeners.isEmpty();
@@ -78,7 +78,7 @@ abstract class LocalListenerHelper<TListener> {
try { try {
unregisterFromServer(); unregisterFromServer();
} catch (RemoteException e) { } 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; 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; Collection<TListener> listeners;
synchronized (mListeners) { synchronized (mListeners) {
listeners = new ArrayList<TListener>(mListeners); listeners = new ArrayList<>(mListeners);
} }
for (TListener listener : listeners) { for (TListener listener : listeners) {
try { try {
operation.execute(listener); operation.execute(listener);

View File

@@ -1579,7 +1579,7 @@ public class LocationManager {
* Adds a GPS Measurement listener. * Adds a GPS Measurement listener.
* *
* @param listener a {@link GpsMeasurementsEvent.Listener} object to register. * @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 * @hide
*/ */
@@ -1602,7 +1602,7 @@ public class LocationManager {
* Adds a GPS Navigation Message listener. * Adds a GPS Navigation Message listener.
* *
* @param listener a {@link GpsNavigationMessageEvent.Listener} object to register. * @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 * @hide
*/ */

View File

@@ -60,6 +60,8 @@ import android.location.Address;
import android.location.Criteria; import android.location.Criteria;
import android.location.GeocoderParams; import android.location.GeocoderParams;
import android.location.Geofence; import android.location.Geofence;
import android.location.GpsMeasurementsEvent;
import android.location.GpsNavigationMessageEvent;
import android.location.IGpsMeasurementsListener; import android.location.IGpsMeasurementsListener;
import android.location.IGpsNavigationMessageListener; import android.location.IGpsNavigationMessageListener;
import android.location.IGpsStatusListener; import android.location.IGpsStatusListener;
@@ -1859,8 +1861,8 @@ public class LocationManagerService extends ILocationManager.Stub {
} }
@Override @Override
public boolean removeGpsMeasurementsListener(IGpsMeasurementsListener listener) { public void removeGpsMeasurementsListener(IGpsMeasurementsListener listener) {
return mGpsMeasurementsProvider.removeListener(listener); mGpsMeasurementsProvider.removeListener(listener);
} }
@Override @Override
@@ -1888,8 +1890,8 @@ public class LocationManagerService extends ILocationManager.Stub {
} }
@Override @Override
public boolean removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) { public void removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) {
return mGpsNavigationMessageProvider.removeListener(listener); mGpsNavigationMessageProvider.removeListener(listener);
} }
@Override @Override

View File

@@ -162,6 +162,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final int GPS_CAPABILITY_MSA = 0x0000004; private static final int GPS_CAPABILITY_MSA = 0x0000004;
private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008; 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_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 // The AGPS SUPL mode
private static final int AGPS_SUPL_MODE_MSA = 0x02; private static final int AGPS_SUPL_MODE_MSA = 0x02;
@@ -348,20 +351,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final ILocationManager mILocationManager; private final ILocationManager mILocationManager;
private Location mLocation = new Location(LocationManager.GPS_PROVIDER); private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle(); private Bundle mLocationExtras = new Bundle();
private GpsStatusListenerHelper mListenerHelper = new GpsStatusListenerHelper() { private final GpsStatusListenerHelper mListenerHelper;
@Override private final GpsMeasurementsProvider mGpsMeasurementsProvider;
protected boolean isSupported() { private final GpsNavigationMessageProvider mGpsNavigationMessageProvider;
return GpsLocationProvider.isSupported();
}
@Override
protected boolean registerWithService() {
return true;
}
@Override
protected void unregisterFromService() {}
};
// Handler for processing events // Handler for processing events
private Handler mHandler; 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() { public IGpsStatusProvider getGpsStatusProvider() {
return mGpsStatusProvider; return mGpsStatusProvider;
} }
@@ -696,6 +653,62 @@ public class GpsLocationProvider implements LocationProviderInterface {
mHandler.getLooper()); 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() { private void listenForBroadcasts() {
@@ -1445,7 +1458,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
} }
if (wasNavigating != mNavigating) { 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 // send an intent to notify that the GPS has been enabled or disabled
Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION); Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION);
@@ -1598,6 +1613,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
mPeriodicTimeInjection = true; mPeriodicTimeInjection = true;
requestUtcTime(); requestUtcTime();
} }
mGpsMeasurementsProvider.onCapabilitiesUpdated(
(capabilities & GPS_CAPABILITY_MEASUREMENTS) == GPS_CAPABILITY_MEASUREMENTS);
mGpsNavigationMessageProvider.onCapabilitiesUpdated(
(capabilities & GPS_CAPABILITY_NAV_MESSAGES) == GPS_CAPABILITY_NAV_MESSAGES);
} }
/** /**

View File

@@ -18,7 +18,9 @@ package com.android.server.location;
import android.location.GpsMeasurementsEvent; import android.location.GpsMeasurementsEvent;
import android.location.IGpsMeasurementsListener; import android.location.IGpsMeasurementsListener;
import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log;
/** /**
* An base implementation for GPS measurements provider. * An base implementation for GPS measurements provider.
@@ -29,8 +31,10 @@ import android.os.RemoteException;
*/ */
public abstract class GpsMeasurementsProvider public abstract class GpsMeasurementsProvider
extends RemoteListenerHelper<IGpsMeasurementsListener> { extends RemoteListenerHelper<IGpsMeasurementsListener> {
public GpsMeasurementsProvider() { private static final String TAG = "GpsMeasurementsProvider";
super("GpsMeasurementsProvider");
public GpsMeasurementsProvider(Handler handler) {
super(handler, TAG);
} }
public void onMeasurementsAvailable(final GpsMeasurementsEvent event) { public void onMeasurementsAvailable(final GpsMeasurementsEvent event) {
@@ -41,7 +45,56 @@ public abstract class GpsMeasurementsProvider
listener.onGpsMeasurementsReceived(event); listener.onGpsMeasurementsReceived(event);
} }
}; };
foreach(operation); 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);
}
}
} }

View File

@@ -18,7 +18,9 @@ package com.android.server.location;
import android.location.GpsNavigationMessageEvent; import android.location.GpsNavigationMessageEvent;
import android.location.IGpsNavigationMessageListener; import android.location.IGpsNavigationMessageListener;
import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log;
/** /**
* An base implementation for GPS navigation messages provider. * An base implementation for GPS navigation messages provider.
@@ -29,8 +31,10 @@ import android.os.RemoteException;
*/ */
public abstract class GpsNavigationMessageProvider public abstract class GpsNavigationMessageProvider
extends RemoteListenerHelper<IGpsNavigationMessageListener> { extends RemoteListenerHelper<IGpsNavigationMessageListener> {
public GpsNavigationMessageProvider() { private static final String TAG = "GpsNavigationMessageProvider";
super("GpsNavigationMessageProvider");
public GpsNavigationMessageProvider(Handler handler) {
super(handler, TAG);
} }
public void onNavigationMessageAvailable(final GpsNavigationMessageEvent event) { public void onNavigationMessageAvailable(final GpsNavigationMessageEvent event) {
@@ -42,7 +46,57 @@ public abstract class GpsNavigationMessageProvider
listener.onGpsNavigationMessageReceived(event); listener.onGpsNavigationMessageReceived(event);
} }
}; };
foreach(operation); 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);
}
}
} }

View File

@@ -17,14 +17,55 @@
package com.android.server.location; package com.android.server.location;
import android.location.IGpsStatusListener; import android.location.IGpsStatusListener;
import android.os.Handler;
import android.os.RemoteException; import android.os.RemoteException;
/** /**
* Implementation of a handler for {@link IGpsStatusListener}. * Implementation of a handler for {@link IGpsStatusListener}.
*/ */
abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> { abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> {
public GpsStatusListenerHelper() { public GpsStatusListenerHelper(Handler handler) {
super("GpsStatusListenerHelper"); super(handler, "GpsStatusListenerHelper");
Operation nullOperation = new Operation() {
@Override
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 onFirstFix(final int timeToFirstFix) { public void onFirstFix(final int timeToFirstFix) {
@@ -34,22 +75,6 @@ abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusLi
listener.onFirstFix(timeToFirstFix); listener.onFirstFix(timeToFirstFix);
} }
}; };
foreach(operation);
}
public void onStatusChanged(final boolean isNavigating) {
Operation operation = new Operation() {
@Override
public void execute(IGpsStatusListener listener) throws RemoteException {
if (isNavigating) {
listener.onGpsStarted();
} else {
listener.onGpsStopped();
}
}
};
foreach(operation); foreach(operation);
} }
@@ -76,7 +101,6 @@ abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusLi
usedInFixMask); usedInFixMask);
} }
}; };
foreach(operation); foreach(operation);
} }
@@ -87,7 +111,6 @@ abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusLi
listener.onNmeaReceived(timestamp, nmea); listener.onNmeaReceived(timestamp, nmea);
} }
}; };
foreach(operation); foreach(operation);
} }

View File

@@ -19,35 +19,41 @@ package com.android.server.location;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.IInterface; import android.os.IInterface;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
/** /**
* A helper class, that handles operations in remote listeners, and tracks for remote process death. * A helper class, that handles operations in remote listeners, and tracks for remote process death.
*/ */
abstract class RemoteListenerHelper<TListener extends IInterface> { abstract class RemoteListenerHelper<TListener extends IInterface> {
private final String mTag; protected static final int RESULT_SUCCESS = 0;
private final HashMap<IBinder, LinkedListener> mListenerMap = protected static final int RESULT_NOT_AVAILABLE = 1;
new HashMap<IBinder, LinkedListener>(); 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;
protected RemoteListenerHelper(String name) { private final Handler mHandler;
private final String mTag;
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); Preconditions.checkNotNull(name);
mHandler = handler;
mTag = name; mTag = name;
} }
public boolean addListener(@NonNull TListener listener) { public boolean addListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to register a 'null' 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(); IBinder binder = listener.asBinder();
LinkedListener deathListener = new LinkedListener(listener); LinkedListener deathListener = new LinkedListener(listener);
synchronized (mListenerMap) { synchronized (mListenerMap) {
@@ -55,77 +61,128 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
// listener already added // listener already added
return true; return true;
} }
try { try {
binder.linkToDeath(deathListener, 0 /* flags */); binder.linkToDeath(deathListener, 0 /* flags */);
} catch (RemoteException e) { } catch (RemoteException e) {
// if the remote process registering the listener is already death, just swallow the // if the remote process registering the listener is already death, just swallow the
// exception and continue // exception and return
Log.e(mTag, "Remote listener already died.", e); Log.v(mTag, "Remote listener already died.", e);
return false; return false;
} }
mListenerMap.put(binder, deathListener); 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; return true;
} }
public boolean removeListener(@NonNull TListener listener) { public void removeListener(@NonNull TListener listener) {
Preconditions.checkNotNull(listener, "Attempted to remove a 'null' 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(); IBinder binder = listener.asBinder();
LinkedListener linkedListener; LinkedListener linkedListener;
synchronized (mListenerMap) { synchronized (mListenerMap) {
linkedListener = mListenerMap.remove(binder); linkedListener = mListenerMap.remove(binder);
if (mListenerMap.isEmpty() && linkedListener != null) { if (mListenerMap.isEmpty()) {
unregisterFromService(); tryUnregister();
} }
} }
if (linkedListener != null) { if (linkedListener != null) {
binder.unlinkToDeath(linkedListener, 0 /* flags */); 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 boolean registerWithService();
protected abstract void unregisterFromService(); protected abstract void unregisterFromService();
protected abstract ListenerOperation<TListener> getHandlerOperation(int result);
protected abstract void handleGpsEnabledChanged(boolean enabled);
protected interface ListenerOperation<TListener extends IInterface> { protected interface ListenerOperation<TListener extends IInterface> {
void execute(TListener listener) throws RemoteException; void execute(TListener listener) throws RemoteException;
} }
protected void foreach(ListenerOperation operation) { protected void foreach(ListenerOperation<TListener> operation) {
Collection<LinkedListener> linkedListeners;
synchronized (mListenerMap) { synchronized (mListenerMap) {
Collection<LinkedListener> values = mListenerMap.values(); foreachUnsafe(operation);
linkedListeners = new ArrayList<LinkedListener>(values);
} }
}
for (LinkedListener linkedListener : linkedListeners) { protected void setSupported(boolean value, ListenerOperation<TListener> notifier) {
TListener listener = linkedListener.getUnderlyingListener(); synchronized (mListenerMap) {
try { mHasIsSupported = true;
operation.execute(listener); mIsSupported = value;
} catch (RemoteException e) { foreachUnsafe(notifier);
Log.e(mTag, "Error in monitored listener.", e);
removeListener(listener);
}
} }
} }
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 { private class LinkedListener implements IBinder.DeathRecipient {
private final TListener mListener; private final TListener mListener;
@@ -144,4 +201,23 @@ abstract class RemoteListenerHelper<TListener extends IInterface> {
removeListener(mListener); 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);
}
}
}
} }