1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.location;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.TestApi;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 import java.security.InvalidParameterException;
28 
29 /**
30  * A class containing a GNSS satellite Navigation Message.
31  */
32 public final class GnssNavigationMessage implements Parcelable {
33 
34     private static final byte[] EMPTY_ARRAY = new byte[0];
35 
36     /**
37      * The type of the GNSS Navigation Message
38      * @hide
39      */
40     @Retention(RetentionPolicy.SOURCE)
41     @IntDef({TYPE_UNKNOWN, TYPE_GPS_L1CA, TYPE_GPS_L2CNAV, TYPE_GPS_L5CNAV, TYPE_GPS_CNAV2,
42             TYPE_SBS, TYPE_GLO_L1CA, TYPE_QZS_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_BDS_CNAV1,
43             TYPE_BDS_CNAV2, TYPE_GAL_I, TYPE_GAL_F, TYPE_IRN_L5CA})
44     public @interface GnssNavigationMessageType {}
45 
46     // The following enumerations must be in sync with the values declared in gps.h
47 
48     /** Message type unknown */
49     public static final int TYPE_UNKNOWN = 0;
50     /** GPS L1 C/A message contained in the structure.  */
51     public static final int TYPE_GPS_L1CA = 0x0101;
52     /** GPS L2-CNAV message contained in the structure. */
53     public static final int TYPE_GPS_L2CNAV = 0x0102;
54     /** GPS L5-CNAV message contained in the structure. */
55     public static final int TYPE_GPS_L5CNAV = 0x0103;
56     /** GPS CNAV-2 message contained in the structure. */
57     public static final int TYPE_GPS_CNAV2 = 0x0104;
58     /** SBAS message contained in the structure. */
59     public static final int TYPE_SBS = 0x0201;
60     /** Glonass L1 CA message contained in the structure. */
61     public static final int TYPE_GLO_L1CA = 0x0301;
62     /** QZSS L1 C/A message contained in the structure. */
63     public static final int TYPE_QZS_L1CA = 0x0401;
64     /** Beidou D1 message contained in the structure. */
65     public static final int TYPE_BDS_D1 = 0x0501;
66     /** Beidou D2 message contained in the structure. */
67     public static final int TYPE_BDS_D2 = 0x0502;
68     /** Beidou CNAV1 message contained in the structure. */
69     public static final int TYPE_BDS_CNAV1 = 0x0503;
70     /** Beidou CNAV2 message contained in the structure. */
71     public static final int TYPE_BDS_CNAV2 = 0x0504;
72     /** Galileo I/NAV message contained in the structure. */
73     public static final int TYPE_GAL_I = 0x0601;
74     /** Galileo F/NAV message contained in the structure. */
75     public static final int TYPE_GAL_F = 0x0602;
76     /** IRNSS L5 C/A message contained in the structure. */
77     public static final int TYPE_IRN_L5CA = 0x0701;
78 
79     /**
80      * The Navigation Message Status is 'unknown'.
81      */
82     public static final int STATUS_UNKNOWN = 0;
83 
84     /**
85      * The Navigation Message was received without any parity error in its navigation words.
86      */
87     public static final int STATUS_PARITY_PASSED = (1<<0);
88 
89     /**
90      * The Navigation Message was received with words that failed parity check, but the receiver was
91      * able to correct those words.
92      */
93     public static final int STATUS_PARITY_REBUILT = (1<<1);
94 
95     /**
96      * Used for receiving GNSS satellite Navigation Messages from the GNSS engine.
97      *
98      * <p>You can implement this interface and call
99      * {@link LocationManager#registerGnssNavigationMessageCallback}.
100      */
101     public static abstract class Callback {
102         /**
103          * The status of GNSS Navigation Message event.
104          * @hide
105          */
106         @Retention(RetentionPolicy.SOURCE)
107         @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
108         public @interface GnssNavigationMessageStatus {}
109 
110         /**
111          * The system does not support tracking of GNSS Navigation Messages.
112          *
113          * This status will not change in the future.
114          */
115         public static final int STATUS_NOT_SUPPORTED = 0;
116 
117         /**
118          * GNSS Navigation Messages are successfully being tracked, it will receive updates once
119          * they are available.
120          */
121         public static final int STATUS_READY = 1;
122 
123         /**
124          * GNSS provider or Location is disabled, updated will not be received until they are
125          * enabled.
126          */
127         public static final int STATUS_LOCATION_DISABLED = 2;
128 
129         /**
130          * Returns the latest collected GNSS Navigation Message.
131          */
onGnssNavigationMessageReceived(GnssNavigationMessage event)132         public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {}
133 
134         /**
135          * Returns the latest status of the GNSS Navigation Messages sub-system.
136          */
onStatusChanged(@nssNavigationMessageStatus int status)137         public void onStatusChanged(@GnssNavigationMessageStatus int status) {}
138     }
139 
140     // End enumerations in sync with gps.h
141 
142     private int mType;
143     private int mSvid;
144     private int mMessageId;
145     private int mSubmessageId;
146     private byte[] mData;
147     private int mStatus;
148 
149     /**
150      * @hide
151      */
152     @TestApi
GnssNavigationMessage()153     public GnssNavigationMessage() {
154         initialize();
155     }
156 
157     /**
158      * Sets all contents to the values stored in the provided object.
159      * @hide
160      */
161     @TestApi
set(GnssNavigationMessage navigationMessage)162     public void set(GnssNavigationMessage navigationMessage) {
163         mType = navigationMessage.mType;
164         mSvid = navigationMessage.mSvid;
165         mMessageId = navigationMessage.mMessageId;
166         mSubmessageId = navigationMessage.mSubmessageId;
167         mData = navigationMessage.mData;
168         mStatus = navigationMessage.mStatus;
169     }
170 
171     /**
172      * Resets all the contents to its original state.
173      * @hide
174      */
175     @TestApi
reset()176     public void reset() {
177         initialize();
178     }
179 
180     /**
181      * Gets the type of the navigation message contained in the object.
182      */
183     @GnssNavigationMessageType
getType()184     public int getType() {
185         return mType;
186     }
187 
188     /**
189      * Sets the type of the navigation message.
190      * @hide
191      */
192     @TestApi
setType(@nssNavigationMessageType int value)193     public void setType(@GnssNavigationMessageType int value) {
194         mType = value;
195     }
196 
197     /**
198      * Gets a string representation of the 'type'.
199      * For internal and logging use only.
200      */
getTypeString()201     private String getTypeString() {
202         switch (mType) {
203             case TYPE_UNKNOWN:
204                 return "Unknown";
205             case TYPE_GPS_L1CA:
206                 return "GPS L1 C/A";
207             case TYPE_GPS_L2CNAV:
208                 return "GPS L2-CNAV";
209             case TYPE_GPS_L5CNAV:
210                 return "GPS L5-CNAV";
211             case TYPE_GPS_CNAV2:
212                 return "GPS CNAV2";
213             case TYPE_SBS:
214                 return "SBS";
215             case TYPE_GLO_L1CA:
216                 return "Glonass L1 C/A";
217             case TYPE_QZS_L1CA:
218                 return "QZSS L1 C/A";
219             case TYPE_BDS_D1:
220                 return "Beidou D1";
221             case TYPE_BDS_D2:
222                 return "Beidou D2";
223             case TYPE_BDS_CNAV1:
224                 return "Beidou CNAV1";
225             case TYPE_BDS_CNAV2:
226                 return "Beidou CNAV2";
227             case TYPE_GAL_I:
228                 return "Galileo I";
229             case TYPE_GAL_F:
230                 return "Galileo F";
231             case TYPE_IRN_L5CA:
232                 return "IRNSS L5 C/A";
233             default:
234                 return "<Invalid:" + mType + ">";
235         }
236     }
237 
238     /**
239      * Gets the satellite ID.
240      *
241      * <p>Range varies by constellation.  See definition at {@code GnssStatus#getSvid(int)}
242      */
getSvid()243     public int getSvid() {
244         return mSvid;
245     }
246 
247     /**
248      * Sets the satellite ID.
249      * @hide
250      */
251     @TestApi
setSvid(int value)252     public void setSvid(int value) {
253         mSvid = value;
254     }
255 
256     /**
257      * Gets the Message identifier.
258      *
259      * <p>This provides an index to help with complete Navigation Message assembly. Similar
260      * identifiers within the data bits themselves often supplement this information, in ways even
261      * more specific to each message type; see the relevant satellite constellation ICDs for
262      * details.
263      *
264      * <ul>
265      * <li> For GPS L1 C/A subframe 4 and 5, this value corresponds to the 'frame id' of the
266      * navigation message, in the range of 1-25 (Subframe 1, 2, 3 does not contain a 'frame id' and
267      * this value can be set to -1.)</li>
268      * <li> For Glonass L1 C/A, this refers to the frame ID, in the range of 1-5.</li>
269      * <li> For BeiDou D1, this refers to the frame number in the range of 1-24</li>
270      * <li> For Beidou D2, this refers to the frame number, in the range of 1-120</li>
271      * <li> For Galileo F/NAV nominal frame structure, this refers to the subframe number, in the
272      * range of 1-12</li>
273      * <li> For Galileo I/NAV nominal frame structure, this refers to the subframe number in the
274      * range of 1-24</li>
275      * <li> For SBAS and Beidou CNAV2, this is unused and can be set to -1.</li>
276      * <li> For QZSS L1 C/A subframe 4 and 5, this value corresponds to the 'frame id' of the
277      * navigation message, in the range of 1-25 (Subframe 1, 2, 3 does not contain a 'frame id' and
278      * this value can be set to -1.)</li>
279      * <li> For Beidou CNAV1 this refers to the page type number in the range of 1-63.</li>
280      * <li> For IRNSS L5 C/A subframe 3 and 4, this value corresponds to the Message Id of the
281      * navigation message, in the range of 1-63. (Subframe 1 and 2 does not contain a message type
282      * id and this value can be set to -1.)</li>
283      * </ul>
284      */
getMessageId()285     public int getMessageId() {
286         return mMessageId;
287     }
288 
289     /**
290      * Sets the Message Identifier.
291      * @hide
292      */
293     @TestApi
setMessageId(int value)294     public void setMessageId(int value) {
295         mMessageId = value;
296     }
297 
298     /**
299      * Gets the sub-message identifier, relevant to the {@link #getType()} of the message.
300      *
301      * <ul>
302      * <li> For GPS L1 C/A, BeiDou D1 &amp; BeiDou D2, the submessage id corresponds to the subframe
303      * number of the navigation message, in the range of 1-5.</li>
304      * <li>For Glonass L1 C/A, this refers to the String number, in the range from 1-15</li>
305      * <li>For Galileo F/NAV, this refers to the page type in the range 1-6</li>
306      * <li>For Galileo I/NAV, this refers to the word type in the range 1-10+</li>
307      * <li>For Galileo in particular, the type information embedded within the data bits may be even
308      * more useful in interpretation, than the nominal page and word types provided in this
309      * field.</li>
310      * <li> For SBAS, the submessage id corresponds to the message type, in the range 1-63.</li>
311      * <li> For Beidou CNAV1, the submessage id corresponds to the subframe number of the
312      * navigation message, in the range of 1-3.</li>
313      * <li> For Beidou CNAV2, the submessage id corresponds to the message type, in the range
314      * 1-63.</li>
315      * <li> For IRNSS L5 C/A, the submessage id corresponds to the subframe number of the
316      * navigation message, in the range of 1-4.</li>
317      * </ul>
318      */
getSubmessageId()319     public int getSubmessageId() {
320         return mSubmessageId;
321     }
322 
323     /**
324      * Sets the Sub-message identifier.
325      * @hide
326      */
327     @TestApi
setSubmessageId(int value)328     public void setSubmessageId(int value) {
329         mSubmessageId = value;
330     }
331 
332     /**
333      * Gets the data of the reported GPS message.
334      *
335      * <p>The bytes (or words) specified using big endian format (MSB first).
336      *
337      * <ul>
338      * <li>For GPS L1 C/A, Beidou D1 &amp; Beidou D2, each subframe contains 10 30-bit words. Each
339      * word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip B31 and B32), with
340      * MSB first, for a total of 40 bytes, covering a time period of 6, 6, and 0.6 seconds,
341      * respectively.</li>
342      * <li>For Glonass L1 C/A, each string contains 85 data bits, including the checksum.  These
343      * bits should be fit into 11 bytes, with MSB first (skip B86-B88), covering a time period of 2
344      * seconds.</li>
345      * <li>For Galileo F/NAV, each word consists of 238-bit (sync &amp; tail symbols excluded). Each
346      * word should be fit into 30-bytes, with MSB first (skip B239, B240), covering a time period of
347      * 10 seconds.</li>
348      * <li>For Galileo I/NAV, each page contains 2 page parts, even and odd, with a total of 2x114 =
349      * 228 bits, (sync &amp; tail excluded) that should be fit into 29 bytes, with MSB first (skip
350      * B229-B232).</li>
351      * <li>For SBAS, each block consists of 250 data bits, that should be fit into 32 bytes.  MSB
352      * first (skip B251-B256).</li>
353      * <li>For Beidou CNAV1, subframe #1 consists of 14 data bits, that should be fit into 2
354      * bytes. MSB first (skip B15-B16).  subframe #2 consists of 600 bits that should be fit into
355      * 75 bytes. subframe #3 consists of 264 data bits that should be fit into 33 bytes.</li>
356      * <li>For Beidou CNAV2, each subframe consists of 288 data bits, that should be fit into 36
357      * bytes.</li>
358      * </ul>
359      */
360     @NonNull
getData()361     public byte[] getData() {
362         return mData;
363     }
364 
365     /**
366      * Sets the data associated with the Navigation Message.
367      * @hide
368      */
369     @TestApi
setData(byte[] value)370     public void setData(byte[] value) {
371         if (value == null) {
372             throw new InvalidParameterException("Data must be a non-null array");
373         }
374 
375         mData = value;
376     }
377 
378     /**
379      * Gets the Status of the navigation message contained in the object.
380      */
getStatus()381     public int getStatus() {
382         return mStatus;
383     }
384 
385     /**
386      * Sets the status of the navigation message.
387      * @hide
388      */
389     @TestApi
setStatus(int value)390     public void setStatus(int value) {
391         mStatus = value;
392     }
393 
394     /**
395      * Gets a string representation of the 'status'.
396      * For internal and logging use only.
397      */
getStatusString()398     private String getStatusString() {
399         switch (mStatus) {
400             case STATUS_UNKNOWN:
401                 return "Unknown";
402             case STATUS_PARITY_PASSED:
403                 return "ParityPassed";
404             case STATUS_PARITY_REBUILT:
405                 return "ParityRebuilt";
406             default:
407                 return "<Invalid:" + mStatus + ">";
408         }
409     }
410 
411     public static final @android.annotation.NonNull Creator<GnssNavigationMessage> CREATOR =
412             new Creator<GnssNavigationMessage>() {
413         @Override
414         public GnssNavigationMessage createFromParcel(Parcel parcel) {
415             GnssNavigationMessage navigationMessage = new GnssNavigationMessage();
416 
417             navigationMessage.setType(parcel.readInt());
418             navigationMessage.setSvid(parcel.readInt());
419             navigationMessage.setMessageId(parcel.readInt());
420             navigationMessage.setSubmessageId(parcel.readInt());
421             int dataLength = parcel.readInt();
422             byte[] data = new byte[dataLength];
423             parcel.readByteArray(data);
424             navigationMessage.setData(data);
425             navigationMessage.setStatus(parcel.readInt());
426 
427             return navigationMessage;
428         }
429 
430         @Override
431         public GnssNavigationMessage[] newArray(int size) {
432             return new GnssNavigationMessage[size];
433         }
434     };
435 
436     @Override
writeToParcel(Parcel parcel, int flags)437     public void writeToParcel(Parcel parcel, int flags) {
438         parcel.writeInt(mType);
439         parcel.writeInt(mSvid);
440         parcel.writeInt(mMessageId);
441         parcel.writeInt(mSubmessageId);
442         parcel.writeInt(mData.length);
443         parcel.writeByteArray(mData);
444         parcel.writeInt(mStatus);
445     }
446 
447     @Override
describeContents()448     public int describeContents() {
449         return 0;
450     }
451 
452     @Override
toString()453     public String toString() {
454         final String format = "   %-15s = %s\n";
455         StringBuilder builder = new StringBuilder("GnssNavigationMessage:\n");
456 
457         builder.append(String.format(format, "Type", getTypeString()));
458         builder.append(String.format(format, "Svid", mSvid));
459         builder.append(String.format(format, "Status", getStatusString()));
460         builder.append(String.format(format, "MessageId", mMessageId));
461         builder.append(String.format(format, "SubmessageId", mSubmessageId));
462 
463         builder.append(String.format(format, "Data", "{"));
464         String prefix = "        ";
465         for(byte value : mData) {
466             builder.append(prefix);
467             builder.append(value);
468             prefix = ", ";
469         }
470         builder.append(" }");
471 
472         return builder.toString();
473     }
474 
initialize()475     private void initialize() {
476         mType = TYPE_UNKNOWN;
477         mSvid = 0;
478         mMessageId = -1;
479         mSubmessageId = -1;
480         mData = EMPTY_ARRAY;
481         mStatus = STATUS_UNKNOWN;
482     }
483 }
484