1 /*
2  * Copyright (C) 2023 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 package com.google.uwb.support.fira;
17 
18 import android.os.PersistableBundle;
19 import android.uwb.UwbAddress;
20 
21 import androidx.annotation.Nullable;
22 
23 import java.nio.ByteBuffer;
24 import java.nio.ByteOrder;
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 /**
29  * Uwb data transfer phase configuration
30  */
31 public class FiraDataTransferPhaseConfig extends FiraParams {
32     private static final int BUNDLE_VERSION_1 = 1;
33     private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
34     private static final int SHORT_MAC_ADDRESS = 0;
35 
36     private final byte mDtpcmRepetition;
37     private final byte mDataTransferControl;
38     private final List<FiraDataTransferPhaseManagementList> mDataTransferPhaseManagementList;
39 
40     private static final String KEY_BUNDLE_VERSION = "bundle_version";
41     private static final String KEY_DTPCM_REPETITION = "dtpcm_repetition";
42     private static final String KEY_DATA_TRANSFER_CONTROL = "data_transfer_control";
43     private static final String KEY_MAC_ADDRESS_LIST = "mac_address";
44     private static final String KEY_SLOT_BITMAP = "slot_bitmap";
45 
46     @Override
getBundleVersion()47     public int getBundleVersion() {
48         return BUNDLE_VERSION_CURRENT;
49     }
50 
getDtpcmRepetition()51     public byte getDtpcmRepetition() {
52         return mDtpcmRepetition;
53     }
54 
getDataTransferControl()55     public byte getDataTransferControl() {
56         return mDataTransferControl;
57     }
58 
getDataTransferPhaseManagementList()59     public List<FiraDataTransferPhaseManagementList> getDataTransferPhaseManagementList() {
60         return mDataTransferPhaseManagementList;
61     }
62 
FiraDataTransferPhaseConfig(byte dtpcmRepetition, byte dataTransferControl, List<FiraDataTransferPhaseManagementList> dataTransferPhaseManagementList)63     private FiraDataTransferPhaseConfig(byte dtpcmRepetition, byte dataTransferControl,
64             List<FiraDataTransferPhaseManagementList> dataTransferPhaseManagementList) {
65         mDtpcmRepetition = dtpcmRepetition;
66         mDataTransferControl = dataTransferControl;
67         mDataTransferPhaseManagementList = dataTransferPhaseManagementList;
68     }
69 
70     @Override
toBundle()71     public PersistableBundle toBundle() {
72         PersistableBundle bundle = super.toBundle();
73         bundle.putInt(KEY_BUNDLE_VERSION, getBundleVersion());
74         bundle.putInt(KEY_DTPCM_REPETITION, mDtpcmRepetition);
75         bundle.putInt(KEY_DATA_TRANSFER_CONTROL, mDataTransferControl);
76 
77         long[] macAddressList = new long[mDataTransferPhaseManagementList.size()];
78         int i = 0;
79         ByteBuffer slotBitmapByteBuffer = ByteBuffer.allocate(
80                 mDataTransferPhaseManagementList.size()
81                         * (1 << ((mDataTransferControl & 0x0F) >> 1)));
82 
83         slotBitmapByteBuffer.order(ByteOrder.LITTLE_ENDIAN);
84 
85         for (FiraDataTransferPhaseManagementList dataTransferPhaseManagementList :
86                 mDataTransferPhaseManagementList) {
87             macAddressList[i++] = uwbAddressToLong(
88                 dataTransferPhaseManagementList.getUwbAddress());
89             slotBitmapByteBuffer.put(dataTransferPhaseManagementList.getSlotBitMap());
90         }
91 
92         bundle.putLongArray(KEY_MAC_ADDRESS_LIST, macAddressList);
93         bundle.putIntArray(KEY_SLOT_BITMAP, byteArrayToIntArray(slotBitmapByteBuffer.array()));
94 
95         return bundle;
96     }
97 
98     @Nullable
byteArrayToIntArray(@ullable byte[] bytes)99     protected static int[] byteArrayToIntArray(@Nullable byte[] bytes) {
100         if (bytes == null) {
101             return null;
102         }
103 
104         int[] values = new int[bytes.length];
105         for (int i = 0; i < values.length; i++) {
106             values[i] = bytes[i];
107         }
108         return values;
109     }
110 
111     @Nullable
intArrayToByteArray(@ullable int[] values)112     protected static byte[] intArrayToByteArray(@Nullable int[] values) {
113         if (values == null) {
114             return null;
115         }
116         byte[] bytes = new byte[values.length];
117         for (int i = 0; i < values.length; i++) {
118             bytes[i] = (byte) values[i];
119         }
120         return bytes;
121     }
122 
fromBundle(PersistableBundle bundle)123     public static FiraDataTransferPhaseConfig fromBundle(PersistableBundle bundle) {
124         switch (bundle.getInt(KEY_BUNDLE_VERSION)) {
125             case BUNDLE_VERSION_1:
126                 return parseVersion1(bundle);
127             default:
128                 throw new IllegalArgumentException("Invalid bundle version");
129         }
130     }
131 
parseVersion1(PersistableBundle bundle)132     private static FiraDataTransferPhaseConfig parseVersion1(PersistableBundle bundle) {
133         FiraDataTransferPhaseConfig.Builder builder = new FiraDataTransferPhaseConfig.Builder();
134 
135         builder.setDtpcmRepetition((byte) bundle.getInt(KEY_DTPCM_REPETITION));
136         byte dataTransferControl = (byte) bundle.getInt(KEY_DATA_TRANSFER_CONTROL);
137         builder.setMacAddressMode((byte) (dataTransferControl & 0x01));
138         builder.setSlotBitmapSize((byte) ((dataTransferControl & 0x0F) >> 1));
139 
140         List<FiraDataTransferPhaseManagementList> mDataTransferPhaseManagementList =
141                 new ArrayList<>();
142         List<UwbAddress> macAddressList = new ArrayList<>();
143         List<byte[]> slotBitmapList = new ArrayList<>();
144 
145         long[] macAddress = bundle.getLongArray(KEY_MAC_ADDRESS_LIST);
146         for (int i = 0; i < macAddress.length; i++) {
147             macAddressList.add(longToUwbAddress(macAddress[i],
148                     ((dataTransferControl & 0x01) == SHORT_MAC_ADDRESS)
149                     ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH
150                         : UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH));
151         }
152 
153         byte[] buffer = intArrayToByteArray(bundle.getIntArray(KEY_SLOT_BITMAP));
154         ByteBuffer slotBitmapByteBuffer = ByteBuffer.wrap(buffer);
155         int chunkBufferSize = 1 << (dataTransferControl >> 1);
156 
157         while (slotBitmapByteBuffer.hasRemaining()) {
158             byte[] data = new byte[chunkBufferSize];
159             int bytesToRead = Math.min(chunkBufferSize, slotBitmapByteBuffer.remaining());
160             slotBitmapByteBuffer.get(data, 0, bytesToRead);
161             slotBitmapList.add(data);
162         }
163 
164         for (int i = 0; i < macAddressList.size(); i++) {
165             mDataTransferPhaseManagementList.add(new FiraDataTransferPhaseManagementList(
166                     macAddressList.get(i), slotBitmapList.get(i)));
167         }
168 
169         builder.setDataTransferPhaseManagementList(mDataTransferPhaseManagementList);
170 
171         return builder.build();
172     }
173 
174     /** Defines parameters for data transfer phase management list */
175     public static class FiraDataTransferPhaseManagementList {
176         private final UwbAddress mUwbAddress;
177         private final byte[] mSlotBitMap;
178 
FiraDataTransferPhaseManagementList(UwbAddress uwbAddress, byte[] slotBitmap)179         public FiraDataTransferPhaseManagementList(UwbAddress uwbAddress, byte[] slotBitmap) {
180             mUwbAddress = uwbAddress;
181             mSlotBitMap = slotBitmap;
182         }
183 
getUwbAddress()184         public UwbAddress getUwbAddress() {
185             return mUwbAddress;
186         }
187 
getSlotBitMap()188         public byte[] getSlotBitMap() {
189             return mSlotBitMap;
190         }
191     }
192 
193     /** Builder */
194     public static class Builder {
195         private byte mDtpcmRepetition;
196         private byte mMacAddressMode;
197         private byte mSlotBitMapSize;
198         private List<FiraDataTransferPhaseManagementList> mDataTransferPhaseManagementList =
199                 new ArrayList<>();
200 
setDtpcmRepetition(byte dtpcmRepetition)201         public FiraDataTransferPhaseConfig.Builder setDtpcmRepetition(byte dtpcmRepetition) {
202             mDtpcmRepetition = dtpcmRepetition;
203             return this;
204         }
205 
setMacAddressMode(byte macAddressMode)206         public FiraDataTransferPhaseConfig.Builder setMacAddressMode(byte macAddressMode) {
207             mMacAddressMode = macAddressMode;
208             return this;
209         }
210 
setSlotBitmapSize(byte slotBitmapSize)211         public FiraDataTransferPhaseConfig.Builder setSlotBitmapSize(byte slotBitmapSize) {
212             mSlotBitMapSize = slotBitmapSize;
213             return this;
214         }
215 
setDataTransferPhaseManagementList( List<FiraDataTransferPhaseManagementList> dataTransferPhaseManagementList)216         public FiraDataTransferPhaseConfig.Builder setDataTransferPhaseManagementList(
217                 List<FiraDataTransferPhaseManagementList> dataTransferPhaseManagementList) {
218             mDataTransferPhaseManagementList = dataTransferPhaseManagementList;
219             return this;
220         }
221 
build()222         public FiraDataTransferPhaseConfig build() {
223             return new FiraDataTransferPhaseConfig(
224                 mDtpcmRepetition,
225                 (byte) ((mSlotBitMapSize << 1) | (mMacAddressMode & 0x01)),
226                 mDataTransferPhaseManagementList);
227         }
228     }
229 }
230