1 /* 2 * Copyright (C) 2022 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.discovery.info; 18 19 import android.annotation.IntRange; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.util.Log; 23 24 import com.android.server.uwb.util.ArrayUtils; 25 import com.android.server.uwb.util.DataTypeConversionUtil; 26 27 import java.util.HashMap; 28 import java.util.Map; 29 30 /** 31 * Holds data of the FiRa OOB administrative error message according to FiRa BLE OOB v1.0 32 * specification. 33 */ 34 public class AdminErrorMessage extends FiraConnectorMessage { 35 private static final String TAG = AdminErrorMessage.class.getSimpleName(); 36 37 /** Type of OOB administrative error defined by FiRa BLE OOB v1.0 . */ 38 public enum ErrorType { 39 /** 40 * Sender: CS or CP, Receiver: CP or CS 41 * 42 * <p>Sent when last Data Packet received overflows resources allocated by FiRa Connector at 43 * given moment. When such error indication is received, the FiRa Device should re-transmit 44 * the packet with shorter size (might result in Packet Header change). 45 */ 46 DATA_PACKET_LENGTH_OVERFLOW(0x8001), 47 /** 48 * Sender: CS or CP, Receiver: CP or CS 49 * 50 * <p>Sent when last Data Packet received overflows resources allocated by FiRa Connector 51 * for FiRa Connector Message at given moment. When such error indication is received, the 52 * FiRa Device should re-transmit the entire fragmented Message session but with shorter 53 * Message size (might result in several Data Packets). 54 */ 55 MESSAGE_LENGTH_OVERFLOW(0x8002), 56 /** 57 * Sender: CS, Receiver: CP 58 * 59 * <p>Sent when last Data Packet received opens new fragmented Message session which exceeds 60 * “Maximum number of concurrent fragmented Message session supported” parameter set in FiRa 61 * Connector Capabilities. When such error indication is received, the FiRa Device should 62 * wait with Message transport until there is at least one Message session completed. 63 */ 64 TOO_MANY_CONCURRENT_FRAGMENTED_MESSAGE_SESSIONS(0x8003), 65 /** 66 * Sender: CS, Receiver: CP 67 * 68 * <p>Sent when last Data Packet was referencing SECID which isn’t currently existing on CS 69 * side. When such error indication is received, the CP should re-sent the Data Packet with 70 * correct SECID (e.g. after refreshing FiRa Connector Capabilities) or abort the Message 71 * transfer and report error to FiRa-enabled Application. 72 */ 73 SECID_INVALID(0x8004), 74 /** 75 * Sender: CP, Receiver: CS 76 * 77 * <p>Sent when last Data Packet was referencing SECID which isn’t expecting any Response on 78 * FiRa Connector Message level. When such error indication is received, the CS should 79 * re-sent the Data Packet with correct SECID or abort the Message transfer. 80 */ 81 SECID_INVALID_FOR_RESPONSE(0x8005), 82 /** 83 * Sender: CS, Receiver: CP 84 * 85 * <p>Sent when last Data Packet was referencing SECID which isn’t currently available for 86 * receiving Messages. When such error indication is received, the CP should re-sent the 87 * Data Packet later or abort the Message transfer and report error to FiRa-enabled 88 * Application. 89 */ 90 SECID_BUSY(0x8006), 91 /** 92 * Sender: CS or CP, Receiver: CP or CS 93 * 94 * <p>Sent when last Data Packet was completing Message transfer and that was rejected by 95 * the Secure Component (on CS side) or FiRa-enabled Application (on CP side) due to 96 * protocol inconsistency. When such error indication is received by CS, it should abort the 97 * Message transfer. When such error indication is received by CP, it should abort the 98 * Message transfer and report error to FiRa-enabled Application. 99 */ 100 SECID_PROTOCOL_ERROR(0x8007), 101 /** 102 * Sender: CS or CP, Receiver: CP or CS 103 * 104 * <p>Sent when last Data Packet was completing Message transfer and that was rejected by 105 * the Secure Component (on CS side) or FiRa-enabled Application (on CP side) due to 106 * internal processing error (unspecified reason). When such error indication is received by 107 * CS, it should abort the Message transfer. When such error indication is received by CP, 108 * it should abort the Message transfer and report error to FiRa-enabled Application. 109 */ 110 SECID_INTERNAL_ERROR(0x8008); 111 112 @IntRange(from = 0x8001, to = 0x8008) 113 private final int mValue; 114 115 private static Map sMap = new HashMap<>(); 116 ErrorType(int value)117 ErrorType(int value) { 118 this.mValue = value; 119 } 120 121 static { 122 for (ErrorType type : ErrorType.values()) { sMap.put(type.mValue, type)123 sMap.put(type.mValue, type); 124 } 125 } 126 127 /** 128 * Get the ErrorType based on the given value. 129 * 130 * @param value type value defined by FiRa. 131 * @return {@link ErrorType} associated with the value, else null if invalid. 132 */ 133 @Nullable valueOf(int value)134 public static ErrorType valueOf(int value) { 135 return (ErrorType) sMap.get(value); 136 } 137 getValue()138 public int getValue() { 139 return mValue; 140 } 141 } 142 143 @NonNull public final ErrorType errorType; 144 145 @Override toString()146 public String toString() { 147 StringBuilder sb = new StringBuilder(); 148 sb.append("AdminErrorMessage: errorType=").append(errorType); 149 return sb.toString(); 150 } 151 152 /** 153 * Convert the FiraConnectorMessage to an AdminErrorMessage if valid. 154 * 155 * @param firaConnectorMessage FiraConnectorMessage 156 * @return AdminErrorMessage if the message is an administrative error message. 157 */ convertToAdminErrorMessage( @onNull FiraConnectorMessage firaConnectorMessage)158 public static AdminErrorMessage convertToAdminErrorMessage( 159 @NonNull FiraConnectorMessage firaConnectorMessage) { 160 if (firaConnectorMessage == null) { 161 throw new IllegalArgumentException("firaConnectorMessage is null"); 162 } 163 if (!isAdminErrorMessage(firaConnectorMessage)) { 164 throw new IllegalArgumentException("firaConnectorMessage is not an AdminEventMessage"); 165 } 166 return new AdminErrorMessage(extractErrorType(firaConnectorMessage.payload)); 167 } 168 AdminErrorMessage(@onNull ErrorType errorType)169 public AdminErrorMessage(@NonNull ErrorType errorType) { 170 super( 171 MessageType.COMMAND_RESPOND, 172 InstructionCode.ERROR_INDICATION, 173 generatePayload(errorType)); 174 this.errorType = errorType; 175 } 176 177 /** 178 * Check if the message is an administrative error message. 179 * 180 * @param message FiraConnectorMessage 181 * @return true if the message is an administrative error message. 182 */ isAdminErrorMessage(@onNull FiraConnectorMessage message)183 public static boolean isAdminErrorMessage(@NonNull FiraConnectorMessage message) { 184 return (message.messageType == MessageType.COMMAND_RESPOND 185 && message.instructionCode == InstructionCode.ERROR_INDICATION 186 && extractErrorType(message.payload) != null); 187 } 188 generatePayload(@onNull ErrorType errorType)189 private static byte[] generatePayload(@NonNull ErrorType errorType) { 190 if (errorType == null) { 191 throw new IllegalArgumentException("errorType is null"); 192 } 193 byte[] errorTypeBytes = DataTypeConversionUtil.i32ToByteArray(errorType.getValue()); 194 return new byte[] {errorTypeBytes[2], errorTypeBytes[3]}; 195 } 196 extractErrorType(@onNull byte[] payload)197 private static ErrorType extractErrorType(@NonNull byte[] payload) { 198 if (ArrayUtils.isEmpty(payload)) { 199 Log.e(TAG, "Failed to extract ErrorType from empty payload."); 200 return null; 201 } 202 ErrorType errorType = 203 ErrorType.valueOf(DataTypeConversionUtil.arbitraryByteArrayToI32(payload)); 204 if (errorType == null) { 205 Log.e(TAG, "Failed to extract invalid ErrorType."); 206 } 207 return errorType; 208 } 209 } 210