1 /*
2  * Copyright (C) 2021 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 com.android.server.uwb.params;
18 
19 import static com.android.server.uwb.config.CapabilityParam.CCC_CHANNEL_5;
20 import static com.android.server.uwb.config.CapabilityParam.CCC_CHANNEL_9;
21 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_12;
22 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_24;
23 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_3;
24 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_4;
25 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_6;
26 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_8;
27 import static com.android.server.uwb.config.CapabilityParam.CCC_CHAPS_PER_SLOT_9;
28 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_CONFIG_MODE_ADAPTIVE;
29 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_CONFIG_MODE_CONTINUOUS;
30 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_CONFIG_MODE_NONE;
31 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_SEQUENCE_AES;
32 import static com.android.server.uwb.config.CapabilityParam.CCC_HOPPING_SEQUENCE_DEFAULT;
33 import static com.android.server.uwb.config.CapabilityParam.CCC_PRIORITIZED_CHANNEL_LIST;
34 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_CHANNELS;
35 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_CHAPS_PER_SLOT;
36 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES;
37 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER;
38 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS;
39 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_PULSE_SHAPE_COMBOS;
40 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_RAN_MULTIPLIER;
41 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_SYNC_CODES;
42 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_UWB_CONFIGS;
43 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_UWBS_MAX_PPM;
44 import static com.android.server.uwb.config.CapabilityParam.CCC_SUPPORTED_VERSIONS;
45 
46 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_12;
47 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_24;
48 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_3;
49 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_4;
50 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_6;
51 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_8;
52 import static com.google.uwb.support.ccc.CccParams.CHAPS_PER_SLOT_9;
53 import static com.google.uwb.support.ccc.CccParams.HOPPING_CONFIG_MODE_ADAPTIVE;
54 import static com.google.uwb.support.ccc.CccParams.HOPPING_CONFIG_MODE_CONTINUOUS;
55 import static com.google.uwb.support.ccc.CccParams.HOPPING_CONFIG_MODE_NONE;
56 import static com.google.uwb.support.ccc.CccParams.HOPPING_SEQUENCE_AES;
57 import static com.google.uwb.support.ccc.CccParams.HOPPING_SEQUENCE_DEFAULT;
58 import static com.google.uwb.support.ccc.CccParams.UWB_CHANNEL_5;
59 import static com.google.uwb.support.ccc.CccParams.UWB_CHANNEL_9;
60 
61 import android.util.Log;
62 
63 import com.android.server.uwb.UwbInjector;
64 import com.android.server.uwb.config.ConfigParam;
65 
66 import com.google.uwb.support.base.Params;
67 import com.google.uwb.support.base.ProtocolVersion;
68 import com.google.uwb.support.ccc.CccProtocolVersion;
69 import com.google.uwb.support.ccc.CccPulseShapeCombo;
70 import com.google.uwb.support.ccc.CccRangingStartedParams;
71 import com.google.uwb.support.ccc.CccRangingStoppedParams;
72 import com.google.uwb.support.ccc.CccSpecificationParams;
73 
74 import java.nio.ByteBuffer;
75 import java.nio.ByteOrder;
76 
77 /**
78  * CCC decoder
79  */
80 public class CccDecoder extends TlvDecoder {
81     private static final String TAG = "CccDecoder";
82     private final UwbInjector mUwbInjector;
83 
CccDecoder(UwbInjector uwbInjector)84     public CccDecoder(UwbInjector uwbInjector) {
85         mUwbInjector = uwbInjector;
86     }
87 
88     @Override
getParams(TlvDecoderBuffer tlvs, Class<T> paramsType, ProtocolVersion protocolVersion)89     public <T extends Params> T getParams(TlvDecoderBuffer tlvs, Class<T> paramsType,
90             ProtocolVersion protocolVersion)
91             throws IllegalArgumentException {
92         if (CccRangingStartedParams.class.equals(paramsType)) {
93             return (T) getCccRangingStartedParamsFromTlvBuffer(tlvs);
94         }
95         if (CccSpecificationParams.class.equals(paramsType)) {
96             return (T) getCccSpecificationParamsFromTlvBuffer(tlvs);
97         }
98         if (CccRangingStoppedParams.class.equals(paramsType)) {
99             return (T) getCccRangingStoppedParamsFromTlvBuffer(tlvs);
100         }
101         return null;
102     }
103 
isBitSet(int flags, int mask)104     private static boolean isBitSet(int flags, int mask) {
105         return (flags & mask) != 0;
106     }
107 
getCccRangingStartedParamsFromTlvBuffer(TlvDecoderBuffer tlvs)108     private CccRangingStartedParams getCccRangingStartedParamsFromTlvBuffer(TlvDecoderBuffer tlvs) {
109         byte[] hopModeKey = tlvs.getByteArray(ConfigParam.HOP_MODE_KEY);
110         int hopModeKeyInt = ByteBuffer.wrap(hopModeKey).order(ByteOrder.LITTLE_ENDIAN).getInt();
111         long uwbTime0;
112         // Backwards compatibility with vendors who were using Google defined
113         // UWB_TIME0 TLV param.
114         try {
115             uwbTime0 = tlvs.getLong(ConfigParam.UWB_TIME0);
116         } catch (IllegalArgumentException e) {
117             uwbTime0 = tlvs.getLong(ConfigParam.UWB_INITIATION_TIME);
118         }
119 
120         return new CccRangingStartedParams.Builder()
121                 // STS_Index0  0 - 0x3FFFFFFFF
122                 .setStartingStsIndex(tlvs.getInt(ConfigParam.STS_INDEX))
123                 .setHopModeKey(hopModeKeyInt)
124                 //  UWB_Time0 0 - 0xFFFFFFFFFFFFFFFF  UWB_INITIATION_TIME
125                 .setUwbTime0(uwbTime0)
126                 // RANGING_INTERVAL = RAN_Multiplier * 96
127                 .setRanMultiplier(tlvs.getInt(ConfigParam.RANGING_INTERVAL) / 96)
128                 .setSyncCodeIndex(tlvs.getByte(ConfigParam.PREAMBLE_CODE_INDEX))
129                 .build();
130     }
131 
getCccSpecificationParamsFromTlvBuffer(TlvDecoderBuffer tlvs)132     private CccSpecificationParams getCccSpecificationParamsFromTlvBuffer(TlvDecoderBuffer tlvs) {
133         CccSpecificationParams.Builder builder = new CccSpecificationParams.Builder();
134         byte[] versions = tlvs.getByteArray(CCC_SUPPORTED_VERSIONS);
135         if (versions.length % 2 != 0) {
136             throw new IllegalArgumentException("Invalid supported protocol versions len "
137                     + versions.length);
138         }
139         for (int i = 0; i < versions.length; i += 2) {
140             builder.addProtocolVersion(CccProtocolVersion.fromBytes(versions, i));
141         }
142         byte[] configs = tlvs.getByteArray(CCC_SUPPORTED_UWB_CONFIGS);
143         for (int i = 0; i < configs.length; i++) {
144             builder.addUwbConfig(configs[i]);
145         }
146         byte[] pulse_shape_combos = tlvs.getByteArray(CCC_SUPPORTED_PULSE_SHAPE_COMBOS);
147         for (int i = 0; i < pulse_shape_combos.length; i++) {
148             builder.addPulseShapeCombo(CccPulseShapeCombo.fromBytes(pulse_shape_combos, i));
149         }
150         int supportedRanMultiplier;
151         try {
152             supportedRanMultiplier = tlvs.getInt(CCC_SUPPORTED_RAN_MULTIPLIER);
153         } catch (IllegalArgumentException e) {
154             Log.w(TAG, "CCC_SUPPORTED_RAN_MULTIPLIER not a 4 byte value");
155             // Try for single byte
156             supportedRanMultiplier = tlvs.getByte(CCC_SUPPORTED_RAN_MULTIPLIER);
157         }
158         builder.setRanMultiplier(supportedRanMultiplier);
159 
160         byte chapsPerslot = tlvs.getByte(CCC_SUPPORTED_CHAPS_PER_SLOT);
161         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_3)) {
162             builder.addChapsPerSlot(CHAPS_PER_SLOT_3);
163         }
164         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_4)) {
165             builder.addChapsPerSlot(CHAPS_PER_SLOT_4);
166         }
167         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_6)) {
168             builder.addChapsPerSlot(CHAPS_PER_SLOT_6);
169         }
170         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_8)) {
171             builder.addChapsPerSlot(CHAPS_PER_SLOT_8);
172         }
173         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_9)) {
174             builder.addChapsPerSlot(CHAPS_PER_SLOT_9);
175         }
176         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_12)) {
177             builder.addChapsPerSlot(CHAPS_PER_SLOT_12);
178         }
179         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_24)) {
180             builder.addChapsPerSlot(CHAPS_PER_SLOT_24);
181         }
182         if (mUwbInjector.getDeviceConfigFacade().isCccSupportedSyncCodesLittleEndian()) {
183             byte[] syncCodes = tlvs.getByteArray(CCC_SUPPORTED_SYNC_CODES);
184             for (int byteIndex = 0; byteIndex < syncCodes.length; byteIndex++) {
185                 byte syncCodeByte = syncCodes[byteIndex];
186                 for (int bitIndex = 0; bitIndex < 8; bitIndex++) {
187                     if ((syncCodeByte & (1 << bitIndex)) != 0) {
188                         int syncCodeValue = (byteIndex * 8) + bitIndex + 1;
189                         builder.addSyncCode(syncCodeValue);
190                     }
191                 }
192             }
193         } else {
194             int syncCodes = ByteBuffer.wrap(tlvs.getByteArray(CCC_SUPPORTED_SYNC_CODES)).getInt();
195             for (int i = 0; i < 32; i++) {
196                 if (isBitSet(syncCodes, 1 << i)) {
197                     builder.addSyncCode(i + 1);
198                 }
199             }
200         }
201 
202         try {
203             byte[] prioritizedChannels = tlvs.getByteArray(CCC_PRIORITIZED_CHANNEL_LIST);
204             for (byte prioritizedChannel : prioritizedChannels) {
205                 builder.addChannel(prioritizedChannel);
206             }
207         } catch (IllegalArgumentException e) {
208             Log.w(TAG, "CCC_PRIORITIZED_CHANNEL_LIST not found");
209             byte channels = tlvs.getByte(CCC_SUPPORTED_CHANNELS);
210             if (isBitSet(channels, CCC_CHANNEL_5)) {
211                 builder.addChannel(UWB_CHANNEL_5);
212             }
213             if (isBitSet(channels, CCC_CHANNEL_9)) {
214                 builder.addChannel(UWB_CHANNEL_9);
215             }
216         }
217         byte hoppingConfigModesAndSequences =
218                 tlvs.getByte(CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES);
219         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_CONFIG_MODE_NONE)) {
220             builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_NONE);
221         }
222         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_CONFIG_MODE_CONTINUOUS)) {
223             builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_CONTINUOUS);
224         }
225         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_CONFIG_MODE_ADAPTIVE)) {
226             builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_ADAPTIVE);
227         }
228         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_SEQUENCE_AES)) {
229             builder.addHoppingSequence(HOPPING_SEQUENCE_AES);
230         }
231         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_SEQUENCE_DEFAULT)) {
232             builder.addHoppingSequence(HOPPING_SEQUENCE_DEFAULT);
233         }
234 
235         try {
236             int maxRangingSessionNumber = tlvs.getInt(CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER);
237             builder.setMaxRangingSessionNumber(maxRangingSessionNumber);
238         } catch (IllegalArgumentException e) {
239             Log.w(TAG, "SUPPORTED_MAX_RANGING_SESSION_NUMBER not found");
240         }
241 
242         try {
243             int minUwbInitiationTimeMs = tlvs.getInt(CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS);
244             builder.setMinUwbInitiationTimeMs(minUwbInitiationTimeMs);
245         } catch (IllegalArgumentException e) {
246             Log.w(TAG, "SUPPORTED_MIN_UWB_INITIATION_TIME_MS not found");
247         }
248 
249         // Attempt to parse the UWBS_MAX_PPM as a short, since the CCC spec R3 defines the
250         // field Device_max_PPM field (in the TimeSync message) as a 2-octet field.
251         try {
252             short uwbsMaxPPM = tlvs.getShort(CCC_SUPPORTED_UWBS_MAX_PPM);
253             builder.setUwbsMaxPPM(uwbsMaxPPM);
254         } catch (IllegalArgumentException e) {
255             Log.w(TAG, "CCC_SUPPORTED_UWBS_MAX_PPM not found");
256         }
257 
258         return builder.build();
259     }
260 
getCccRangingStoppedParamsFromTlvBuffer(TlvDecoderBuffer tlvs)261     private CccRangingStoppedParams getCccRangingStoppedParamsFromTlvBuffer(TlvDecoderBuffer tlvs) {
262         int lastStsIndexUsed = tlvs.getInt(ConfigParam.LAST_STS_INDEX_USED);
263         return new CccRangingStoppedParams.Builder()
264                 .setLastStsIndexUsed(lastStsIndexUsed)
265                 .build();
266     }
267 }
268