Expose Gps Navigation Messages in the platform.
b/16405976

Change-Id: Ic982ea2467d116585780c20c2fa7e974e50f8345
diff --git a/Android.mk b/Android.mk
index 7dfa6a0..278e67f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -297,6 +297,7 @@
 	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.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 @@
     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 @@
     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 @@
         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 @@
     /**
      * 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 @@
         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.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.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 @@
     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 @@
             mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
         }
         mGpsMeasurementsProvider = gpsProvider.getGpsMeasurementsProvider();
+        mGpsNavigationMessageProvider = gpsProvider.getGpsNavigationMessageProvider();
 
         /*
         Load package name(s) containing location provider support.
@@ -1847,7 +1851,6 @@
         if (!hasLocationAccess) {
             return false;
         }
-
         return mGpsMeasurementsProvider.addListener(listener);
     }
 
@@ -1857,6 +1860,35 @@
     }
 
     @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.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 @@
         protected boolean isSupported() {
             return GpsLocationProvider.isSupported();
         }
+
+        @Override
+        protected boolean registerWithService() {
+            return true;
+        }
+
+        @Override
+        protected void unregisterFromService() {}
     };
 
     // Handler for processing events
@@ -374,16 +383,34 @@
         }
 
         @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 @@
         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 @@
     }
 
     /**
-     * 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 @@
 
     // 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 @@
  */
 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 @@
  * 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 @@
  * 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 @@
             } 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 @@
 
     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 @@
         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 @@
             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 @@
 
         @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_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 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 @@
             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 @@
             (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 @@
         checkAndClearExceptionFromCallback(env, __FUNCTION__);
     } else {
         ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%d", data->size);
-        return;
     }
 }
 
@@ -1223,7 +1231,7 @@
 
 static jboolean android_location_GpsLocationProvider_is_measurement_supported(
         JNIEnv* env,
-        jobject obj) {
+        jclass clazz) {
     if (sGpsMeasurementInterface != NULL) {
         return JNI_TRUE;
     }
@@ -1259,6 +1267,110 @@
     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 @@
             (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)