/* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.snippet.ranging; import static android.ranging.RangingPreference.DEVICE_ROLE_INITIATOR; import static android.ranging.uwb.UwbComplexChannel.UWB_CHANNEL_9; import static android.ranging.uwb.UwbComplexChannel.UWB_PREAMBLE_CODE_INDEX_11; import android.ranging.DataNotificationConfig; import android.ranging.RangingConfig; import android.ranging.RangingDevice; import android.ranging.RangingPreference; import android.ranging.SensorFusionParams; import android.ranging.SessionConfig; import android.ranging.ble.cs.BleCsRangingParams; import android.ranging.ble.rssi.BleRssiRangingParams; import android.ranging.oob.DeviceHandle; import android.ranging.oob.OobInitiatorRangingConfig; import android.ranging.oob.OobResponderRangingConfig; import android.ranging.raw.RawInitiatorRangingConfig; import android.ranging.raw.RawRangingDevice; import android.ranging.raw.RawResponderRangingConfig; import android.ranging.uwb.UwbAddress; import android.ranging.uwb.UwbComplexChannel; import android.ranging.uwb.UwbRangingParams; import android.ranging.wifi.rtt.RttRangingParams; import com.google.android.mobly.snippet.SnippetObjectConverter; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.lang.reflect.Type; import java.time.Duration; import java.util.UUID; public class RangingPreferenceConverter implements SnippetObjectConverter { RangingSnippet.OobTransportFactory mTransportHandleFactory; RangingPreferenceConverter(RangingSnippet.OobTransportFactory transportHandleFactory) { mTransportHandleFactory = transportHandleFactory; } @Override public JSONObject serialize(Object object) { throw new UnsupportedOperationException("Not implemented"); } @Override public RangingPreference deserialize(JSONObject j, Type type) throws JSONException { if (type != RangingPreference.class) return null; return new RangingPreference.Builder(j.getInt("device_role"), getRangingParams(j.getJSONObject("ranging_params"), j.getInt("device_role"))) .setSessionConfig( new SessionConfig.Builder() .setSensorFusionParams( getSensorFusionParams( j.getJSONObject("sensor_fusion_params")) ) .setDataNotificationConfig( getDataNotificationConfig( j.getBoolean("enable_range_data_notifications")) ) .setRangingMeasurementsLimit(j.getInt("measurement_limit")) .build() ) .build(); } private RangingConfig getRangingParams( JSONObject j, @RangingPreference.DeviceRole int role ) throws JSONException { RangingConfig params; if (j.getInt("session_type") == RangingConfig.RANGING_SESSION_RAW) { if (role == DEVICE_ROLE_INITIATOR) { params = getRawInitiatorRangingConfig(j); } else { params = getRawResponderRangingConfig(j); } } else { if (role == DEVICE_ROLE_INITIATOR) { params = getOobInitiatorRangingConfig(j); } else { params = getOobResponderRangingConfig(j); } } return params; } private OobInitiatorRangingConfig getOobInitiatorRangingConfig( JSONObject j ) throws JSONException { OobInitiatorRangingConfig.Builder builder = new OobInitiatorRangingConfig.Builder(); JSONArray jPeers = j.getJSONArray("peer_ids"); for (int i = 0; i < jPeers.length(); i++) { RangingDevice device = new RangingDevice.Builder() .setUuid(UUID.fromString(jPeers.getString(i))) .build(); builder.addDeviceHandle(new DeviceHandle.Builder( device, mTransportHandleFactory.createOobTransport(device) ).build()); } return builder .setFastestRangingInterval( Duration.ofMillis(j.getJSONArray("ranging_interval_ms").getInt(0))) .setSlowestRangingInterval( Duration.ofMillis(j.getJSONArray("ranging_interval_ms").getInt(1))) .setSecurityLevel(j.getInt("security_level")) .setRangingMode(j.getInt("ranging_mode")) .build(); } private OobResponderRangingConfig getOobResponderRangingConfig( JSONObject j ) throws JSONException { RangingDevice device = new RangingDevice.Builder() .setUuid(UUID.fromString(j.getString("peer_id"))) .build(); return new OobResponderRangingConfig.Builder( new DeviceHandle.Builder( device, mTransportHandleFactory.createOobTransport(device)).build()) .build(); } private RawInitiatorRangingConfig getRawInitiatorRangingConfig( JSONObject j ) throws JSONException { RawInitiatorRangingConfig.Builder builder = new RawInitiatorRangingConfig.Builder(); JSONArray jPeerParams = j.getJSONArray("peer_params"); for (int i = 0; i < jPeerParams.length(); i++) { builder.addRawRangingDevice(getRawRangingDevice(jPeerParams.getJSONObject(i))); } return builder.build(); } private RawResponderRangingConfig getRawResponderRangingConfig( JSONObject j ) throws JSONException { return new RawResponderRangingConfig.Builder() .setRawRangingDevice(getRawRangingDevice(j.getJSONObject("peer_params"))) .build(); } private RawRangingDevice getRawRangingDevice(JSONObject j) throws JSONException { RawRangingDevice.Builder builder = new RawRangingDevice.Builder(); builder.setRangingDevice( new RangingDevice.Builder() .setUuid(UUID.fromString(j.getString("peer_id"))) .build() ); if (!j.isNull("uwb_params")) { builder.setUwbRangingParams(getUwbParams(j.getJSONObject("uwb_params"))); } if (!j.isNull("cs_params")) { builder.setCsRangingParams(getCsParams(j.getJSONObject("cs_params"))); } if (!j.isNull("rtt_params")) { builder.setRttRangingParams(getRttParams(j.getJSONObject("rtt_params"))); } if (!j.isNull("rssi_params")) { builder.setBleRssiRangingParams(getBleRssiRangingParams( j.getJSONObject("rssi_params"))); } return builder.build(); } private UwbRangingParams getUwbParams(JSONObject j) throws JSONException { UwbRangingParams.Builder builder = new UwbRangingParams.Builder( j.getInt("session_id"), j.getInt("config_id"), UwbAddress.fromBytes(toBytes(j.getJSONArray("device_address"))), UwbAddress.fromBytes(toBytes(j.getJSONArray("peer_address"))) ); builder.setSubSessionId(j.getInt("sub_session_id")) .setSessionKeyInfo(toBytes(j.getJSONArray("session_key_info"))) .setComplexChannel( new UwbComplexChannel.Builder().setChannel(UWB_CHANNEL_9) .setPreambleIndex(UWB_PREAMBLE_CODE_INDEX_11).build()) .setRangingUpdateRate(j.getInt("ranging_update_rate")) .setSlotDuration(j.getInt("slot_duration_ms")); if (!j.isNull("sub_session_key_info")) { builder.setSubSessionKeyInfo(toBytes(j.getJSONArray("sub_session_key_info"))); } return builder.build(); } private RttRangingParams getRttParams(JSONObject j) throws JSONException { return new RttRangingParams.Builder(j.getString("service_name")) .setPeriodicRangingHwFeatureEnabled( j.getBoolean("enable_periodic_ranging_hw_feature")) .setRangingUpdateRate(j.getInt("ranging_update_rate")) .build(); } private BleRssiRangingParams getBleRssiRangingParams(JSONObject j) throws JSONException { return new BleRssiRangingParams.Builder(j.getString("peer_address")) .setRangingUpdateRate(j.getInt("ranging_update_rate")) .build(); } private BleCsRangingParams getCsParams(JSONObject j) throws JSONException { return new BleCsRangingParams.Builder(j.getString("peer_address")) .setRangingUpdateRate(j.getInt("ranging_update_rate")) .setSecurityLevel(j.getInt("security_level")) .build(); } private SensorFusionParams getSensorFusionParams(JSONObject j) throws JSONException { return new SensorFusionParams.Builder() .setSensorFusionEnabled(j.getBoolean("is_sensor_fusion_enabled")) .build(); } private DataNotificationConfig getDataNotificationConfig(boolean enableRangeDataNotifications) { return new DataNotificationConfig.Builder() .setNotificationConfigType( enableRangeDataNotifications ? DataNotificationConfig.NOTIFICATION_CONFIG_ENABLE : DataNotificationConfig.NOTIFICATION_CONFIG_DISABLE ) .build(); } private byte[] toBytes(JSONArray jArray) throws JSONException { if (jArray == null) { return null; } byte[] bArray = new byte[jArray.length()]; for (int i = 0; i < jArray.length(); i++) { bArray[i] = (byte) jArray.getInt(i); } return bArray; } }