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.google.uwb.support.fira; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.os.PersistableBundle; 24 import android.uwb.RangingSession; 25 import android.uwb.UwbAddress; 26 27 import androidx.annotation.Nullable; 28 29 /** 30 * UWB parameters used to add/remove controlees for a FiRa session 31 * 32 * <p>This is passed as a bundle to the service API {@link RangingSession#addControlee} and 33 * {@link RangingSession#removeControlee}. 34 */ 35 public class FiraControleeParams extends FiraParams { 36 private static final int BUNDLE_VERSION_1 = 1; 37 private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1; 38 39 @MulticastListUpdateAction private final int mAction; 40 @Nullable private final UwbAddress[] mAddressList; 41 @Nullable private final int[] mSubSessionIdList; 42 @Nullable private final byte[] mSubSessionKeyList; 43 private static final String KEY_ACTION = "action"; 44 private static final String KEY_MAC_ADDRESS_MODE = "mac_address_mode"; 45 private static final String KEY_ADDRESS_LIST = "address_list"; 46 private static final String KEY_SUB_SESSION_ID_LIST = "sub_session_id_list"; 47 private static final String KEY_SUB_SESSION_KEY_LIST = "sub_session_key_list"; 48 FiraControleeParams( @ulticastListUpdateAction int action, @Nullable UwbAddress[] addressList, @Nullable int[] subSessionIdList, @Nullable byte[] subSessionKeyList)49 private FiraControleeParams( 50 @MulticastListUpdateAction int action, 51 @Nullable UwbAddress[] addressList, 52 @Nullable int[] subSessionIdList, 53 @Nullable byte[] subSessionKeyList) { 54 mAction = action; 55 mAddressList = addressList; 56 mSubSessionIdList = subSessionIdList; 57 mSubSessionKeyList = subSessionKeyList; 58 } 59 60 @Override getBundleVersion()61 protected int getBundleVersion() { 62 return BUNDLE_VERSION_CURRENT; 63 } 64 65 @MulticastListUpdateAction getAction()66 public int getAction() { 67 return mAction; 68 } 69 70 @Nullable getAddressList()71 public UwbAddress[] getAddressList() { 72 return mAddressList; 73 } 74 75 @Nullable getSubSessionIdList()76 public int[] getSubSessionIdList() { 77 return mSubSessionIdList; 78 } 79 80 /** 81 * TODO(b/232453347): Needs to accept an array of arrays for supporting multiple controlees. 82 */ 83 @Nullable getSubSessionKeyList()84 public byte[] getSubSessionKeyList() { 85 return mSubSessionKeyList; 86 } 87 88 @Nullable byteArrayToIntArray(@ullable byte[] bytes)89 private static int[] byteArrayToIntArray(@Nullable byte[] bytes) { 90 if (bytes == null) { 91 return null; 92 } 93 94 int[] values = new int[bytes.length]; 95 for (int i = 0; i < values.length; i++) { 96 values[i] = bytes[i]; 97 } 98 return values; 99 } 100 101 @Nullable intArrayToByteArray(@ullable int[] values)102 private static byte[] intArrayToByteArray(@Nullable int[] values) { 103 if (values == null) { 104 return null; 105 } 106 byte[] bytes = new byte[values.length]; 107 for (int i = 0; i < values.length; i++) { 108 bytes[i] = (byte) values[i]; 109 } 110 return bytes; 111 } 112 113 @Override toBundle()114 public PersistableBundle toBundle() { 115 PersistableBundle bundle = super.toBundle(); 116 requireNonNull(mAddressList); 117 bundle.putInt(KEY_ACTION, mAction); 118 119 long[] addressList = new long[mAddressList.length]; 120 int i = 0; 121 for (UwbAddress address : mAddressList) { 122 addressList[i++] = uwbAddressToLong(address); 123 } 124 int macAddressMode = MAC_ADDRESS_MODE_2_BYTES; 125 if (mAddressList[0].size() == UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH) { 126 macAddressMode = MAC_ADDRESS_MODE_8_BYTES; 127 } 128 bundle.putInt(KEY_MAC_ADDRESS_MODE, macAddressMode); 129 bundle.putLongArray(KEY_ADDRESS_LIST, addressList); 130 bundle.putIntArray(KEY_SUB_SESSION_ID_LIST, mSubSessionIdList); 131 bundle.putIntArray(KEY_SUB_SESSION_KEY_LIST, byteArrayToIntArray(mSubSessionKeyList)); 132 return bundle; 133 } 134 fromBundle(PersistableBundle bundle)135 public static FiraControleeParams fromBundle(PersistableBundle bundle) { 136 if (!isCorrectProtocol(bundle)) { 137 throw new IllegalArgumentException("Invalid protocol"); 138 } 139 140 switch (getBundleVersion(bundle)) { 141 case BUNDLE_VERSION_1: 142 return parseVersion1(bundle); 143 144 default: 145 throw new IllegalArgumentException("Invalid bundle version"); 146 } 147 } 148 parseVersion1(PersistableBundle bundle)149 private static FiraControleeParams parseVersion1(PersistableBundle bundle) { 150 FiraControleeParams.Builder builder = new FiraControleeParams.Builder(); 151 int action = bundle.getInt(KEY_ACTION, MULTICAST_LIST_UPDATE_ACTION_ADD); 152 int macAddressMode = bundle.getInt(KEY_MAC_ADDRESS_MODE); 153 int addressByteLength = UwbAddress.SHORT_ADDRESS_BYTE_LENGTH; 154 if (macAddressMode == MAC_ADDRESS_MODE_8_BYTES) { 155 addressByteLength = UwbAddress.EXTENDED_ADDRESS_BYTE_LENGTH; 156 } 157 long[] addresses = bundle.getLongArray(KEY_ADDRESS_LIST); 158 UwbAddress[] addressList = new UwbAddress[addresses.length]; 159 for (int i = 0; i < addresses.length; i++) { 160 addressList[i] = longToUwbAddress(addresses[i], addressByteLength); 161 } 162 builder.setAction(action); 163 builder.setAddressList(addressList); 164 builder.setSubSessionIdList(bundle.getIntArray(KEY_SUB_SESSION_ID_LIST)); 165 builder.setSubSessionKeyList( 166 intArrayToByteArray(bundle.getIntArray(KEY_SUB_SESSION_KEY_LIST))); 167 return builder.build(); 168 } 169 170 /** Builder */ 171 public static class Builder { 172 private int mAction = MULTICAST_LIST_UPDATE_ACTION_ADD; 173 @Nullable private UwbAddress[] mAddressList = null; 174 @Nullable private int[] mSubSessionIdList = null; 175 @Nullable private byte[] mSubSessionKeyList = null; 176 setAction(@ulticastListUpdateAction int action)177 public FiraControleeParams.Builder setAction(@MulticastListUpdateAction int action) { 178 mAction = action; 179 return this; 180 } 181 setAddressList(UwbAddress[] addressList)182 public FiraControleeParams.Builder setAddressList(UwbAddress[] addressList) { 183 mAddressList = addressList; 184 return this; 185 } 186 setSubSessionIdList(int[] subSessionIdList)187 public FiraControleeParams.Builder setSubSessionIdList(int[] subSessionIdList) { 188 mSubSessionIdList = subSessionIdList; 189 return this; 190 } 191 192 /** Sub Session Key List setter. This is a 2D array of keys represented as 1D array */ setSubSessionKeyList(byte[] subSessionKeyList)193 public FiraControleeParams.Builder setSubSessionKeyList(byte[] subSessionKeyList) { 194 mSubSessionKeyList = subSessionKeyList; 195 return this; 196 } 197 checkAddressList()198 private void checkAddressList() { 199 checkArgument(mAddressList != null && mAddressList.length > 0); 200 for (UwbAddress uwbAddress : mAddressList) { 201 requireNonNull(uwbAddress); 202 checkArgument(uwbAddress.size() == UwbAddress.SHORT_ADDRESS_BYTE_LENGTH); 203 } 204 205 checkArgument( 206 mSubSessionIdList == null 207 || mSubSessionIdList.length == mAddressList.length); 208 209 // SubSessionKey may not always be present as it can be read from secure component 210 // also. 211 if (mSubSessionKeyList != null) { 212 if (mAction == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_16_BYTE) { 213 checkArgument( 214 mSubSessionKeyList.length == 16 * mSubSessionIdList.length); 215 } else if (mAction == P_STS_MULTICAST_LIST_UPDATE_ACTION_ADD_32_BYTE) { 216 checkArgument( 217 mSubSessionKeyList.length == 32 * mSubSessionIdList.length); 218 } 219 } 220 } 221 build()222 public FiraControleeParams build() { 223 checkAddressList(); 224 return new FiraControleeParams( 225 mAction, 226 mAddressList, 227 mSubSessionIdList, 228 mSubSessionKeyList); 229 } 230 } 231 } 232