• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.IntRange;
22 import androidx.annotation.Nullable;
23 
24 import java.nio.ByteBuffer;
25 import java.nio.ByteOrder;
26 import java.util.ArrayList;
27 import java.util.List;
28 
29 /**
30  * Uwb Hybrid session controller configuration
31  */
32 public class FiraHybridSessionControllerConfig extends FiraParams {
33     private static final int BUNDLE_VERSION_1 = 1;
34     private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
35 
36     private static final int SHORT_MAC_ADDRESS = 0;
37     private static final int EXTENDED_MAC_ADDRESS = 1;
38     private static final int PHASE_LIST_SIZE = 20;
39 
40     private final int mNumberOfPhases;
41     private final List<FiraHybridSessionPhaseList> mPhaseList;
42 
43     public static final String KEY_BUNDLE_VERSION = "bundle_version";
44     public static final String KEY_NUMBER_OF_PHASES = "number_of_phases";
45     public static final String KEY_MESSAGE_CONTROL = "message_control";
46     public static final String KEY_UPDATE_TIME = "update_time";
47     public static final String KEY_PHASE_LIST = "phase_list";
48 
49     @Override
getBundleVersion()50     public int getBundleVersion() {
51         return BUNDLE_VERSION_CURRENT;
52     }
53 
getNumberOfPhases()54     public int getNumberOfPhases() {
55         return mNumberOfPhases;
56     }
57 
getPhaseList()58     public List<FiraHybridSessionPhaseList> getPhaseList() {
59         return mPhaseList;
60     }
61 
FiraHybridSessionControllerConfig(int numberOfPhases, List<FiraHybridSessionPhaseList> phaseList)62     private FiraHybridSessionControllerConfig(int numberOfPhases,
63             List<FiraHybridSessionPhaseList> phaseList) {
64         mNumberOfPhases = numberOfPhases;
65         mPhaseList = phaseList;
66     }
67 
68     //TODO, move these utility methods to helper class
69     @Nullable
byteArrayToIntArray(@ullable byte[] bytes)70     private static int[] byteArrayToIntArray(@Nullable byte[] bytes) {
71         if (bytes == null) {
72             return null;
73         }
74 
75         int[] values = new int[bytes.length];
76         for (int i = 0; i < values.length; i++) {
77             values[i] = bytes[i];
78         }
79         return values;
80     }
81 
82     @Nullable
intArrayToByteArray(@ullable int[] values)83     private static byte[] intArrayToByteArray(@Nullable int[] values) {
84         if (values == null) {
85             return null;
86         }
87         byte[] bytes = new byte[values.length];
88         for (int i = 0; i < values.length; i++) {
89             bytes[i] = (byte) values[i];
90         }
91         return bytes;
92     }
93 
toBundle()94     public PersistableBundle toBundle() {
95         PersistableBundle bundle = super.toBundle();
96         bundle.putInt(KEY_BUNDLE_VERSION, getBundleVersion());
97         bundle.putInt(KEY_NUMBER_OF_PHASES, mNumberOfPhases);
98 
99         ByteBuffer buffer = ByteBuffer.allocate(mNumberOfPhases * PHASE_LIST_SIZE);
100         buffer.order(ByteOrder.LITTLE_ENDIAN);
101         for (FiraHybridSessionPhaseList phaseList : mPhaseList) {
102             buffer.putInt(phaseList.getSessionId());
103             buffer.putShort(phaseList.getStartSlotIndex());
104             buffer.putShort(phaseList.getEndSlotIndex());
105             buffer.putInt(phaseList.getMessageControl());
106             buffer.putLong(uwbAddressToLong(phaseList.getMacAddress()));
107         }
108 
109         bundle.putIntArray(KEY_PHASE_LIST, byteArrayToIntArray(buffer.array()));
110         return bundle;
111     }
112 
fromBundle(PersistableBundle bundle)113     public static FiraHybridSessionControllerConfig fromBundle(PersistableBundle bundle) {
114         switch (bundle.getInt(KEY_BUNDLE_VERSION)) {
115             case BUNDLE_VERSION_1:
116                 return parseVersion1(bundle);
117             default:
118                 throw new IllegalArgumentException("Invalid bundle version");
119         }
120     }
121 
parseVersion1(PersistableBundle bundle)122     private static FiraHybridSessionControllerConfig parseVersion1(PersistableBundle bundle) {
123         FiraHybridSessionControllerConfig.Builder builder =
124                 new FiraHybridSessionControllerConfig.Builder();
125 
126         int numberOfPhases = bundle.getInt(KEY_NUMBER_OF_PHASES);
127         builder.setNumberOfPhases(numberOfPhases);
128 
129         byte[] phaseByteArray = intArrayToByteArray(bundle.getIntArray(KEY_PHASE_LIST));
130         ByteBuffer buffer = ByteBuffer.wrap(phaseByteArray);
131         buffer.order(ByteOrder.LITTLE_ENDIAN);
132 
133         for (int i = 0; i < numberOfPhases; i++) {
134             final int sessionId = buffer.getInt();
135             final short startSlotIndex = buffer.getShort();
136             final short endSlotIndex = buffer.getShort();
137             final byte messageControl = (byte) buffer.getInt();
138             final int addressLength =  ((messageControl & 0x01) == SHORT_MAC_ADDRESS)
139                     ? UwbAddress.SHORT_ADDRESS_BYTE_LENGTH
140                     : UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH;
141 
142             FiraHybridSessionPhaseList mFiraHybridSessionPhaseList = new FiraHybridSessionPhaseList(
143                     sessionId,
144                     startSlotIndex,
145                     endSlotIndex,
146                     messageControl,
147                     longToUwbAddress(buffer.getLong(), addressLength)
148             );
149 
150             builder.addPhaseList(mFiraHybridSessionPhaseList);
151         }
152         return builder.build();
153     }
154 
155     /** Builder */
156     public static class Builder {
157         private int mNumberOfPhases;
158         private final List<FiraHybridSessionPhaseList> mPhaseList = new ArrayList<>();
159 
setNumberOfPhases(int numberOfPhases)160         public FiraHybridSessionControllerConfig.Builder setNumberOfPhases(int numberOfPhases) {
161             mNumberOfPhases = numberOfPhases;
162             return this;
163         }
164 
addPhaseList( FiraHybridSessionPhaseList phaseList)165         public FiraHybridSessionControllerConfig.Builder addPhaseList(
166                 FiraHybridSessionPhaseList phaseList) {
167             mPhaseList.add(phaseList);
168             return this;
169         }
170 
build()171         public FiraHybridSessionControllerConfig build() {
172             if (mPhaseList.size() == 0) {
173                 throw new IllegalStateException("No hybrid session phase list have been set");
174             }
175             return new FiraHybridSessionControllerConfig(
176                     mNumberOfPhases,
177                     mPhaseList);
178         }
179     }
180 
181     /** Defines parameters for hybrid session's secondary phase list */
182     public static class FiraHybridSessionPhaseList {
183         private final int mSessionId;
184 
185         @IntRange(from = 1, to = 32767)
186         private final short mStartSlotIndex;
187 
188         @IntRange(from = 1, to = 32767)
189         private final short mEndSlotIndex;
190         private final byte mMessageControl;
191         private final UwbAddress mMacAddress;
192 
FiraHybridSessionPhaseList(int sessionId, @IntRange(from = 1, to = 32767) short startSlotIndex, @IntRange(from = 1, to = 32767) short endSlotIndex, byte messageControl, UwbAddress macAddress)193         public FiraHybridSessionPhaseList(int sessionId,
194                 @IntRange(from = 1, to = 32767) short startSlotIndex,
195                 @IntRange(from = 1, to = 32767) short endSlotIndex,
196                 byte messageControl,
197                 UwbAddress macAddress) {
198             mSessionId = sessionId;
199             mStartSlotIndex = startSlotIndex;
200             mEndSlotIndex = endSlotIndex;
201             mMessageControl = messageControl;
202             mMacAddress = macAddress;
203         }
204 
getSessionId()205         public int getSessionId() {
206             return mSessionId;
207         }
208 
209         @IntRange(from = 1, to = 32767)
getStartSlotIndex()210         public short getStartSlotIndex() {
211             return mStartSlotIndex;
212         }
213 
214         @IntRange(from = 1, to = 32767)
getEndSlotIndex()215         public short getEndSlotIndex() {
216             return mEndSlotIndex;
217         }
218 
getMessageControl()219         public byte getMessageControl() {
220             return mMessageControl;
221         }
222 
getMacAddress()223         public UwbAddress getMacAddress() {
224             return mMacAddress;
225         }
226     }
227 }
228