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.android.server.uwb.params; 18 19 import android.annotation.Nullable; 20 import android.util.ArrayMap; 21 import android.util.Log; 22 23 import com.android.internal.annotations.VisibleForTesting; 24 import com.android.server.uwb.config.ConfigParam; 25 import com.android.server.uwb.util.UwbUtil; 26 27 import java.nio.BufferUnderflowException; 28 import java.nio.ByteBuffer; 29 import java.nio.ByteOrder; 30 import java.util.Arrays; 31 import java.util.Collection; 32 import java.util.Map; 33 import java.util.Objects; 34 35 /*** 36 * This assumes little endian data and 1 byte tags. This is intended for handling UCI interface 37 * data. 38 * @see com.android.server.uwb.secure.iso7816.TlvParser 39 */ 40 public class TlvDecoderBuffer { 41 private static final String TAG = "TlvDecoderBuffer"; 42 private final ByteBuffer mBuffer; 43 private final int mNumParams; 44 private final Map<Byte, Tlv> mTlvs = new ArrayMap<>(); 45 46 @VisibleForTesting 47 public static class Tlv { 48 public final byte tagType; 49 public final byte length; 50 public final byte[] value; 51 52 @Override equals(Object o)53 public boolean equals(Object o) { 54 if (this == o) return true; 55 if (!(o instanceof Tlv)) return false; 56 Tlv tlv = (Tlv) o; 57 return tagType == tlv.tagType && length == tlv.length && Arrays.equals(value, 58 tlv.value); 59 } 60 61 @Override hashCode()62 public int hashCode() { 63 int result = Objects.hash(tagType, length); 64 result = 31 * result + Arrays.hashCode(value); 65 return result; 66 } 67 Tlv(byte tagType, byte length, byte[] value)68 Tlv(byte tagType, byte length, byte[] value) { 69 this.tagType = tagType; 70 this.length = length; 71 this.value = value; 72 } 73 74 @Override toString()75 public String toString() { 76 return "Tlv[tagType: " + tagType + ", length: " + length + ", value: " 77 + UwbUtil.toHexString(value) + "]"; 78 } 79 } 80 TlvDecoderBuffer(byte[] tlvArray, int noOfParams)81 public TlvDecoderBuffer(byte[] tlvArray, int noOfParams) { 82 mBuffer = ByteBuffer.wrap(tlvArray); 83 mNumParams = noOfParams; 84 } 85 86 @VisibleForTesting getByteArray()87 public byte[] getByteArray() { 88 return mBuffer.array(); 89 } 90 91 @VisibleForTesting getNumParams()92 public int getNumParams() { 93 return mNumParams; 94 } 95 96 @VisibleForTesting getTlvs()97 public Collection<Tlv> getTlvs() { 98 return mTlvs.values(); 99 } 100 parse()101 public boolean parse() { 102 if (mBuffer.capacity() == 0) return false; 103 while (mBuffer.hasRemaining()) { 104 try { 105 byte tagType = mBuffer.get(); 106 byte length = mBuffer.get(); 107 byte[] value = new byte[length]; 108 mBuffer.get(value); 109 Log.i(TAG, "Parsed TLV: " + new Tlv(tagType, length, value)); 110 mTlvs.put(tagType, new Tlv(tagType, length, value)); 111 } catch (BufferUnderflowException e) { 112 Log.e(TAG, "Failed to parse buffer at position: " + mBuffer.position(), e); 113 return false; 114 } 115 } 116 if (mNumParams != mTlvs.size()) { 117 Log.e(TAG, "Num TLVs parsed does not equal the num params, tlvs: " + mTlvs.size() 118 + ", num params: " + mNumParams); 119 return false; 120 } 121 return true; 122 } 123 124 @Nullable getTlv(int tagType)125 private Tlv getTlv(int tagType) { 126 byte[] tagTypeByte = ConfigParam.getTagBytes(tagType); 127 if (tagTypeByte.length > 1) { 128 throw new IllegalArgumentException("Invalid tagType: " + Arrays.toString(tagTypeByte)); 129 } 130 Tlv tlv = mTlvs.get(tagTypeByte[0]); 131 if (tlv == null) { 132 throw new IllegalArgumentException("Tag type: " + tagType + " not present"); 133 } 134 return tlv; 135 } 136 getByte(int tagType)137 public Byte getByte(int tagType) { 138 Tlv tlv = getTlv(tagType); 139 if (tlv.length != Byte.BYTES) { 140 throw new IllegalArgumentException( 141 "Mismatch in value type, expected byte found len: " + tlv.length); 142 } 143 try { 144 return ByteBuffer.wrap(tlv.value).get(); 145 } catch (BufferUnderflowException e) { 146 throw new IllegalArgumentException(e); 147 } 148 } 149 getShort(int tagType)150 public Short getShort(int tagType) { 151 Tlv tlv = getTlv(tagType); 152 if (tlv.length != Short.BYTES) { 153 throw new IllegalArgumentException( 154 "Mismatch in value type, expected short found len: " + tlv.length); 155 } 156 try { 157 return ByteBuffer.wrap(tlv.value).order(ByteOrder.LITTLE_ENDIAN).getShort(); 158 } catch (BufferUnderflowException e) { 159 throw new IllegalArgumentException(e); 160 } 161 } 162 getInt(int tagType)163 public Integer getInt(int tagType) { 164 Tlv tlv = getTlv(tagType); 165 if (tlv.length != Integer.BYTES) { 166 throw new IllegalArgumentException( 167 "Mismatch in value type, expected int found len: " + tlv.length); 168 } 169 try { 170 return ByteBuffer.wrap(tlv.value).order(ByteOrder.LITTLE_ENDIAN).getInt(); 171 } catch (BufferUnderflowException e) { 172 throw new IllegalArgumentException(e); 173 } 174 } 175 getLong(int tagType)176 public Long getLong(int tagType) { 177 Tlv tlv = getTlv(tagType); 178 if (tlv.length != Long.BYTES) { 179 throw new IllegalArgumentException( 180 "Mismatch in value long, expected int found len: " + tlv.length); 181 } 182 try { 183 return ByteBuffer.wrap(tlv.value).order(ByteOrder.LITTLE_ENDIAN).getLong(); 184 } catch (BufferUnderflowException e) { 185 throw new IllegalArgumentException(e); 186 } 187 } 188 getByteArray(int tagType)189 public byte[] getByteArray(int tagType) { 190 Tlv tlv = getTlv(tagType); 191 byte[] value = new byte[tlv.length]; 192 try { 193 ByteBuffer.wrap(tlv.value).get(value); 194 return value; 195 } catch (BufferUnderflowException e) { 196 throw new IllegalArgumentException(e); 197 } 198 } 199 } 200