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