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 & 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 & 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 & 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 & 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