1 /*
2  * Copyright (C) 2023 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.radar;
18 
19 import android.os.PersistableBundle;
20 
21 import androidx.annotation.Nullable;
22 
23 import com.google.uwb.support.base.RequiredParam;
24 
25 /**
26  * Radar sweep data packet
27  *
28  * <p>This is part of {@link RadarData}.
29  */
30 public class RadarSweepData extends RadarParams {
31     private static final int BUNDLE_VERSION_1 = 1;
32     private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
33 
34     private static final String KEY_SEQUENCE_NUMBER = "sequence_number";
35     private static final String KEY_TIMESTAMP = "timestamp";
36     private static final String KEY_VENDOR_SPECIFIC_DATA = "vendor_specific_data";
37     private static final String KEY_SAMPLE_DATA = "sample_data";
38 
39     private final long mSequenceNumber;
40     private final long mTimestamp;
41     private final byte[] mVendorSpecificData;
42     private final byte[] mSampleData;
43 
RadarSweepData( long sequenceNumber, long timestamp, byte[] vendorSpecificData, byte[] sampleData)44     private RadarSweepData(
45             long sequenceNumber, long timestamp, byte[] vendorSpecificData, byte[] sampleData) {
46         mSequenceNumber = sequenceNumber;
47         mTimestamp = timestamp;
48         mVendorSpecificData = vendorSpecificData;
49         mSampleData = sampleData;
50     }
51 
52     @Override
getBundleVersion()53     protected int getBundleVersion() {
54         return BUNDLE_VERSION_CURRENT;
55     }
56 
57     @Nullable
byteArrayToIntArray(@ullable byte[] bytes)58     private static int[] byteArrayToIntArray(@Nullable byte[] bytes) {
59         if (bytes == null) {
60             return null;
61         }
62         int[] values = new int[bytes.length];
63         for (int i = 0; i < values.length; i++) {
64             values[i] = bytes[i];
65         }
66         return values;
67     }
68 
69     @Nullable
intArrayToByteArray(@ullable int[] values)70     private static byte[] intArrayToByteArray(@Nullable int[] values) {
71         if (values == null) {
72             return null;
73         }
74         byte[] bytes = new byte[values.length];
75         for (int i = 0; i < values.length; i++) {
76             bytes[i] = (byte) values[i];
77         }
78         return bytes;
79     }
80 
81     @Override
toBundle()82     public PersistableBundle toBundle() {
83         PersistableBundle bundle = super.toBundle();
84         bundle.putLong(KEY_SEQUENCE_NUMBER, mSequenceNumber);
85         bundle.putLong(KEY_TIMESTAMP, mTimestamp);
86         bundle.putIntArray(KEY_VENDOR_SPECIFIC_DATA, byteArrayToIntArray(mVendorSpecificData));
87         bundle.putIntArray(KEY_SAMPLE_DATA, byteArrayToIntArray(mSampleData));
88         return bundle;
89     }
90 
91     /** Unpack the {@link PersistableBundle} to a {@link RadarSweepData} */
fromBundle(PersistableBundle bundle)92     public static RadarSweepData fromBundle(PersistableBundle bundle) {
93         if (!isCorrectProtocol(bundle)) {
94             throw new IllegalArgumentException("Invalid protocol");
95         }
96 
97         switch (getBundleVersion(bundle)) {
98             case BUNDLE_VERSION_1:
99                 return parseBundleVersion1(bundle);
100 
101             default:
102                 throw new IllegalArgumentException("unknown bundle version");
103         }
104     }
105 
parseBundleVersion1(PersistableBundle bundle)106     private static RadarSweepData parseBundleVersion1(PersistableBundle bundle) {
107         return new RadarSweepData.Builder()
108                 .setSequenceNumber(bundle.getLong(KEY_SEQUENCE_NUMBER))
109                 .setTimestamp(bundle.getLong(KEY_TIMESTAMP))
110                 .setVendorSpecificData(
111                         intArrayToByteArray(bundle.getIntArray(KEY_VENDOR_SPECIFIC_DATA)))
112                 .setSampleData(intArrayToByteArray(bundle.getIntArray(KEY_SAMPLE_DATA)))
113                 .build();
114     }
115 
getSequenceNumber()116     public long getSequenceNumber() {
117         return mSequenceNumber;
118     }
119 
getTimestamp()120     public long getTimestamp() {
121         return mTimestamp;
122     }
123 
getVendorSpecificData()124     public byte[] getVendorSpecificData() {
125         return mVendorSpecificData;
126     }
127 
getSampleData()128     public byte[] getSampleData() {
129         return mSampleData;
130     }
131 
132     /** Builder */
133     public static final class Builder {
134         private RequiredParam<Long> mSequenceNumber = new RequiredParam<>();
135         private RequiredParam<Long> mTimestamp = new RequiredParam<>();
136         private byte[] mVendorSpecificData;
137         private byte[] mSampleData;
138 
139         /** Sets sequence number */
setSequenceNumber(long sequenceNumber)140         public RadarSweepData.Builder setSequenceNumber(long sequenceNumber) {
141             mSequenceNumber.set(sequenceNumber);
142             return this;
143         }
144 
145         /** Sets timestamp */
setTimestamp(long timestamp)146         public RadarSweepData.Builder setTimestamp(long timestamp) {
147             mTimestamp.set(timestamp);
148             return this;
149         }
150 
151         /** Sets vendor specific data */
setVendorSpecificData(byte[] vendorSpecificData)152         public RadarSweepData.Builder setVendorSpecificData(byte[] vendorSpecificData) {
153             mVendorSpecificData = vendorSpecificData;
154             return this;
155         }
156 
157         /** Sets sample data */
setSampleData(byte[] sampleData)158         public RadarSweepData.Builder setSampleData(byte[] sampleData) {
159             mSampleData = sampleData;
160             return this;
161         }
162 
163         /** Build {@link RadarSweepData} */
build()164         public RadarSweepData build() {
165             if (mSequenceNumber.get() < 0) {
166                 throw new IllegalArgumentException("Invalid sequence number");
167             }
168             if (mTimestamp.get() < 0) {
169                 throw new IllegalArgumentException("Invalid timestamp");
170             }
171             if (mSampleData == null || mSampleData.length == 0) {
172                 throw new IllegalArgumentException("Empty radar sample data");
173             }
174             return new RadarSweepData(
175                     mSequenceNumber.get(), mTimestamp.get(), mVendorSpecificData, mSampleData);
176         }
177     }
178 }
179