• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 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.ranging.oob;
18 
19 import com.android.server.ranging.RangingTechnology;
20 import com.android.server.ranging.blerssi.BleRssiOobCapabilities;
21 import com.android.server.ranging.cs.CsOobCapabilities;
22 import com.android.server.ranging.rtt.RttOobCapabilities;
23 import com.android.server.ranging.uwb.UwbOobCapabilities;
24 
25 import com.google.auto.value.AutoValue;
26 import com.google.common.base.Preconditions;
27 import com.google.common.collect.ImmutableList;
28 import com.google.common.collect.Sets;
29 
30 import java.nio.ByteBuffer;
31 import java.util.Arrays;
32 
33 import javax.annotation.Nullable;
34 
35 /** The Capability Response Message Additional Data for Finder OOB. */
36 @AutoValue
37 public abstract class CapabilityResponseMessage {
38 
39     // Size of properties in bytes when serialized.
40     private static final int MIN_SIZE_BYTES = 2;
41 
42     private static final int RANGING_TECHNOLOGIES_SIZE_BYTES = 2;
43 
44     /**
45      * Parses the given byte array and returns {@link CapabilityResponseMessage} object. Throws
46      * {@link
47      * IllegalArgumentException} on invalid input.
48      */
parseBytes(byte[] payload)49     public static CapabilityResponseMessage parseBytes(byte[] payload) {
50         OobHeader header = OobHeader.parseBytes(payload);
51 
52         if (header.getMessageType() != MessageType.CAPABILITY_RESPONSE) {
53             throw new IllegalArgumentException(
54                     String.format("Invalid message type: %s, expected %s",
55                             header.getMessageType(), MessageType.CAPABILITY_RESPONSE));
56         }
57 
58         if (payload.length < header.getSize() + MIN_SIZE_BYTES) {
59             throw new IllegalArgumentException(String.format(
60                     "CapabilityResponseMessage payload size is %d bytes", payload.length));
61         }
62 
63         int parseCursor = header.getSize();
64 
65         // Parse ranging technologies bitfield
66         byte[] rangingTechnologiesBytes =
67                 Arrays.copyOfRange(payload, parseCursor,
68                         parseCursor + RANGING_TECHNOLOGIES_SIZE_BYTES);
69         ImmutableList<RangingTechnology> rangingTechnologies =
70                 RangingTechnology.fromBitmap(rangingTechnologiesBytes);
71         parseCursor += RANGING_TECHNOLOGIES_SIZE_BYTES;
72 
73         // Parse Capability data for different ranging technologies
74         UwbOobCapabilities uwbCapabilities = null;
75         CsOobCapabilities csCapabilities = null;
76         RttOobCapabilities rttCapabilities = null;
77         BleRssiOobCapabilities bleRssiCapabilities = null;
78         ImmutableList.Builder<RangingTechnology> rangingTechnologiesPriority =
79                 ImmutableList.builder();
80         int countTechsParsed = 0;
81         while (parseCursor < payload.length && countTechsParsed++ < rangingTechnologies.size()) {
82             byte[] remainingBytes = Arrays.copyOfRange(payload, parseCursor, payload.length);
83             TechnologyHeader techHeader = TechnologyHeader.parseBytes(remainingBytes);
84             switch (techHeader.getRangingTechnology()) {
85                 case UWB:
86                     uwbCapabilities = UwbOobCapabilities.parseBytes(remainingBytes);
87                     parseCursor += techHeader.getSize();
88                     rangingTechnologiesPriority.add(RangingTechnology.UWB);
89                     break;
90                 case CS:
91                     csCapabilities = CsOobCapabilities.parseBytes(remainingBytes);
92                     parseCursor += techHeader.getSize();
93                     rangingTechnologiesPriority.add(RangingTechnology.CS);
94                     break;
95                 case RTT:
96                     rttCapabilities = RttOobCapabilities.parseBytes(remainingBytes);
97                     parseCursor += techHeader.getSize();
98                     rangingTechnologiesPriority.add(RangingTechnology.RTT);
99                     break;
100                 case RSSI:
101                     bleRssiCapabilities = BleRssiOobCapabilities.parseBytes(remainingBytes);
102                     parseCursor += techHeader.getSize();
103                     rangingTechnologiesPriority.add(RangingTechnology.RSSI);
104                     break;
105                 default:
106                     rangingTechnologiesPriority.add(techHeader.getRangingTechnology());
107                     parseCursor += techHeader.getSize();
108                     break;
109             }
110         }
111 
112         return CapabilityResponseMessage.builder()
113                 .setHeader(header)
114                 .setSupportedRangingTechnologies(rangingTechnologies)
115                 .setUwbCapabilities(uwbCapabilities)
116                 .setCsCapabilities(csCapabilities)
117                 .setRttCapabilities(rttCapabilities)
118                 .setBleRssiCapabilities(bleRssiCapabilities)
119                 .setRangingTechnologiesPriority(rangingTechnologiesPriority.build())
120                 .build();
121     }
122 
123     /** Serializes this {@link CapabilityResponseMessage} object to bytes. */
toBytes()124     public final byte[] toBytes() {
125         int size = MIN_SIZE_BYTES + getHeader().getSize();
126         if (getUwbCapabilities() != null) {
127             size += UwbOobCapabilities.getSize();
128         }
129         if (getCsCapabilities() != null) {
130             size += CsOobCapabilities.getSize();
131         }
132         if (getRttCapabilities() != null) {
133             size += getRttCapabilities().toBytes().length;
134         }
135         if (getBleRssiCapabilities() != null) {
136             size += BleRssiOobCapabilities.getSize();
137         }
138         ByteBuffer byteBuffer = ByteBuffer.allocate(size);
139         byteBuffer
140                 .put(getHeader().toBytes())
141                 .put(RangingTechnology.toBitmap(getSupportedRangingTechnologies()));
142         for (RangingTechnology tech : getRangingTechnologiesPriority()) {
143             switch (tech) {
144                 case UWB:
145                     UwbOobCapabilities uwbCapabilities = getUwbCapabilities();
146                     if (uwbCapabilities != null) {
147                         byteBuffer.put(uwbCapabilities.toBytes());
148                     }
149                     break;
150                 case CS:
151                     CsOobCapabilities csCapabilities = getCsCapabilities();
152                     if (csCapabilities != null) {
153                         byteBuffer.put(csCapabilities.toBytes());
154                     }
155                     break;
156                 case RTT:
157                     RttOobCapabilities rttCapabilities = getRttCapabilities();
158                     if (rttCapabilities != null) {
159                         byteBuffer.put(rttCapabilities.toBytes());
160                     }
161                     break;
162                 case RSSI:
163                     BleRssiOobCapabilities rssiOobCapabilities = getBleRssiCapabilities();
164                     if (rssiOobCapabilities != null) {
165                         byteBuffer.put(rssiOobCapabilities.toBytes());
166                     }
167                     break;
168                 default:
169                     throw new UnsupportedOperationException("Not implemented");
170             }
171         }
172         return byteBuffer.array();
173     }
174 
175     /** Returns the OOB header. */
getHeader()176     public abstract OobHeader getHeader();
177 
178     /** Returns the supported ranging technologies. */
getSupportedRangingTechnologies()179     public abstract ImmutableList<RangingTechnology> getSupportedRangingTechnologies();
180 
181     /**
182      * Returns the priority of requested ranging technologies, with earlier items in the list being
183      * of
184      * higher priority.
185      */
getRangingTechnologiesPriority()186     public abstract ImmutableList<RangingTechnology> getRangingTechnologiesPriority();
187 
188     /** Returns an Optional of UWB capability data. */
189     @Nullable
getUwbCapabilities()190     public abstract UwbOobCapabilities getUwbCapabilities();
191 
192     /** Returns an Optional of CS capability data. */
193     @Nullable
getCsCapabilities()194     public abstract CsOobCapabilities getCsCapabilities();
195 
196     @Nullable
getRttCapabilities()197     public abstract RttOobCapabilities getRttCapabilities();
198 
199     @Nullable
getBleRssiCapabilities()200     public abstract BleRssiOobCapabilities getBleRssiCapabilities();
201 
202     /** Returns a builder for {@link CapabilityResponseMessage}. */
builder()203     public static Builder builder() {
204         return new AutoValue_CapabilityResponseMessage.Builder()
205                 .setRangingTechnologiesPriority(ImmutableList.of());
206     }
207 
208     /** Builder for {@link CapabilityResponseMessage}. */
209     @AutoValue.Builder
210     public abstract static class Builder {
211 
setHeader(OobHeader header)212         public abstract Builder setHeader(OobHeader header);
213 
setSupportedRangingTechnologies( ImmutableList<RangingTechnology> rangingTechnologies)214         public abstract Builder setSupportedRangingTechnologies(
215                 ImmutableList<RangingTechnology> rangingTechnologies);
216 
setUwbCapabilities(@ullable UwbOobCapabilities uwbCapabilities)217         public abstract Builder setUwbCapabilities(@Nullable UwbOobCapabilities uwbCapabilities);
218 
setCsCapabilities(@ullable CsOobCapabilities csCapabilities)219         public abstract Builder setCsCapabilities(@Nullable CsOobCapabilities csCapabilities);
220 
setRttCapabilities(@ullable RttOobCapabilities rttCapabilities)221         public abstract Builder setRttCapabilities(@Nullable RttOobCapabilities rttCapabilities);
222 
setBleRssiCapabilities( @ullable BleRssiOobCapabilities bleRssiCapabilities)223         public abstract Builder setBleRssiCapabilities(
224                 @Nullable BleRssiOobCapabilities bleRssiCapabilities);
225 
setRangingTechnologiesPriority( ImmutableList<RangingTechnology> rangingTechnologiesPriority )226         public abstract Builder setRangingTechnologiesPriority(
227                 ImmutableList<RangingTechnology> rangingTechnologiesPriority
228         );
229 
autoBuild()230         abstract CapabilityResponseMessage autoBuild();
231 
build()232         public final CapabilityResponseMessage build() {
233             CapabilityResponseMessage capabilityResponseMessage = autoBuild();
234             Preconditions.checkArgument(
235                     (capabilityResponseMessage
236                             .getRangingTechnologiesPriority()
237                             .contains(RangingTechnology.UWB)
238                             == (capabilityResponseMessage.getUwbCapabilities() != null)),
239                     "Priority list doesn't match UWB capabilities set.");
240             Preconditions.checkArgument(
241                     (capabilityResponseMessage.getRangingTechnologiesPriority().contains(
242                             RangingTechnology.CS)
243                             == (capabilityResponseMessage.getCsCapabilities() != null)),
244                     "Priority list doesn't match CS capabilities set.");
245             Preconditions.checkArgument(
246                     capabilityResponseMessage.getRangingTechnologiesPriority().size()
247                             == Sets.newEnumSet(
248                                     capabilityResponseMessage.getRangingTechnologiesPriority(),
249                                     RangingTechnology.class)
250                             .size(),
251                     "Priority list contains duplicates.");
252             return capabilityResponseMessage;
253         }
254     }
255 }
256