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