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