1 /* 2 * Copyright (C) 2017 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.cts.gnss.suplClient; 18 19 import android.location.cts.asn1.base.PacketBuilder; 20 import android.location.cts.asn1.supl2.rrlp_messages.PDU; 21 import android.location.cts.asn1.supl2.supl_pos.PosPayLoad; 22 import android.location.cts.asn1.supl2.supl_pos.SUPLPOS; 23 import android.location.cts.asn1.supl2.supl_pos_init.NavigationModel; 24 import android.location.cts.asn1.supl2.supl_pos_init.RequestedAssistData; 25 import android.location.cts.asn1.supl2.supl_pos_init.SUPLPOSINIT; 26 import android.location.cts.asn1.supl2.supl_start.PosProtocol; 27 import android.location.cts.asn1.supl2.supl_start.PosTechnology; 28 import android.location.cts.asn1.supl2.supl_start.PrefMethod; 29 import android.location.cts.asn1.supl2.supl_start.SETCapabilities; 30 import android.location.cts.asn1.supl2.supl_start.SUPLSTART; 31 import android.location.cts.asn1.supl2.ulp.ULP_PDU; 32 import android.location.cts.asn1.supl2.ulp.UlpMessage; 33 import android.location.cts.asn1.supl2.ulp_components.CellInfo; 34 import android.location.cts.asn1.supl2.ulp_components.LocationId; 35 import android.location.cts.asn1.supl2.ulp_components.Position; 36 import android.location.cts.asn1.supl2.ulp_components.Position.timestampType; 37 import android.location.cts.asn1.supl2.ulp_components.PositionEstimate; 38 import android.location.cts.asn1.supl2.ulp_components.PositionEstimate.latitudeSignType; 39 import android.location.cts.asn1.supl2.ulp_components.SessionID; 40 import android.location.cts.asn1.supl2.ulp_components.SetSessionID; 41 import android.location.cts.asn1.supl2.ulp_components.Status; 42 import android.location.cts.asn1.supl2.ulp_components.Version; 43 import android.location.cts.asn1.supl2.ulp_components.WcdmaCellInformation; 44 45 import java.math.BigInteger; 46 import java.net.InetAddress; 47 import java.net.UnknownHostException; 48 import java.nio.ByteBuffer; 49 import java.nio.ByteOrder; 50 import java.util.BitSet; 51 import java.util.Calendar; 52 import java.util.GregorianCalendar; 53 import java.util.Random; 54 import java.util.TimeZone; 55 56 import javax.annotation.Nullable; 57 58 /** 59 * A class that generates several types of GPS SUPL client payloads that can be transmitted over a 60 * GPS socket. 61 * 62 * <p>Two types of SUPL payloads are supported in this version: Local Location and WCDMA versions. 63 * However, it should be straightforward to extend this class to support other types of SUPL 64 * requests. 65 */ 66 public class SuplRrlpMessagesGenerator { 67 // Scale factors used for conversion from latitude and longitude in SUPL protocol format 68 // to decimal format 69 private static final double POSITION_ESTIMATE_LAT_SCALE_FACTOR = 90.0 / 8388608.0; 70 private static final double POSITION_ESTIMATE_LNG_SCALE_FACTOR = 180.0 / 8388608.0; 71 72 /** 73 * Generate a SUPL START message that can be send by the SUPL client to the server in the case 74 * that device location is known via a latitude and a longitude. 75 * 76 * <p>SUPL START is the first message to be send from the client to the server. The server should 77 * response to the SUPL START message with a SUPL RESPONSE message containing a SessionID. 78 * 79 */ generateSuplStartLocalLocationMessage(@ullable InetAddress ipAddress)80 public static byte[] generateSuplStartLocalLocationMessage(@Nullable InetAddress ipAddress) 81 throws UnknownHostException { 82 83 ULP_PDU ulpPdu = new ULP_PDU(); 84 Version version = ulpPdu.setVersionToNewInstance(); 85 version.setMinToNewInstance().setInteger(BigInteger.ZERO); 86 version.setMajToNewInstance().setInteger(BigInteger.valueOf(2)); 87 version.setServindToNewInstance().setInteger(BigInteger.ZERO); 88 ulpPdu.setVersion(version); 89 90 SessionID sessionId = ulpPdu.setSessionIDToNewInstance(); 91 92 SetSessionID setSessionId = sessionId.setSetSessionIDToNewInstance(); 93 setSessionId.setSessionIdToNewInstance() 94 .setInteger(BigInteger.valueOf(new Random().nextInt(65536))); 95 if (ipAddress == null){ 96 ipAddress = InetAddress.getLocalHost(); 97 } 98 byte[] ipAsbytes = ipAddress.getAddress(); 99 setSessionId.setSetIdToNewInstance().setIPAddressToNewInstance().setIpv4AddressToNewInstance() 100 .setValue(ipAsbytes); 101 102 UlpMessage message = new UlpMessage(); 103 SUPLSTART suplStart = message.setMsSUPLSTARTToNewInstance(); 104 SETCapabilities setCapabilities = suplStart.setSETCapabilitiesToNewInstance(); 105 PosTechnology posTechnology = setCapabilities.setPosTechnologyToNewInstance(); 106 posTechnology.setAgpsSETassistedToNewInstance().setValue(false); 107 posTechnology.setAgpsSETBasedToNewInstance().setValue(true); 108 posTechnology.setAutonomousGPSToNewInstance().setValue(true); 109 posTechnology.setAFLTToNewInstance().setValue(false); 110 posTechnology.setECIDToNewInstance().setValue(false); 111 posTechnology.setEOTDToNewInstance().setValue(false); 112 posTechnology.setOTDOAToNewInstance().setValue(false); 113 114 setCapabilities.setPrefMethodToNewInstance().setValue(PrefMethod.Value.agpsSETBasedPreferred); 115 116 PosProtocol posProtocol = setCapabilities.setPosProtocolToNewInstance(); 117 posProtocol.setTia801ToNewInstance().setValue(false); 118 posProtocol.setRrlpToNewInstance().setValue(true); 119 posProtocol.setRrcToNewInstance().setValue(false); 120 121 LocationId locationId = suplStart.setLocationIdToNewInstance(); 122 CellInfo cellInfo = locationId.setCellInfoToNewInstance(); 123 cellInfo.setExtensionVer2_CellInfo_extensionToNewInstance(); 124 // FF-FF-FF-FF-FF-FF 125 final String macBinary = "111111111111111111111111111111111111111111111111"; 126 BitSet bits = new BitSet(macBinary.length()); 127 for (int i = 0; i < macBinary.length(); ++i) { 128 if (macBinary.charAt(i) == '1') { 129 bits.set(i); 130 } 131 } 132 cellInfo.getExtensionVer2_CellInfo_extension().setWlanAPToNewInstance() 133 .setApMACAddressToNewInstance().setValue(bits); 134 locationId.setStatusToNewInstance().setValue(Status.Value.current); 135 136 message.setMsSUPLSTART(suplStart); 137 138 ulpPdu.setMessage(message); 139 return encodeUlp(ulpPdu); 140 } 141 142 /** 143 * Generate a SUPL POS INIT message that can be send by the SUPL client to the server in the case 144 * that device location is known via a latitude and a longitude. 145 * 146 * <p>SUPL POS INIT is the second message to be send from the client to the server after receiving 147 * a SUPL RESPONSE containing a SessionID from the server. The SessionID received 148 * from the server response should set in the SUPL POS INIT message. 149 * 150 */ generateSuplPositionInitLocalLocationMessage(SessionID sessionId, long latE7, long lngE7)151 public static byte[] generateSuplPositionInitLocalLocationMessage(SessionID sessionId, long latE7, 152 long lngE7) { 153 154 ULP_PDU ulpPdu = new ULP_PDU(); 155 Version version = ulpPdu.setVersionToNewInstance(); 156 version.setMinToNewInstance().setInteger(BigInteger.ZERO); 157 version.setMajToNewInstance().setInteger(BigInteger.valueOf(2)); 158 version.setServindToNewInstance().setInteger(BigInteger.ZERO); 159 ulpPdu.setVersion(version); 160 161 ulpPdu.setSessionID(sessionId); 162 163 UlpMessage message = new UlpMessage(); 164 SUPLPOSINIT suplPosInit = message.setMsSUPLPOSINITToNewInstance(); 165 SETCapabilities setCapabilities = suplPosInit.setSETCapabilitiesToNewInstance(); 166 PosTechnology posTechnology = setCapabilities.setPosTechnologyToNewInstance(); 167 posTechnology.setAgpsSETassistedToNewInstance().setValue(false); 168 posTechnology.setAgpsSETBasedToNewInstance().setValue(true); 169 posTechnology.setAutonomousGPSToNewInstance().setValue(true); 170 posTechnology.setAFLTToNewInstance().setValue(false); 171 posTechnology.setECIDToNewInstance().setValue(false); 172 posTechnology.setEOTDToNewInstance().setValue(false); 173 posTechnology.setOTDOAToNewInstance().setValue(false); 174 175 setCapabilities.setPrefMethodToNewInstance().setValue(PrefMethod.Value.agpsSETBasedPreferred); 176 177 PosProtocol posProtocol = setCapabilities.setPosProtocolToNewInstance(); 178 posProtocol.setTia801ToNewInstance().setValue(false); 179 posProtocol.setRrlpToNewInstance().setValue(true); 180 posProtocol.setRrcToNewInstance().setValue(false); 181 182 RequestedAssistData reqAssistData = suplPosInit.setRequestedAssistDataToNewInstance(); 183 184 reqAssistData.setAlmanacRequestedToNewInstance().setValue(false); 185 reqAssistData.setUtcModelRequestedToNewInstance().setValue(false); 186 reqAssistData.setIonosphericModelRequestedToNewInstance().setValue(true); 187 reqAssistData.setDgpsCorrectionsRequestedToNewInstance().setValue(false); 188 reqAssistData.setReferenceLocationRequestedToNewInstance().setValue(false); 189 reqAssistData.setReferenceTimeRequestedToNewInstance().setValue(true); 190 reqAssistData.setAcquisitionAssistanceRequestedToNewInstance().setValue(false); 191 reqAssistData.setRealTimeIntegrityRequestedToNewInstance().setValue(false); 192 reqAssistData.setNavigationModelRequestedToNewInstance().setValue(true); 193 NavigationModel navigationModelData = reqAssistData.setNavigationModelDataToNewInstance(); 194 navigationModelData.setGpsWeekToNewInstance().setInteger(BigInteger.ZERO); 195 navigationModelData.setGpsToeToNewInstance().setInteger(BigInteger.ZERO); 196 navigationModelData.setNSATToNewInstance().setInteger(BigInteger.ZERO); 197 navigationModelData.setToeLimitToNewInstance().setInteger(BigInteger.ZERO); 198 199 LocationId locationId = suplPosInit.setLocationIdToNewInstance(); 200 CellInfo cellInfo = locationId.setCellInfoToNewInstance(); 201 cellInfo.setExtensionVer2_CellInfo_extensionToNewInstance(); 202 // FF-FF-FF-FF-FF-FF 203 final String macBinary = "111111111111111111111111111111111111111111111111"; 204 BitSet bits = new BitSet(macBinary.length()); 205 for (int i = 0; i < macBinary.length(); ++i) { 206 if (macBinary.charAt(i) == '1') { 207 bits.set(i); 208 } 209 } 210 cellInfo.getExtensionVer2_CellInfo_extension().setWlanAPToNewInstance() 211 .setApMACAddressToNewInstance().setValue(bits); 212 locationId.setStatusToNewInstance().setValue(Status.Value.current); 213 214 Position pos = suplPosInit.setPositionToNewInstance(); 215 timestampType utcTime = pos.setTimestampToNewInstance(); 216 Calendar currentTime = GregorianCalendar.getInstance(TimeZone.getTimeZone("UTC")); 217 utcTime.setYear(currentTime.get(Calendar.YEAR)); 218 utcTime.setMonth(currentTime.get(Calendar.MONTH) + 1); // Calendar's MONTH starts from 0. 219 utcTime.setDay(currentTime.get(Calendar.DAY_OF_MONTH)); 220 utcTime.setHour(currentTime.get(Calendar.HOUR_OF_DAY)); 221 utcTime.setMinute(currentTime.get(Calendar.MINUTE)); 222 utcTime.setSecond(currentTime.get(Calendar.SECOND)); 223 224 PositionEstimate posEstimate = pos.setPositionEstimateToNewInstance(); 225 226 long latSuplFormat = (long) (Math.abs(latE7) / (POSITION_ESTIMATE_LAT_SCALE_FACTOR * 1E7)); 227 long lngSuplFormat = (long) (lngE7 / (POSITION_ESTIMATE_LNG_SCALE_FACTOR * 1E7)); 228 posEstimate.setLatitudeToNewInstance().setInteger(BigInteger.valueOf(latSuplFormat)); 229 posEstimate.setLongitudeToNewInstance().setInteger(BigInteger.valueOf(lngSuplFormat)); 230 posEstimate.setLatitudeSignToNewInstance() 231 .setValue(latE7 > 0 ? latitudeSignType.Value.north : latitudeSignType.Value.south); 232 233 message.setMsSUPLPOSINIT(suplPosInit); 234 235 ulpPdu.setMessage(message); 236 return encodeUlp(ulpPdu); 237 } 238 generateAssistanceDataAckMessage(SessionID sessionId)239 public static byte[] generateAssistanceDataAckMessage(SessionID sessionId) { 240 ULP_PDU ulpPdu = new ULP_PDU(); 241 Version version = ulpPdu.setVersionToNewInstance(); 242 version.setMinToNewInstance().setInteger(BigInteger.ZERO); 243 version.setMajToNewInstance().setInteger(BigInteger.valueOf(2)); 244 version.setServindToNewInstance().setInteger(BigInteger.ZERO); 245 ulpPdu.setVersion(version); 246 247 ulpPdu.setSessionID(sessionId); 248 249 PDU pdu = new PDU(); 250 pdu.setReferenceNumberToNewInstance(); 251 pdu.getReferenceNumber().setInteger(BigInteger.ONE); 252 pdu.setComponentToNewInstance(); 253 pdu.getComponent().setAssistanceDataAckToNewInstance(); 254 255 PacketBuilder payloadBuilder = new PacketBuilder(); 256 try { 257 payloadBuilder.appendAll(pdu.encodePerUnaligned()); 258 } catch (IllegalArgumentException | IllegalStateException | IndexOutOfBoundsException 259 | UnsupportedOperationException e) { 260 throw new RuntimeException(e); 261 } 262 PosPayLoad.rrlpPayloadType rrlpPayload = new PosPayLoad.rrlpPayloadType(); 263 rrlpPayload.setValue(payloadBuilder.getPaddedBytes()); 264 265 UlpMessage message = new UlpMessage(); 266 SUPLPOS suplPos = message.setMsSUPLPOSToNewInstance(); 267 suplPos.setPosPayLoadToNewInstance(); 268 suplPos.getPosPayLoad().setRrlpPayload(rrlpPayload); 269 270 ulpPdu.setMessage(message); 271 272 return encodeUlp(ulpPdu); 273 } 274 275 /** Encodes a ULP_PDU message into bytes and sets the length field. */ encodeUlp(ULP_PDU message)276 public static byte[] encodeUlp(ULP_PDU message) { 277 message.setLengthToNewInstance(); 278 message.getLength().setInteger(BigInteger.ZERO); 279 PacketBuilder messageBuilder = new PacketBuilder(); 280 messageBuilder.appendAll(message.encodePerUnaligned()); 281 byte[] result = messageBuilder.getPaddedBytes(); 282 ByteBuffer buffer = ByteBuffer.wrap(result); 283 buffer.order(ByteOrder.BIG_ENDIAN); 284 buffer.putShort((short) result.length); 285 return buffer.array(); 286 } 287 288 } 289