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