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.secure.csml;
18 
19 import static com.android.server.uwb.secure.csml.SecureRangingInfo.SECURE_RANGING_INFO_TAG;
20 
21 import android.util.Log;
22 
23 import androidx.annotation.NonNull;
24 
25 import com.android.server.uwb.params.TlvBuffer;
26 import com.android.server.uwb.params.TlvDecoderBuffer;
27 
28 import java.nio.ByteBuffer;
29 import java.util.Arrays;
30 import java.util.Optional;
31 
32 import javax.annotation.Nullable;
33 
34 /**
35  * The session data used to config UWB session, see CSML 8.5.3.3.
36  */
37 public class SessionData {
38 
39     public static final String TAG = SessionData.class.getSimpleName();
40 
41     public static final int UWB_SESSION_DATA_VERSION_HEADER = 0x80;
42     public static int UWB_SESSION_DATA_VERSION_MINOR = 1;
43     public static int UWB_SESSION_DATA_VERSION_MAJOR = 1;
44     public static int UWB_SESSION_DATA_VERSION_MINOR_CURRENT = 1;
45     public static int UWB_SESSION_DATA_VERSION_MAJOR_CURRENT = 1;
46     public static int UWB_SESSION_ID = 0x81;
47     public static int UWB_SUB_SESSION_ID = 0x82;
48 
49     public static final int CONFIGURATION_PARAMS = 0xA3;
50     public Optional<ConfigurationParams> mConfigurationParams;
51     public Optional<SecureRangingInfo> mSecureRangingInfo;
52     public static int SESSION_DATA_COUNT_MAX = 8;
53 
54     public final int mSessionId;
55     public final Optional<Integer> mSubSessionId;
56 
SessionData(int sessionId, Optional<Integer> subSessionId, Optional<ConfigurationParams> configurationParams, Optional<SecureRangingInfo> secureRangingInfo)57     private SessionData(int sessionId, Optional<Integer> subSessionId,
58             Optional<ConfigurationParams> configurationParams,
59             Optional<SecureRangingInfo> secureRangingInfo) {
60         mSessionId = sessionId;
61         mSubSessionId = subSessionId;
62         mConfigurationParams = configurationParams;
63         mSecureRangingInfo = secureRangingInfo;
64     }
65 
66     /**
67      * Convert to raw data represented as TLV according to CSML 8.5.3.3.
68      */
69     @NonNull
toBytes()70     public byte[] toBytes() {
71         TlvBuffer.Builder sessionDataBuilder = new TlvBuffer.Builder()
72                 .putByteArray(UWB_SESSION_DATA_VERSION_HEADER, new byte[]{
73                         (byte) UWB_SESSION_DATA_VERSION_MAJOR_CURRENT,
74                         (byte) UWB_SESSION_DATA_VERSION_MINOR_CURRENT});
75 
76         sessionDataBuilder.putByteArray(UWB_SESSION_ID, ByteBuffer.allocate(4).putInt(
77                 mSessionId).array());
78         mSubSessionId.ifPresent(
79                 integer -> sessionDataBuilder.putByteArray(UWB_SUB_SESSION_ID, ByteBuffer.allocate(
80                         4).putInt(
81                         integer).array()));
82         mConfigurationParams.ifPresent(
83                 configurationParams -> sessionDataBuilder.putByteArray(CONFIGURATION_PARAMS,
84                         configurationParams.toBytes()));
85         mSecureRangingInfo.ifPresent(
86                 secureRangingInfo -> sessionDataBuilder.putByteArray(
87                         SECURE_RANGING_INFO_TAG, secureRangingInfo.toBytes()));
88 
89         return sessionDataBuilder.build().getByteArray();
90     }
91 
isPresent(TlvDecoderBuffer tlvDecoderBuffer, int tagType)92     private static boolean isPresent(TlvDecoderBuffer tlvDecoderBuffer, int tagType) {
93         try {
94             tlvDecoderBuffer.getByte(tagType);
95         } catch (IllegalArgumentException e) {
96             try {
97                 tlvDecoderBuffer.getByteArray(tagType);
98             } catch (IllegalArgumentException e1) {
99                 return false;
100             }
101         }
102         return true;
103     }
104 
105     /** Converts {@link SessionData} from the TLV data payload. */
106     @Nullable
fromBytes(@onNull byte[] data)107     public static SessionData fromBytes(@NonNull byte[] data) {
108         TlvDecoderBuffer tlvs = new TlvDecoderBuffer(data, SESSION_DATA_COUNT_MAX);
109         tlvs.parse();
110 
111         if (isPresent(tlvs, UWB_SESSION_DATA_VERSION_HEADER)) {
112             byte[] sessionDataVersion = tlvs.getByteArray(UWB_SESSION_DATA_VERSION_HEADER);
113             if (sessionDataVersion.length == 2 && versionCheck(sessionDataVersion)) {
114                 Builder sessionDataBuilder = new Builder();
115                 if (isPresent(tlvs, UWB_SESSION_ID)) {
116                     sessionDataBuilder.setSessionId(
117                             ByteBuffer.wrap(tlvs.getByteArray(UWB_SESSION_ID)).getInt());
118                 }
119                 if (isPresent(tlvs, UWB_SUB_SESSION_ID)) {
120                     sessionDataBuilder.setSubSessionId(
121                             ByteBuffer.wrap(tlvs.getByteArray(UWB_SUB_SESSION_ID)).getInt());
122                 }
123                 if (isPresent(tlvs, CONFIGURATION_PARAMS)) {
124                     byte[] configurationParams = tlvs.getByteArray(CONFIGURATION_PARAMS);
125                     sessionDataBuilder.setConfigParams(
126                             ConfigurationParams.fromBytes(configurationParams));
127                 }
128                 if (isPresent(tlvs, SECURE_RANGING_INFO_TAG)) {
129                     byte[] secureRangingInfo = tlvs.getByteArray(SECURE_RANGING_INFO_TAG);
130                     sessionDataBuilder.setSecureRangingInfo(
131                             SecureRangingInfo.fromBytes(secureRangingInfo));
132                 }
133                 return sessionDataBuilder.build();
134             }
135             Log.e(TAG, "UWB_SESSION_DATA_VERSION " + Arrays.toString(sessionDataVersion)
136                     + " Not supported");
137         } else {
138             Log.e(TAG, "Controlee info version is not included. Failure !");
139         }
140         return null;
141     }
142 
versionCheck(byte[] sessionDataVersion)143     private static boolean versionCheck(byte[] sessionDataVersion) {
144         return sessionDataVersion[0] == UWB_SESSION_DATA_VERSION_MAJOR
145                 && sessionDataVersion[1] == UWB_SESSION_DATA_VERSION_MINOR;
146     }
147 
148     /** Builder */
149     public static class Builder {
150         private int mSessionId;
151         private Optional<Integer> mSubSessionId = Optional.empty();
152         private Optional<ConfigurationParams> mConfigurationParams = Optional.empty();
153         private Optional<SecureRangingInfo> mSecureRangingInfo = Optional.empty();
154 
Builder()155         public Builder() {
156         }
157 
setSessionId(int sessionId)158         SessionData.Builder setSessionId(int sessionId) {
159             mSessionId = sessionId;
160             return this;
161         }
162 
setSubSessionId(int subSessionId)163         SessionData.Builder setSubSessionId(int subSessionId) {
164             mSubSessionId = Optional.of(subSessionId);
165             return this;
166         }
167 
168         @NonNull
setConfigParams( @onNull ConfigurationParams configurationParams)169         SessionData.Builder setConfigParams(
170                 @NonNull ConfigurationParams configurationParams) {
171             mConfigurationParams = Optional.of(configurationParams);
172             return this;
173         }
174 
175         @NonNull
setSecureRangingInfo(@onNull SecureRangingInfo secureRangingInfo)176         SessionData.Builder setSecureRangingInfo(@NonNull SecureRangingInfo secureRangingInfo) {
177             mSecureRangingInfo = Optional.of(secureRangingInfo);
178             return this;
179         }
180 
181         @NonNull
build()182         SessionData build() {
183             return new SessionData(mSessionId, mSubSessionId,
184                     mConfigurationParams, mSecureRangingInfo);
185         }
186     }
187 }
188