1 /*
2  * Copyright (C) 2024 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.aliro.AliroParams.CHAPS_PER_SLOT_12;
47 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_24;
48 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_3;
49 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_4;
50 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_6;
51 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_8;
52 import static com.google.uwb.support.aliro.AliroParams.CHAPS_PER_SLOT_9;
53 import static com.google.uwb.support.aliro.AliroParams.HOPPING_CONFIG_MODE_ADAPTIVE;
54 import static com.google.uwb.support.aliro.AliroParams.HOPPING_CONFIG_MODE_CONTINUOUS;
55 import static com.google.uwb.support.aliro.AliroParams.HOPPING_CONFIG_MODE_NONE;
56 import static com.google.uwb.support.aliro.AliroParams.HOPPING_SEQUENCE_AES;
57 import static com.google.uwb.support.aliro.AliroParams.HOPPING_SEQUENCE_DEFAULT;
58 import static com.google.uwb.support.aliro.AliroParams.UWB_CHANNEL_5;
59 import static com.google.uwb.support.aliro.AliroParams.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.aliro.AliroProtocolVersion;
67 import com.google.uwb.support.aliro.AliroPulseShapeCombo;
68 import com.google.uwb.support.aliro.AliroRangingStartedParams;
69 import com.google.uwb.support.aliro.AliroRangingStoppedParams;
70 import com.google.uwb.support.aliro.AliroSpecificationParams;
71 import com.google.uwb.support.base.Params;
72 import com.google.uwb.support.base.ProtocolVersion;
73 
74 import java.nio.ByteBuffer;
75 import java.nio.ByteOrder;
76 
77 /**
78  * Aliro decoder - this started out as a copy of the CCC decoder.
79  */
80 public class AliroDecoder extends TlvDecoder {
81     private static final String TAG = "AliroDecoder";
82     private final UwbInjector mUwbInjector;
83 
AliroDecoder(UwbInjector uwbInjector)84     public AliroDecoder(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 (AliroRangingStartedParams.class.equals(paramsType)) {
93             return (T) getAliroRangingStartedParamsFromTlvBuffer(tlvs);
94         }
95         if (AliroSpecificationParams.class.equals(paramsType)) {
96             return (T) getAliroSpecificationParamsFromTlvBuffer(tlvs);
97         }
98         if (AliroRangingStoppedParams.class.equals(paramsType)) {
99             return (T) getAliroRangingStoppedParamsFromTlvBuffer(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 
getAliroRangingStartedParamsFromTlvBuffer( TlvDecoderBuffer tlvs)108     private AliroRangingStartedParams getAliroRangingStartedParamsFromTlvBuffer(
109             TlvDecoderBuffer tlvs) {
110         byte[] hopModeKey = tlvs.getByteArray(ConfigParam.HOP_MODE_KEY);
111         int hopModeKeyInt = ByteBuffer.wrap(hopModeKey).order(ByteOrder.LITTLE_ENDIAN).getInt();
112         long uwbTime0;
113         // Backwards compatibility with vendors who were using Google defined
114         // UWB_TIME0 TLV param.
115         try {
116             uwbTime0 = tlvs.getLong(ConfigParam.UWB_TIME0);
117         } catch (IllegalArgumentException e) {
118             uwbTime0 = tlvs.getLong(ConfigParam.UWB_INITIATION_TIME);
119         }
120 
121         return new AliroRangingStartedParams.Builder()
122                 // STS_Index0  0 - 0x3FFFFFFFF
123                 .setStartingStsIndex(tlvs.getInt(ConfigParam.STS_INDEX))
124                 .setHopModeKey(hopModeKeyInt)
125                 //  UWB_Time0 0 - 0xFFFFFFFFFFFFFFFF  UWB_INITIATION_TIME
126                 .setUwbTime0(uwbTime0)
127                 // RANGING_INTERVAL = RAN_Multiplier * 96
128                 .setRanMultiplier(tlvs.getInt(ConfigParam.RANGING_INTERVAL) / 96)
129                 .setSyncCodeIndex(tlvs.getByte(ConfigParam.PREAMBLE_CODE_INDEX))
130                 .build();
131     }
132 
getAliroSpecificationParamsFromTlvBuffer( TlvDecoderBuffer tlvs)133     private AliroSpecificationParams getAliroSpecificationParamsFromTlvBuffer(
134             TlvDecoderBuffer tlvs) {
135         AliroSpecificationParams.Builder builder = new AliroSpecificationParams.Builder();
136         byte[] versions = tlvs.getByteArray(CCC_SUPPORTED_VERSIONS);
137         if (versions.length % 2 != 0) {
138             throw new IllegalArgumentException("Invalid supported protocol versions len "
139                     + versions.length);
140         }
141         for (int i = 0; i < versions.length; i += 2) {
142             builder.addProtocolVersion(AliroProtocolVersion.fromBytes(versions, i));
143         }
144         byte[] configs = tlvs.getByteArray(CCC_SUPPORTED_UWB_CONFIGS);
145         for (int i = 0; i < configs.length; i++) {
146             builder.addUwbConfig(configs[i]);
147         }
148         byte[] pulse_shape_combos = tlvs.getByteArray(CCC_SUPPORTED_PULSE_SHAPE_COMBOS);
149         for (int i = 0; i < pulse_shape_combos.length; i++) {
150             builder.addPulseShapeCombo(AliroPulseShapeCombo.fromBytes(pulse_shape_combos, i));
151         }
152         builder.setRanMultiplier(tlvs.getInt(CCC_SUPPORTED_RAN_MULTIPLIER));
153         byte chapsPerslot = tlvs.getByte(CCC_SUPPORTED_CHAPS_PER_SLOT);
154         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_3)) {
155             builder.addChapsPerSlot(CHAPS_PER_SLOT_3);
156         }
157         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_4)) {
158             builder.addChapsPerSlot(CHAPS_PER_SLOT_4);
159         }
160         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_6)) {
161             builder.addChapsPerSlot(CHAPS_PER_SLOT_6);
162         }
163         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_8)) {
164             builder.addChapsPerSlot(CHAPS_PER_SLOT_8);
165         }
166         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_9)) {
167             builder.addChapsPerSlot(CHAPS_PER_SLOT_9);
168         }
169         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_12)) {
170             builder.addChapsPerSlot(CHAPS_PER_SLOT_12);
171         }
172         if (isBitSet(chapsPerslot, CCC_CHAPS_PER_SLOT_24)) {
173             builder.addChapsPerSlot(CHAPS_PER_SLOT_24);
174         }
175         // TODO(b/321757248): Consider replacing with an Aliro flag.
176         if (mUwbInjector.getDeviceConfigFacade().isCccSupportedSyncCodesLittleEndian()) {
177             byte[] syncCodes = tlvs.getByteArray(CCC_SUPPORTED_SYNC_CODES);
178             for (int byteIndex = 0; byteIndex < syncCodes.length; byteIndex++) {
179                 byte syncCodeByte = syncCodes[byteIndex];
180                 for (int bitIndex = 0; bitIndex < 8; bitIndex++) {
181                     if ((syncCodeByte & (1 << bitIndex)) != 0) {
182                         int syncCodeValue = (byteIndex * 8) + bitIndex + 1;
183                         builder.addSyncCode(syncCodeValue);
184                     }
185                 }
186             }
187         } else {
188             int syncCodes = ByteBuffer.wrap(tlvs.getByteArray(CCC_SUPPORTED_SYNC_CODES)).getInt();
189             for (int i = 0; i < 32; i++) {
190                 if (isBitSet(syncCodes, 1 << i)) {
191                     builder.addSyncCode(i + 1);
192                 }
193             }
194         }
195 
196         try {
197             byte[] prioritizedChannels = tlvs.getByteArray(CCC_PRIORITIZED_CHANNEL_LIST);
198             for (byte prioritizedChannel : prioritizedChannels) {
199                 builder.addChannel(prioritizedChannel);
200             }
201         } catch (IllegalArgumentException e) {
202             Log.w(TAG, "CCC_PRIORITIZED_CHANNEL_LIST not found");
203             byte channels = tlvs.getByte(CCC_SUPPORTED_CHANNELS);
204             if (isBitSet(channels, CCC_CHANNEL_5)) {
205                 builder.addChannel(UWB_CHANNEL_5);
206             }
207             if (isBitSet(channels, CCC_CHANNEL_9)) {
208                 builder.addChannel(UWB_CHANNEL_9);
209             }
210         }
211         byte hoppingConfigModesAndSequences =
212                 tlvs.getByte(CCC_SUPPORTED_HOPPING_CONFIG_MODES_AND_SEQUENCES);
213         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_CONFIG_MODE_NONE)) {
214             builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_NONE);
215         }
216         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_CONFIG_MODE_CONTINUOUS)) {
217             builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_CONTINUOUS);
218         }
219         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_CONFIG_MODE_ADAPTIVE)) {
220             builder.addHoppingConfigMode(HOPPING_CONFIG_MODE_ADAPTIVE);
221         }
222         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_SEQUENCE_AES)) {
223             builder.addHoppingSequence(HOPPING_SEQUENCE_AES);
224         }
225         if (isBitSet(hoppingConfigModesAndSequences, CCC_HOPPING_SEQUENCE_DEFAULT)) {
226             builder.addHoppingSequence(HOPPING_SEQUENCE_DEFAULT);
227         }
228 
229         try {
230             int maxRangingSessionNumber = tlvs.getInt(CCC_SUPPORTED_MAX_RANGING_SESSION_NUMBER);
231             builder.setMaxRangingSessionNumber(maxRangingSessionNumber);
232         } catch (IllegalArgumentException e) {
233             Log.w(TAG, "SUPPORTED_MAX_RANGING_SESSION_NUMBER not found");
234         }
235 
236         try {
237             int minUwbInitiationTimeMs = tlvs.getInt(CCC_SUPPORTED_MIN_UWB_INITIATION_TIME_MS);
238             builder.setMinUwbInitiationTimeMs(minUwbInitiationTimeMs);
239         } catch (IllegalArgumentException e) {
240             Log.w(TAG, "SUPPORTED_MIN_UWB_INITIATION_TIME_MS not found");
241         }
242 
243         // Attempt to parse the UWBS_MAX_PPM as a short, since the CCC spec R3 defines the
244         // field Device_max_PPM field (in the TimeSync message) as a 2-octet field.
245         try {
246             short uwbsMaxPPM = tlvs.getShort(CCC_SUPPORTED_UWBS_MAX_PPM);
247             builder.setUwbsMaxPPM(uwbsMaxPPM);
248         } catch (IllegalArgumentException e) {
249             Log.w(TAG, "CCC_SUPPORTED_UWBS_MAX_PPM not found");
250         }
251 
252         return builder.build();
253     }
254 
getAliroRangingStoppedParamsFromTlvBuffer( TlvDecoderBuffer tlvs)255     private AliroRangingStoppedParams getAliroRangingStoppedParamsFromTlvBuffer(
256             TlvDecoderBuffer tlvs) {
257         int lastStsIndexUsed = tlvs.getInt(ConfigParam.LAST_STS_INDEX_USED);
258         return new AliroRangingStoppedParams.Builder()
259                 .setLastStsIndexUsed(lastStsIndexUsed)
260                 .build();
261     }
262 }
263