diff options
17 files changed, 927 insertions, 100 deletions
@@ -297,6 +297,7 @@ LOCAL_SRC_FILES += \ location/java/android/location/IGeocodeProvider.aidl \ location/java/android/location/IGeofenceProvider.aidl \ location/java/android/location/IGpsMeasurementsListener.aidl \ + location/java/android/location/IGpsNavigationMessageListener.aidl \ location/java/android/location/IGpsStatusListener.aidl \ location/java/android/location/IGpsStatusProvider.aidl \ location/java/android/location/ILocationListener.aidl \ diff --git a/location/java/android/location/GpsMeasurementListenerTransport.java b/location/java/android/location/GpsMeasurementListenerTransport.java index 48a4b44..2d9a372 100644 --- a/location/java/android/location/GpsMeasurementListenerTransport.java +++ b/location/java/android/location/GpsMeasurementListenerTransport.java @@ -16,99 +16,51 @@ 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; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; /** - * A handler class to manage transport listeners for {@link GpsMeasurementsEvent.Listener}, - * and post the events in a handler. + * A handler class to manage transport listeners for {@link GpsMeasurementsEvent.Listener}. * * @hide */ -class GpsMeasurementListenerTransport { - private static final String TAG = "GpsMeasurementListenerTransport"; - +class GpsMeasurementListenerTransport + extends LocalListenerHelper<GpsMeasurementsEvent.Listener> { private final Context mContext; private final ILocationManager mLocationManager; private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport(); - private final HashSet<GpsMeasurementsEvent.Listener> mListeners = - new HashSet<GpsMeasurementsEvent.Listener>(); public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) { + super("GpsMeasurementListenerTransport"); mContext = context; mLocationManager = locationManager; } - public boolean add(@NonNull GpsMeasurementsEvent.Listener 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 - if (mListeners.isEmpty()) { - boolean registeredWithServer; - try { - registeredWithServer = mLocationManager.addGpsMeasurementsListener( - mListenerTransport, - mContext.getPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error handling first listener.", e); - return false; - } - - if (!registeredWithServer) { - Log.e(TAG, "Unable to register listener transport."); - return false; - } - } - - if (mListeners.contains(listener)) { - return true; - } - - mListeners.add(listener); - } - - return true; + @Override + protected boolean registerWithServer() throws RemoteException { + return mLocationManager.addGpsMeasurementsListener( + mListenerTransport, + mContext.getPackageName()); } - public void remove(@NonNull GpsMeasurementsEvent.Listener listener) { - Preconditions.checkNotNull(listener); - - synchronized (mListeners) { - boolean removed = mListeners.remove(listener); - - boolean isLastListener = removed && mListeners.isEmpty(); - if (isLastListener) { - try { - mLocationManager.removeGpsMeasurementsListener(mListenerTransport); - } catch (RemoteException e) { - Log.e(TAG, "Error handling last listener.", e); - } - } - } + @Override + protected void unregisterFromServer() throws RemoteException { + mLocationManager.removeGpsMeasurementsListener(mListenerTransport); } private class ListenerTransport extends IGpsMeasurementsListener.Stub { @Override - public void onGpsMeasurementsReceived(final GpsMeasurementsEvent eventArgs) { - Collection<GpsMeasurementsEvent.Listener> listeners; - synchronized (mListeners) { - listeners = new ArrayList<GpsMeasurementsEvent.Listener>(mListeners); - } + public void onGpsMeasurementsReceived(final GpsMeasurementsEvent event) { + ListenerOperation<GpsMeasurementsEvent.Listener> operation = + new ListenerOperation<GpsMeasurementsEvent.Listener>() { + @Override + public void execute(GpsMeasurementsEvent.Listener listener) throws RemoteException { + listener.onGpsMeasurementsReceived(event); + } + }; - for (final GpsMeasurementsEvent.Listener listener : listeners) { - listener.onGpsMeasurementsReceived(eventArgs); - } + foreach(operation); } } } diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java new file mode 100644 index 0000000..2eb4708 --- /dev/null +++ b/location/java/android/location/GpsNavigationMessage.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.security.InvalidParameterException; + +/** + * A class containing a GPS satellite Navigation Message. + * + * @hide + */ +public class GpsNavigationMessage implements Parcelable { + private static final String TAG = "GpsNavigationMessage"; + private static final byte[] EMPTY_ARRAY = new byte[0]; + + // The following enumerations must be in sync with the values declared in gps.h + + /** + * The type of the navigation message is not available or unknown. + */ + public static final byte TYPE_UNKNOWN = 0; + + /** + * The Navigation Message is of type L1 C/A. + */ + public static final byte TYPE_L1CA = 1; + + /** + * The Navigation Message is of type L1-CNAV. + */ + public static final byte TYPE_L2CNAV = 2; + + /** + * The Navigation Message is of type L5-CNAV. + */ + public static final byte TYPE_L5CNAV = 3; + + /** + * The Navigation Message is of type CNAV-2. + */ + public static final byte TYPE_CNAV2 = 4; + + // End enumerations in sync with gps.h + + private byte mType; + private byte mPrn; + private short mMessageId; + private short mSubmessageId; + private byte[] mData; + + GpsNavigationMessage() { + initialize(); + } + + /** + * Sets all contents to the values stored in the provided object. + */ + public void set(GpsNavigationMessage navigationMessage) { + mType = navigationMessage.mType; + mPrn = navigationMessage.mPrn; + mMessageId = navigationMessage.mMessageId; + mSubmessageId = navigationMessage.mSubmessageId; + mData = navigationMessage.mData; + } + + /** + * Resets all the contents to its original state. + */ + public void reset() { + initialize(); + } + + /** + * Gets the type of the navigation message contained in the object. + */ + public byte getType() { + return mType; + } + + /** + * Sets the type of the navigation message. + */ + public void setType(byte value) { + switch (value) { + case TYPE_UNKNOWN: + case TYPE_L1CA: + case TYPE_L2CNAV: + case TYPE_L5CNAV: + case TYPE_CNAV2: + mType = value; + break; + default: + Log.d(TAG, "Sanitizing invalid 'type': " + value); + mType = TYPE_UNKNOWN; + break; + } + } + + /** + * Gets a string representation of the 'type'. + * For internal and logging use only. + */ + private String getTypeString() { + switch (mType) { + case TYPE_UNKNOWN: + return "Unknown"; + case TYPE_L1CA: + return "L1 C/A"; + case TYPE_L2CNAV: + return "L2-CNAV"; + case TYPE_L5CNAV: + return "L5-CNAV"; + case TYPE_CNAV2: + return "CNAV-2"; + default: + return "<Invalid>"; + } + } + + /** + * Gets the Pseudo-random number. + * Range: [1, 32]. + */ + public byte getPrn() { + return mPrn; + } + + /** + * Sets the Pseud-random number. + */ + public void setPrn(byte value) { + mPrn = value; + } + + /** + * Gets the Message Identifier. + * It provides an index so the complete Navigation Message can be assembled. i.e. for L1 C/A + * subframe 4 and 5, this value corresponds to the 'frame id' of the navigation message. + * Subframe 1, 2, 3 does not contain a 'frame id' and this might be reported as -1. + */ + public short getMessageId() { + return mMessageId; + } + + /** + * Sets the Message Identifier. + */ + public void setMessageId(short value) { + mMessageId = value; + } + + /** + * Gets the Sub-message Identifier. + * If required by {@link #getType()}, this value contains a sub-index within the current message + * (or frame) that is being transmitted. i.e. for L1 C/A the sub-message identifier corresponds + * to the sub-frame Id of the navigation message. + */ + public short getSubmessageId() { + return mSubmessageId; + } + + /** + * Sets the Sub-message identifier. + */ + public void setSubmessageId(short value) { + mSubmessageId = value; + } + + /** + * Gets the data associated with the Navigation Message. + * The bytes (or words) specified using big endian format (MSB first). + */ + @NonNull + public byte[] getData() { + return mData; + } + + /** + * Sets the data associated with the Navigation Message. + */ + public void setData(byte[] value) { + if (value == null) { + throw new InvalidParameterException("Data must be a non-null array"); + } + + mData = value; + } + + public static final Creator<GpsNavigationMessage> CREATOR = + new Creator<GpsNavigationMessage>() { + @Override + public GpsNavigationMessage createFromParcel(Parcel parcel) { + GpsNavigationMessage navigationMessage = new GpsNavigationMessage(); + + navigationMessage.setType(parcel.readByte()); + navigationMessage.setPrn(parcel.readByte()); + navigationMessage.setMessageId((short) parcel.readInt()); + navigationMessage.setSubmessageId((short) parcel.readInt()); + + int dataLength = parcel.readInt(); + byte[] data = new byte[dataLength]; + parcel.readByteArray(data); + navigationMessage.setData(data); + + return navigationMessage; + } + + @Override + public GpsNavigationMessage[] newArray(int size) { + return new GpsNavigationMessage[size]; + } + }; + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeByte(mType); + parcel.writeByte(mPrn); + parcel.writeInt(mMessageId); + parcel.writeInt(mSubmessageId); + parcel.writeInt(mData.length); + parcel.writeByteArray(mData); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + final String format = " %-15s = %s\n"; + StringBuilder builder = new StringBuilder("GpsNavigationMessage:\n"); + + builder.append(String.format(format, "Type", getTypeString())); + builder.append(String.format(format, "Prn", mPrn)); + builder.append(String.format(format, "MessageId", mMessageId)); + builder.append(String.format(format, "SubmessageId", mSubmessageId)); + + builder.append(String.format(format, "Data", "{")); + String prefix = " "; + for(byte value : mData) { + builder.append(prefix); + builder.append(value); + prefix = ", "; + } + builder.append(" }"); + + return builder.toString(); + } + + private void initialize() { + mType = TYPE_UNKNOWN; + mPrn = 0; + mMessageId = -1; + mSubmessageId = -1; + mData = EMPTY_ARRAY; + } +} diff --git a/location/java/android/location/GpsNavigationMessageEvent.aidl b/location/java/android/location/GpsNavigationMessageEvent.aidl new file mode 100644 index 0000000..f84c2f7 --- /dev/null +++ b/location/java/android/location/GpsNavigationMessageEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +parcelable GpsNavigationMessageEvent; diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java new file mode 100644 index 0000000..50ffa75 --- /dev/null +++ b/location/java/android/location/GpsNavigationMessageEvent.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.annotation.NonNull; +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. + * Events are delivered to registered instances of {@link Listener}. + * + * @hide + */ +public class GpsNavigationMessageEvent implements Parcelable { + private final GpsNavigationMessage mNavigationMessage; + + /** + * Used for receiving GPS satellite Navigation Messages from the GPS engine. + * You can implement this interface and call + * {@link LocationManager#addGpsNavigationMessageListener}. + * + * @hide + */ + public interface Listener { + void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event); + } + + public GpsNavigationMessageEvent(GpsNavigationMessage message) { + if (message == null) { + throw new InvalidParameterException("Parameter 'message' must not be null."); + } + mNavigationMessage = message; + } + + @NonNull + public GpsNavigationMessage getNavigationMessage() { + return mNavigationMessage; + } + + public static final Creator<GpsNavigationMessageEvent> CREATOR = + new Creator<GpsNavigationMessageEvent>() { + @Override + public GpsNavigationMessageEvent createFromParcel(Parcel in) { + ClassLoader classLoader = getClass().getClassLoader(); + GpsNavigationMessage navigationMessage = in.readParcelable(classLoader); + return new GpsNavigationMessageEvent(navigationMessage); + } + + @Override + public GpsNavigationMessageEvent[] newArray(int size) { + return new GpsNavigationMessageEvent[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mNavigationMessage, flags); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("[ GpsNavigationMessageEvent:\n\n"); + builder.append(mNavigationMessage.toString()); + builder.append("\n]"); + return builder.toString(); + } +} diff --git a/location/java/android/location/GpsNavigationMessageListenerTransport.java b/location/java/android/location/GpsNavigationMessageListenerTransport.java new file mode 100644 index 0000000..ec4812b --- /dev/null +++ b/location/java/android/location/GpsNavigationMessageListenerTransport.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.content.Context; +import android.os.RemoteException; + +/** + * A handler class to manage transport listeners for {@link GpsNavigationMessageEvent.Listener}. + * + * @hide + */ +class GpsNavigationMessageListenerTransport + extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> { + private final Context mContext; + private final ILocationManager mLocationManager; + + private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport(); + + public GpsNavigationMessageListenerTransport( + Context context, + ILocationManager locationManager) { + super("GpsNavigationMessageListenerTransport"); + mContext = context; + mLocationManager = locationManager; + } + + @Override + protected boolean registerWithServer() throws RemoteException { + return mLocationManager.addGpsNavigationMessageListener( + mListenerTransport, + mContext.getPackageName()); + } + + @Override + protected void unregisterFromServer() throws RemoteException { + mLocationManager.removeGpsNavigationMessageListener(mListenerTransport); + } + + private class ListenerTransport extends IGpsNavigationMessageListener.Stub { + @Override + public void onGpsNavigationMessageReceived(final GpsNavigationMessageEvent event) { + ListenerOperation<GpsNavigationMessageEvent.Listener> operation = + new ListenerOperation<GpsNavigationMessageEvent.Listener>() { + @Override + public void execute(GpsNavigationMessageEvent.Listener listener) + throws RemoteException { + listener.onGpsNavigationMessageReceived(event); + } + }; + + foreach(operation); + } + } +} diff --git a/location/java/android/location/IGpsNavigationMessageListener.aidl b/location/java/android/location/IGpsNavigationMessageListener.aidl new file mode 100644 index 0000000..18603fe --- /dev/null +++ b/location/java/android/location/IGpsNavigationMessageListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.location.GpsNavigationMessageEvent; + +/** + * {@hide} + */ +oneway interface IGpsNavigationMessageListener { + void onGpsNavigationMessageReceived(in GpsNavigationMessageEvent event); +} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index a1acaf1..1501710 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -22,6 +22,7 @@ import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; import android.location.IGpsMeasurementsListener; +import android.location.IGpsNavigationMessageListener; import android.location.IGpsStatusListener; import android.location.ILocationListener; import android.location.Location; @@ -63,6 +64,11 @@ interface ILocationManager boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName); boolean removeGpsMeasurementsListener(in IGpsMeasurementsListener listener); + boolean addGpsNavigationMessageListener( + in IGpsNavigationMessageListener listener, + in String packageName); + boolean removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener); + // --- deprecated --- List<String> getAllProviders(); List<String> getProviders(in Criteria criteria, boolean enabledOnly); diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java new file mode 100644 index 0000000..1f3bf67 --- /dev/null +++ b/location/java/android/location/LocalListenerHelper.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import com.android.internal.util.Preconditions; + +import android.annotation.NonNull; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +/** + * A base handler class to manage transport and local listeners. + * + * @hide + */ +abstract class LocalListenerHelper<TListener> { + private final HashSet<TListener> mListeners = new HashSet<TListener>(); + private final String mTag; + + protected LocalListenerHelper(String name) { + Preconditions.checkNotNull(name); + 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 + if (mListeners.isEmpty()) { + boolean registeredWithService; + try { + registeredWithService = registerWithServer(); + } catch (RemoteException e) { + Log.e(mTag, "Error handling first listener.", e); + return false; + } + if (!registeredWithService) { + Log.e(mTag, "Unable to register listener transport."); + return false; + } + } + + if (mListeners.contains(listener)) { + return true; + } + 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(); + if (isLastRemoved) { + try { + unregisterFromServer(); + } catch (RemoteException e) { + + } + } + } + } + + protected abstract boolean registerWithServer() throws RemoteException; + protected abstract void unregisterFromServer() throws RemoteException; + + protected interface ListenerOperation<TListener> { + void execute(TListener listener) throws RemoteException; + } + + protected void foreach(ListenerOperation operation) { + Collection<TListener> listeners; + synchronized (mListeners) { + listeners = new ArrayList<TListener>(mListeners); + } + + for (TListener listener : listeners) { + try { + operation.execute(listener); + } catch (RemoteException e) { + Log.e(mTag, "Error in monitored listener.", e); + // don't return, give a fair chance to all listeners to receive the event + } + } + } +} diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index d6a8fb8..082a158 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -59,6 +59,7 @@ public class LocationManager { private final Context mContext; private final ILocationManager mService; private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport; + private final GpsNavigationMessageListenerTransport mGpsNavigationMessageListenerTransport; private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners = new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>(); private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners = @@ -310,6 +311,8 @@ public class LocationManager { mService = service; mContext = context; mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService); + mGpsNavigationMessageListenerTransport = + new GpsNavigationMessageListenerTransport(mContext, mService); } private LocationProvider createProvider(String name, ProviderProperties properties) { @@ -1573,7 +1576,7 @@ public class LocationManager { /** * Adds a GPS Measurement listener. * - * @param listener a {@link android.location.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. * * @hide @@ -1593,6 +1596,30 @@ public class LocationManager { mGpsMeasurementListenerTransport.remove(listener); } + /** + * 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. + * + * @hide + */ + public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) { + return mGpsNavigationMessageListenerTransport.add(listener); + } + + /** + * Removes a GPS Navigation Message listener. + * + * @param listener a {@link GpsNavigationMessageEvent.Listener} object to remove. + * + * @hide + */ + public void removeGpsNavigationMessageListener( + GpsNavigationMessageEvent.Listener listener) { + mGpsNavigationMessageListenerTransport.remove(listener); + } + /** * Retrieves information about the current status of the GPS engine. * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged} diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index bae2d22..3a4e2ee 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -28,6 +28,7 @@ import com.android.server.location.GeofenceManager; import com.android.server.location.GeofenceProxy; import com.android.server.location.GpsLocationProvider; import com.android.server.location.GpsMeasurementsProvider; +import com.android.server.location.GpsNavigationMessageProvider; import com.android.server.location.LocationBlacklist; import com.android.server.location.LocationFudger; import com.android.server.location.LocationProviderInterface; @@ -60,6 +61,7 @@ import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; import android.location.IGpsMeasurementsListener; +import android.location.IGpsNavigationMessageListener; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; import android.location.ILocationListener; @@ -159,6 +161,7 @@ public class LocationManagerService extends ILocationManager.Stub { private PassiveProvider mPassiveProvider; // track passive provider for special cases private LocationBlacklist mBlacklist; private GpsMeasurementsProvider mGpsMeasurementsProvider; + private GpsNavigationMessageProvider mGpsNavigationMessageProvider; // --- fields below are protected by mLock --- // Set of providers that are explicitly enabled @@ -409,6 +412,7 @@ public class LocationManagerService extends ILocationManager.Stub { mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider); } mGpsMeasurementsProvider = gpsProvider.getGpsMeasurementsProvider(); + mGpsNavigationMessageProvider = gpsProvider.getGpsNavigationMessageProvider(); /* Load package name(s) containing location provider support. @@ -1847,7 +1851,6 @@ public class LocationManagerService extends ILocationManager.Stub { if (!hasLocationAccess) { return false; } - return mGpsMeasurementsProvider.addListener(listener); } @@ -1857,6 +1860,35 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public boolean addGpsNavigationMessageListener( + IGpsNavigationMessageListener listener, + String packageName) { + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + checkResolutionLevelIsSufficientForProviderUse( + allowedResolutionLevel, + LocationManager.GPS_PROVIDER); + + int uid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + boolean hasLocationAccess; + try { + hasLocationAccess = checkLocationAccess(uid, packageName, allowedResolutionLevel); + } finally { + Binder.restoreCallingIdentity(identity); + } + + if (!hasLocationAccess) { + return false; + } + return mGpsNavigationMessageProvider.addListener(listener); + } + + @Override + public boolean removeGpsNavigationMessageListener(IGpsNavigationMessageListener listener) { + return mGpsNavigationMessageProvider.removeListener(listener); + } + + @Override public boolean sendExtraCommand(String provider, String command, Bundle extras) { if (provider == null) { // throw NullPointerException to remain compatible with previous implementation diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index 09873c7..058a23e 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -38,6 +38,7 @@ import android.hardware.location.GeofenceHardwareImpl; import android.location.Criteria; import android.location.FusedBatchOptions; import android.location.GpsMeasurementsEvent; +import android.location.GpsNavigationMessageEvent; import android.location.IGpsGeofenceHardware; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; @@ -324,6 +325,14 @@ public class GpsLocationProvider implements LocationProviderInterface { protected boolean isSupported() { return GpsLocationProvider.isSupported(); } + + @Override + protected boolean registerWithService() { + return true; + } + + @Override + protected void unregisterFromService() {} }; // Handler for processing events @@ -374,16 +383,34 @@ public class GpsLocationProvider implements LocationProviderInterface { } @Override - protected void onFirstListenerAdded() { - native_start_measurement_collection(); + protected boolean registerWithService() { + return native_start_measurement_collection(); } @Override - protected void onLastListenerRemoved() { + 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; } @@ -396,6 +423,10 @@ public class GpsLocationProvider implements LocationProviderInterface { return mGpsMeasurementsProvider; } + public GpsNavigationMessageProvider getGpsNavigationMessageProvider() { + return mGpsNavigationMessageProvider; + } + private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -1357,13 +1388,20 @@ public class GpsLocationProvider implements LocationProviderInterface { } /** - * called from native code - Gps Data callback + * called from native code - Gps measurements callback */ private void reportMeasurementData(GpsMeasurementsEvent event) { mGpsMeasurementsProvider.onMeasurementsAvailable(event); } /** + * called from native code - GPS navigation message callback + */ + private void reportNavigationMessage(GpsNavigationMessageEvent event) { + mGpsNavigationMessageProvider.onNavigationMessageAvailable(event); + } + + /** * called from native code to inform us what the GPS engine capabilities are */ private void setEngineCapabilities(int capabilities) { @@ -1954,6 +1992,11 @@ public class GpsLocationProvider implements LocationProviderInterface { // Gps Hal measurements support. private static native boolean native_is_measurement_supported(); - private static native boolean native_start_measurement_collection(); - private static native boolean native_stop_measurement_collection(); + private native boolean native_start_measurement_collection(); + private native boolean native_stop_measurement_collection(); + + // Gps Navigation message support. + private static native boolean native_is_navigation_message_supported(); + private native boolean native_start_navigation_message_collection(); + private native boolean native_stop_navigation_message_collection(); } diff --git a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java index 001f638..1c48257 100644 --- a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java @@ -29,6 +29,9 @@ import android.os.RemoteException; */ public abstract class GpsMeasurementsProvider extends RemoteListenerHelper<IGpsMeasurementsListener> { + public GpsMeasurementsProvider() { + super("GpsMeasurementsProvider"); + } public void onMeasurementsAvailable(final GpsMeasurementsEvent event) { ListenerOperation<IGpsMeasurementsListener> operation = diff --git a/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java new file mode 100644 index 0000000..fca7378 --- /dev/null +++ b/services/core/java/com/android/server/location/GpsNavigationMessageProvider.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.location; + +import android.location.GpsNavigationMessageEvent; +import android.location.IGpsNavigationMessageListener; +import android.os.RemoteException; + +/** + * An base implementation for GPS navigation messages provider. + * It abstracts out the responsibility of handling listeners, while still allowing technology + * specific implementations to be built. + * + * @hide + */ +public abstract class GpsNavigationMessageProvider + extends RemoteListenerHelper<IGpsNavigationMessageListener> { + public GpsNavigationMessageProvider() { + super("GpsNavigationMessageProvider"); + } + + public void onNavigationMessageAvailable(final GpsNavigationMessageEvent event) { + ListenerOperation<IGpsNavigationMessageListener> operation = + new ListenerOperation<IGpsNavigationMessageListener>() { + @Override + public void execute(IGpsNavigationMessageListener listener) + throws RemoteException { + listener.onGpsNavigationMessageReceived(event); + } + }; + + foreach(operation); + } +} diff --git a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java index b741e75..27cf3d8 100644 --- a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java +++ b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java @@ -23,6 +23,10 @@ import android.os.RemoteException; * Implementation of a handler for {@link IGpsStatusListener}. */ abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> { + public GpsStatusListenerHelper() { + super("GpsStatusListenerHelper"); + } + public void onFirstFix(final int timeToFirstFix) { Operation operation = new Operation() { @Override diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index 79335f7..451af18 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -32,16 +32,19 @@ 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> { - private static final String TAG = "RemoteListenerHelper"; - + private final String mTag; private final HashMap<IBinder, LinkedListener> mListenerMap = new HashMap<IBinder, LinkedListener>(); + protected RemoteListenerHelper(String name) { + Preconditions.checkNotNull(name); + mTag = name; + } + public boolean addListener(@NonNull TListener listener) { Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); - if (!isSupported()) { - Log.e(TAG, "Refused to add listener, the feature is not supported."); + Log.e(mTag, "Refused to add listener, the feature is not supported."); return false; } @@ -58,13 +61,17 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { } catch (RemoteException e) { // if the remote process registering the listener is already death, just swallow the // exception and continue - Log.e(TAG, "Remote listener already died.", e); + Log.e(mTag, "Remote listener already died.", e); return false; } mListenerMap.put(binder, deathListener); if (mListenerMap.size() == 1) { - onFirstListenerAdded(); + if (!registerWithService()) { + Log.e(mTag, "RegisterWithService failed, listener will be removed."); + removeListener(listener); + return false; + } } } @@ -73,9 +80,8 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { public boolean removeListener(@NonNull TListener listener) { Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener."); - if (!isSupported()) { - Log.e(TAG, "Refused to remove listener, the feature is not supported."); + Log.e(mTag, "Refused to remove listener, the feature is not supported."); return false; } @@ -84,26 +90,19 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { synchronized (mListenerMap) { linkedListener = mListenerMap.remove(binder); if (mListenerMap.isEmpty() && linkedListener != null) { - onLastListenerRemoved(); + unregisterFromService(); } } if (linkedListener != null) { binder.unlinkToDeath(linkedListener, 0 /* flags */); } - return true; } protected abstract boolean isSupported(); - - protected void onFirstListenerAdded() { - // event triggered when the first listener has been added - } - - protected void onLastListenerRemoved() { - // event triggered when the last listener has bee removed - } + protected abstract boolean registerWithService(); + protected abstract void unregisterFromService(); protected interface ListenerOperation<TListener extends IInterface> { void execute(TListener listener) throws RemoteException; @@ -121,7 +120,7 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { try { operation.execute(listener); } catch (RemoteException e) { - Log.e(TAG, "Error in monitored listener.", e); + Log.e(mTag, "Error in monitored listener.", e); removeListener(listener); } } @@ -141,7 +140,7 @@ abstract class RemoteListenerHelper<TListener extends IInterface> { @Override public void binderDied() { - Log.d(TAG, "Remote Listener died: " + mListener); + Log.d(mTag, "Remote Listener died: " + mListener); removeListener(mListener); } } diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp index 8bb6e8a..46327d7 100644 --- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp @@ -53,6 +53,7 @@ static jmethodID method_reportGeofenceRemoveStatus; static jmethodID method_reportGeofencePauseStatus; static jmethodID method_reportGeofenceResumeStatus; static jmethodID method_reportMeasurementData; +static jmethodID method_reportNavigationMessages; static const GpsInterface* sGpsInterface = NULL; static const GpsXtraInterface* sGpsXtraInterface = NULL; @@ -62,6 +63,7 @@ static const GpsDebugInterface* sGpsDebugInterface = NULL; static const AGpsRilInterface* sAGpsRilInterface = NULL; static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL; static const GpsMeasurementInterface* sGpsMeasurementInterface = NULL; +static const GpsNavigationMessageInterface* sGpsNavigationMessageInterface = NULL; // temporary storage for GPS callbacks static GpsSvStatus sGpsSvStatus; @@ -447,6 +449,10 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, clazz, "reportMeasurementData", "(Landroid/location/GpsMeasurementsEvent;)V"); + method_reportNavigationMessages = env->GetMethodID( + clazz, + "reportNavigationMessage", + "(Landroid/location/GpsNavigationMessageEvent;)V"); err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); if (err == 0) { @@ -472,6 +478,9 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, (const GpsGeofencingInterface*)sGpsInterface->get_extension(GPS_GEOFENCING_INTERFACE); sGpsMeasurementInterface = (const GpsMeasurementInterface*)sGpsInterface->get_extension(GPS_MEASUREMENT_INTERFACE); + sGpsNavigationMessageInterface = + (const GpsNavigationMessageInterface*)sGpsInterface->get_extension( + GPS_NAVIGATION_MESSAGE_INTERFACE); } } @@ -1212,7 +1221,6 @@ static void measurement_callback(GpsData* data) { checkAndClearExceptionFromCallback(env, __FUNCTION__); } else { ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%d", data->size); - return; } } @@ -1223,7 +1231,7 @@ GpsMeasurementCallbacks sGpsMeasurementCallbacks = { static jboolean android_location_GpsLocationProvider_is_measurement_supported( JNIEnv* env, - jobject obj) { + jclass clazz) { if (sGpsMeasurementInterface != NULL) { return JNI_TRUE; } @@ -1259,6 +1267,110 @@ static jboolean android_location_GpsLocationProvider_stop_measurement_collection return JNI_TRUE; } +static jobject translate_gps_navigation_message(JNIEnv* env, GpsNavigationMessage* message) { + size_t dataLength = message->data_length; + uint8_t* data = message->data; + if (dataLength == 0 || data == NULL) { + ALOGE("Invalid Navigation Message found: data=%p, length=%d", data, dataLength); + return NULL; + } + + jclass navigationMessageClass = env->FindClass("android/location/GpsNavigationMessage"); + jmethodID navigationMessageCtor = env->GetMethodID(navigationMessageClass, "<init>", "()V"); + jobject navigationMessageObject = env->NewObject(navigationMessageClass, navigationMessageCtor); + + jmethodID setTypeMethod = env->GetMethodID(navigationMessageClass, "setType", "(B)V"); + env->CallVoidMethod(navigationMessageObject, setTypeMethod, message->type); + + jmethodID setPrnMethod = env->GetMethodID(navigationMessageClass, "setPrn", "(B)V"); + env->CallVoidMethod(navigationMessageObject, setPrnMethod, message->prn); + + jmethodID setMessageIdMethod = env->GetMethodID(navigationMessageClass, "setMessageId", "(S)V"); + env->CallVoidMethod(navigationMessageObject, setMessageIdMethod, message->message_id); + + jmethodID setSubmessageIdMethod = + env->GetMethodID(navigationMessageClass, "setSubmessageId", "(S)V"); + env->CallVoidMethod(navigationMessageObject, setSubmessageIdMethod, message->submessage_id); + + jbyteArray dataArray = env->NewByteArray(dataLength); + env->SetByteArrayRegion(dataArray, 0, dataLength, (jbyte*) data); + jmethodID setDataMethod = env->GetMethodID(navigationMessageClass, "setData", "([B)V"); + env->CallVoidMethod(navigationMessageObject, setDataMethod, dataArray); + + return navigationMessageObject; +} + +static void navigation_message_callback(GpsNavigationMessage* message) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (message == NULL) { + ALOGE("Invalid Navigation Message provided to callback"); + return; + } + + if (message->size == sizeof(GpsNavigationMessage)) { + jobject navigationMessage = translate_gps_navigation_message(env, message); + + jclass navigationMessageEventClass = + env->FindClass("android/location/GpsNavigationMessageEvent"); + jmethodID navigationMessageEventCtor = env->GetMethodID( + navigationMessageEventClass, + "<init>", + "(Landroid/location/GpsNavigationMessage;)V"); + jobject navigationMessageEvent = env->NewObject( + navigationMessageEventClass, + navigationMessageEventCtor, + navigationMessage); + + env->CallVoidMethod(mCallbacksObj, method_reportNavigationMessages, navigationMessageEvent); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + } else { + ALOGE("Invalid GpsNavigationMessage size found: %d", message->size); + } +} + +GpsNavigationMessageCallbacks sGpsNavigationMessageCallbacks = { + sizeof(GpsNavigationMessageCallbacks), + navigation_message_callback, +}; + +static jboolean android_location_GpsLocationProvider_is_navigation_message_supported( + JNIEnv* env, + jclass clazz) { + if(sGpsNavigationMessageInterface != NULL) { + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_start_navigation_message_collection( + JNIEnv* env, + jobject obj) { + if (sGpsNavigationMessageInterface == NULL) { + ALOGE("Navigation Message interface is not available."); + return JNI_FALSE; + } + + int result = sGpsNavigationMessageInterface->init(&sGpsNavigationMessageCallbacks); + if (result != GPS_NAVIGATION_MESSAGE_OPERATION_SUCCESS) { + ALOGE("An error has been found in %s: %d", __FUNCTION__, result); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +static jboolean android_location_GpsLocationProvider_stop_navigation_message_collection( + JNIEnv* env, + jobject obj) { + if (sGpsNavigationMessageInterface == NULL) { + ALOGE("Navigation Message interface is not available."); + return JNI_FALSE; + } + + sGpsNavigationMessageInterface->close(); + return JNI_TRUE; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, @@ -1336,7 +1448,16 @@ static JNINativeMethod sMethods[] = { (void*) android_location_GpsLocationProvider_start_measurement_collection}, {"native_stop_measurement_collection", "()Z", - (void*) android_location_GpsLocationProvider_stop_measurement_collection} + (void*) android_location_GpsLocationProvider_stop_measurement_collection}, + {"native_is_navigation_message_supported", + "()Z", + (void*) android_location_GpsLocationProvider_is_navigation_message_supported}, + {"native_start_navigation_message_collection", + "()Z", + (void*) android_location_GpsLocationProvider_start_navigation_message_collection}, + {"native_stop_navigation_message_collection", + "()Z", + (void*) android_location_GpsLocationProvider_stop_navigation_message_collection}, }; int register_android_server_location_GpsLocationProvider(JNIEnv* env) |