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 import android.uwb.RangingReport;
21 
22 import com.google.uwb.support.base.RequiredParam;
23 import com.google.uwb.support.fira.FiraParams.StatusCode;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 /**
29  * Radar data packet
30  *
31  * <p>This is passed as the mRangingReportMetadata bundle in the RangingReport. {@link
32  * RangingReport#getRangingReportMetadata()} This will be passed for Radar sessions only.
33  */
34 public class RadarData extends RadarParams {
35     private static final int BUNDLE_VERSION_1 = 1;
36     private static final int BUNDLE_VERSION_CURRENT = BUNDLE_VERSION_1;
37 
38     private static final String KEY_STATUS_CODE = "status_code";
39     private static final String KEY_RADAR_DATA_TYPE = "radar_data_type";
40     private static final String KEY_SAMPLES_PER_SWEEP = "samples_per_sweep";
41     private static final String KEY_BITS_PER_SAMPLE = "bits_per_samples";
42     private static final String KEY_SWEEP_OFFSET = "sweep_offset";
43     private static final String KEY_SWEEP_DATA = "sweep_data";
44 
45     @StatusCode private final int mStatusCode;
46     @RadarDataType private final int mRadarDataType;
47     @SamplesPerSweep private final int mSamplesPerSweep;
48     @BitsPerSample private final int mBitsPerSample;
49     @SweepOffset private final int mSweepOffset;
50     private final List<RadarSweepData> mSweepData;
51 
RadarData( @tatusCode int statusCode, @RadarDataType int radarDataType, @SamplesPerSweep int samplesPerSweep, @BitsPerSample int bitsPerSample, @SweepOffset int sweepOffset, List<RadarSweepData> sweepData)52     private RadarData(
53             @StatusCode int statusCode,
54             @RadarDataType int radarDataType,
55             @SamplesPerSweep int samplesPerSweep,
56             @BitsPerSample int bitsPerSample,
57             @SweepOffset int sweepOffset,
58             List<RadarSweepData> sweepData) {
59         mStatusCode = statusCode;
60         mRadarDataType = radarDataType;
61         mSamplesPerSweep = samplesPerSweep;
62         mBitsPerSample = bitsPerSample;
63         mSweepOffset = sweepOffset;
64         mSweepData = sweepData;
65     }
66 
67     @Override
getBundleVersion()68     protected int getBundleVersion() {
69         return BUNDLE_VERSION_CURRENT;
70     }
71 
72     @Override
toBundle()73     public PersistableBundle toBundle() {
74         PersistableBundle bundle = super.toBundle();
75         bundle.putInt(KEY_STATUS_CODE, mStatusCode);
76         bundle.putInt(KEY_RADAR_DATA_TYPE, mRadarDataType);
77         bundle.putInt(KEY_SAMPLES_PER_SWEEP, mSamplesPerSweep);
78         bundle.putInt(KEY_BITS_PER_SAMPLE, mBitsPerSample);
79         bundle.putInt(KEY_SWEEP_OFFSET, mSweepOffset);
80         int sweep_index = 0;
81         for (RadarSweepData sweep : mSweepData) {
82             bundle.putPersistableBundle(KEY_SWEEP_DATA + sweep_index, sweep.toBundle());
83             sweep_index++;
84         }
85         return bundle;
86     }
87 
88     /** Unpack the {@link PersistableBundle} to a {@link RadarData} */
fromBundle(PersistableBundle bundle)89     public static RadarData fromBundle(PersistableBundle bundle) {
90         if (!isCorrectProtocol(bundle)) {
91             throw new IllegalArgumentException("Invalid protocol");
92         }
93 
94         switch (getBundleVersion(bundle)) {
95             case BUNDLE_VERSION_1:
96                 return parseBundleVersion1(bundle);
97 
98             default:
99                 throw new IllegalArgumentException("unknown bundle version");
100         }
101     }
102 
parseBundleVersion1(PersistableBundle bundle)103     private static RadarData parseBundleVersion1(PersistableBundle bundle) {
104         RadarData.Builder builder =
105                 new RadarData.Builder()
106                         .setStatusCode(bundle.getInt(KEY_STATUS_CODE))
107                         .setRadarDataType(bundle.getInt(KEY_RADAR_DATA_TYPE))
108                         .setSamplesPerSweep(bundle.getInt(KEY_SAMPLES_PER_SWEEP))
109                         .setBitsPerSample(bundle.getInt(KEY_BITS_PER_SAMPLE))
110                         .setSweepOffset(bundle.getInt(KEY_SWEEP_OFFSET));
111 
112         int sweep_index = 0;
113         PersistableBundle sweepBundle = bundle.getPersistableBundle(KEY_SWEEP_DATA + sweep_index);
114         while (sweepBundle != null) {
115             builder.addSweepData(RadarSweepData.fromBundle(sweepBundle));
116             sweep_index++;
117             sweepBundle = bundle.getPersistableBundle(KEY_SWEEP_DATA + sweep_index);
118         }
119         return builder.build();
120     }
121 
122     @StatusCode
getStatusCode()123     public int getStatusCode() {
124         return mStatusCode;
125     }
126 
127     @RadarDataType
getRadarDataType()128     public int getRadarDataType() {
129         return mRadarDataType;
130     }
131 
132     @SamplesPerSweep
getSamplesPerSweep()133     public int getSamplesPerSweep() {
134         return mSamplesPerSweep;
135     }
136 
137     @BitsPerSample
getBitsPerSample()138     public int getBitsPerSample() {
139         return mBitsPerSample;
140     }
141 
142     @SweepOffset
getSweepOffset()143     public int getSweepOffset() {
144         return mSweepOffset;
145     }
146 
getSweepData()147     public List<RadarSweepData> getSweepData() {
148         return mSweepData;
149     }
150 
151     /** Builder */
152     public static final class Builder {
153         @StatusCode private RequiredParam<Integer> mStatusCode = new RequiredParam<>();
154         @RadarDataType private RequiredParam<Integer> mRadarDataType = new RequiredParam<>();
155         @SamplesPerSweep private RequiredParam<Integer> mSamplesPerSweep = new RequiredParam<>();
156         @BitsPerSample private RequiredParam<Integer> mBitsPerSample = new RequiredParam<>();
157         @SweepOffset private RequiredParam<Integer> mSweepOffset = new RequiredParam<>();
158         private List<RadarSweepData> mSweepData = new ArrayList<>();
159 
160         /** Sets status code */
setStatusCode(@tatusCode int statusCode)161         public RadarData.Builder setStatusCode(@StatusCode int statusCode) {
162             mStatusCode.set(statusCode);
163             return this;
164         }
165 
166         /** Sets radar data type */
setRadarDataType(@adarDataType int radarDataType)167         public RadarData.Builder setRadarDataType(@RadarDataType int radarDataType) {
168             mRadarDataType.set(radarDataType);
169             return this;
170         }
171 
172         /** Sets samples per sweep */
setSamplesPerSweep(@amplesPerSweep int samplesPerSweep)173         public RadarData.Builder setSamplesPerSweep(@SamplesPerSweep int samplesPerSweep) {
174             mSamplesPerSweep.set(samplesPerSweep);
175             return this;
176         }
177 
178         /** Sets bits per sample */
setBitsPerSample(@itsPerSample int bitsPerSample)179         public RadarData.Builder setBitsPerSample(@BitsPerSample int bitsPerSample) {
180             mBitsPerSample.set(bitsPerSample);
181             return this;
182         }
183 
184         /** Sets sweep offset */
setSweepOffset(@weepOffset int sweepOffset)185         public RadarData.Builder setSweepOffset(@SweepOffset int sweepOffset) {
186             mSweepOffset.set(sweepOffset);
187             return this;
188         }
189 
190         /** Adds a list of {@link RadarSweepData} */
setSweepData(List<RadarSweepData> sweepData)191         public RadarData.Builder setSweepData(List<RadarSweepData> sweepData) {
192             mSweepData.addAll(sweepData);
193             return this;
194         }
195 
196         /** Adds a single {@link RadarSweepData} */
addSweepData(RadarSweepData sweepData)197         public RadarData.Builder addSweepData(RadarSweepData sweepData) {
198             mSweepData.add(sweepData);
199             return this;
200         }
201 
202         /** Build {@link RadarData} */
build()203         public RadarData build() {
204             if (mRadarDataType.get() == RadarParams.RADAR_DATA_TYPE_RADAR_SWEEP_SAMPLES
205                     && mSweepData.size() == 0) {
206                 throw new IllegalArgumentException("No radar sweep data");
207             }
208             return new RadarData(
209                     mStatusCode.get(),
210                     mRadarDataType.get(),
211                     mSamplesPerSweep.get(),
212                     mBitsPerSample.get(),
213                     mSweepOffset.get(),
214                     mSweepData);
215         }
216     }
217 }
218