blob: c621e284bb2ef2324c69adc1d79f2307877b25b2 [file] [log] [blame]
/*
* Copyright (C) 2021 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.android.server.uwb;
import static com.android.server.uwb.data.UwbUciConstants.REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS;
import android.annotation.Nullable;
import android.content.AttributionSource;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import android.uwb.IUwbAdapter;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.RangingChangeReason;
import android.uwb.SessionHandle;
import android.uwb.UwbAddress;
import androidx.annotation.VisibleForTesting;
import com.android.server.uwb.data.UwbMulticastListUpdateStatus;
import com.android.server.uwb.data.UwbRangingData;
import com.android.server.uwb.data.UwbUciConstants;
import com.android.server.uwb.jni.INativeUwbManager;
import com.android.server.uwb.jni.NativeUwbManager;
import com.android.server.uwb.proto.UwbStatsLog;
import com.android.server.uwb.util.ArrayUtils;
import com.google.uwb.support.base.Params;
import com.google.uwb.support.ccc.CccOpenRangingParams;
import com.google.uwb.support.ccc.CccParams;
import com.google.uwb.support.ccc.CccRangingStartedParams;
import com.google.uwb.support.ccc.CccStartRangingParams;
import com.google.uwb.support.fira.FiraParams;
import com.google.uwb.support.fira.FiraRangingReconfigureParams;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class UwbSessionManager implements INativeUwbManager.SessionNotification {
private static final String TAG = "UwbSessionManager";
private static final int SESSION_OPEN_RANGING = 1;
private static final int SESSION_START_RANGING = 2;
private static final int SESSION_STOP_RANGING = 3;
private static final int SESSION_RECONFIG_RANGING = 4;
private static final int SESSION_CLOSE = 5;
// TODO: don't expose the internal field for testing.
@VisibleForTesting
final ConcurrentHashMap<Integer, UwbSession> mSessionTable = new ConcurrentHashMap();
private final NativeUwbManager mNativeUwbManager;
private final UwbMetrics mUwbMetrics;
private final UwbSessionNotificationManager mSessionNotificationManager;
private final UwbConfigurationManager mConfigurationManager;
private final int mMaxSessionNumber;
private final EventTask mEventTask;
public UwbSessionManager(UwbConfigurationManager uwbConfigurationManager,
NativeUwbManager nativeUwbManager, UwbMetrics uwbMetrics,
UwbSessionNotificationManager uwbSessionNotificationManager,
Looper serviceLooper) {
mNativeUwbManager = nativeUwbManager;
mNativeUwbManager.setSessionListener(this);
mUwbMetrics = uwbMetrics;
mConfigurationManager = uwbConfigurationManager;
mSessionNotificationManager = uwbSessionNotificationManager;
mMaxSessionNumber = mNativeUwbManager.getMaxSessionNumber();
mEventTask = new EventTask(serviceLooper);
}
@Override
public void onRangeDataNotificationReceived(UwbRangingData rangingData) {
long sessionId = rangingData.getSessionId();
UwbSession uwbSession = getUwbSession((int) sessionId);
if (uwbSession != null) {
mUwbMetrics.logRangingResult(uwbSession.getProfileType(), rangingData);
mSessionNotificationManager.onRangingResult(uwbSession, rangingData);
} else {
Log.i(TAG, "Session is not initialized or Ranging Data is Null");
}
}
@Override
public void onMulticastListUpdateNotificationReceived(
UwbMulticastListUpdateStatus multicastListUpdateStatus) {
Log.d(TAG, "onMulticastListUpdateNotificationReceived");
UwbSession uwbSession = getUwbSession((int) multicastListUpdateStatus.getSessionId());
if (uwbSession == null) {
Log.d(TAG, "onSessionStatusNotificationReceived - invalid session");
return;
}
uwbSession.setMulticastListUpdateStatus(multicastListUpdateStatus);
synchronized (uwbSession.getWaitObj()) {
uwbSession.getWaitObj().blockingNotify();
}
}
@Override
public void onSessionStatusNotificationReceived(long sessionId, int state, int reasonCode) {
Log.i(TAG, "onSessionStatusNotificationReceived - Session ID : " + sessionId + ", state : "
+ UwbSessionNotificationHelper.getSessionStateString(state) + " reasonCode:"
+ reasonCode);
UwbSession uwbSession = mSessionTable.get((int) sessionId);
if (uwbSession == null) {
Log.d(TAG, "onSessionStatusNotificationReceived - invalid session");
return;
}
int prevState = uwbSession.getSessionState();
synchronized (uwbSession.getWaitObj()) {
uwbSession.getWaitObj().blockingNotify();
setCurrentSessionState((int) sessionId, state);
}
//TODO : process only error handling in this switch function, b/218921154
switch (state) {
case UwbUciConstants.UWB_SESSION_STATE_IDLE:
if (prevState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
if (reasonCode
== UwbUciConstants.REASON_MAX_RANGING_ROUND_RETRY_COUNT_REACHED) {
mSessionNotificationManager.onRangingStopped(uwbSession, reasonCode);
mUwbMetrics.longRangingStopEvent(uwbSession);
}
} else if (prevState == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
//mSessionNotificationManager.onRangingReconfigureFailed(
// uwbSession, reasonCode);
}
break;
default:
break;
}
}
private byte getSessionType(String protocolName) {
byte sessionType = UwbUciConstants.SESSION_TYPE_RANGING;
if (protocolName.equals(FiraParams.PROTOCOL_NAME)) {
sessionType = UwbUciConstants.SESSION_TYPE_RANGING;
} else if (protocolName.equals(CccParams.PROTOCOL_NAME)) {
sessionType = UwbUciConstants.SESSION_TYPE_CCC;
}
return sessionType;
}
private int setAppConfigurations(UwbSession uwbSession) {
return mConfigurationManager.setAppConfigurations(uwbSession.getSessionId(),
uwbSession.getParams());
}
public synchronized void initSession(AttributionSource attributionSource,
SessionHandle sessionHandle, int sessionId,
String protocolName, Params params, IUwbRangingCallbacks rangingCallbacks)
throws RemoteException {
Log.i(TAG, "initSession() : Enter - sessionId : " + sessionId);
UwbSession uwbSession = createUwbSession(attributionSource, sessionHandle, sessionId,
protocolName, params, rangingCallbacks);
if (isExistedSession(sessionId)) {
Log.i(TAG, "Duplicated sessionId");
rangingCallbacks.onRangingOpenFailed(sessionHandle, RangingChangeReason.UNKNOWN,
UwbSessionNotificationHelper.convertStatusToParam(protocolName,
UwbUciConstants.STATUS_CODE_ERROR_SESSION_DUPLICATE));
mUwbMetrics.logRangingInitEvent(uwbSession,
UwbUciConstants.STATUS_CODE_ERROR_SESSION_DUPLICATE);
return;
}
if (getSessionCount() >= mMaxSessionNumber) {
Log.i(TAG, "Max Sessions Exceeded");
rangingCallbacks.onRangingOpenFailed(sessionHandle,
RangingChangeReason.MAX_SESSIONS_REACHED,
UwbSessionNotificationHelper.convertStatusToParam(protocolName,
UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED));
mUwbMetrics.logRangingInitEvent(uwbSession,
UwbUciConstants.STATUS_CODE_ERROR_MAX_SESSIONS_EXCEEDED);
return;
}
byte sessionType = getSessionType(protocolName);
try {
uwbSession.getBinder().linkToDeath(uwbSession, 0);
} catch (RemoteException e) {
uwbSession.binderDied();
Log.e(TAG, "linkToDeath fail - sessionID : " + uwbSession.getSessionId());
rangingCallbacks.onRangingOpenFailed(sessionHandle, RangingChangeReason.UNKNOWN,
UwbSessionNotificationHelper.convertStatusToParam(protocolName,
UwbUciConstants.STATUS_CODE_FAILED));
mUwbMetrics.logRangingInitEvent(uwbSession,
UwbUciConstants.STATUS_CODE_FAILED);
removeSession(uwbSession);
return;
}
mSessionTable.put(sessionId, uwbSession);
mEventTask.execute(SESSION_OPEN_RANGING, uwbSession);
return;
}
// TODO: use UwbInjector.
@VisibleForTesting
UwbSession createUwbSession(AttributionSource attributionSource, SessionHandle sessionHandle,
int sessionId, String protocolName, Params params,
IUwbRangingCallbacks iUwbRangingCallbacks) {
return new UwbSession(attributionSource, sessionHandle, sessionId, protocolName, params,
iUwbRangingCallbacks);
}
public synchronized void deInitSession(SessionHandle sessionHandle) {
if (!isExistedSession(sessionHandle)) {
Log.i(TAG, "Not initialized session ID");
return;
}
int sessionId = getSessionId(sessionHandle);
Log.i(TAG, "sessionDeInit() - Session ID : " + sessionId);
UwbSession uwbSession = getUwbSession(sessionId);
mEventTask.execute(SESSION_CLOSE, uwbSession);
return;
}
public synchronized void startRanging(SessionHandle sessionHandle, @Nullable Params params) {
if (!isExistedSession(sessionHandle)) {
Log.i(TAG, "Not initialized session ID");
return;
}
int sessionId = getSessionId(sessionHandle);
Log.i(TAG, "startRanging() - Session ID : " + sessionId);
UwbSession uwbSession = getUwbSession(sessionId);
int currentSessionState = getCurrentSessionState(sessionId);
if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME)
&& params instanceof CccStartRangingParams) {
CccStartRangingParams rangingStartParams = (CccStartRangingParams) params;
Log.i(TAG, "startRanging() - update RAN multiplier: "
+ rangingStartParams.getRanMultiplier());
// Need to update the RAN multiplier from the CccStartRangingParams for CCC session.
uwbSession.updateCccParamsOnStart(rangingStartParams);
}
mEventTask.execute(SESSION_START_RANGING, uwbSession);
} else if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
Log.i(TAG, "session is already ranging");
mSessionNotificationManager.onRangingStartFailed(
uwbSession, UwbUciConstants.STATUS_CODE_REJECTED);
} else {
Log.i(TAG, "session can't start ranging");
mSessionNotificationManager.onRangingStartFailed(
uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
mUwbMetrics.longRangingStartEvent(uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
}
}
public synchronized void stopRanging(SessionHandle sessionHandle) {
if (!isExistedSession(sessionHandle)) {
Log.i(TAG, "Not initialized session ID");
return;
}
int sessionId = getSessionId(sessionHandle);
Log.i(TAG, "stopRanging() - Session ID : " + sessionId);
UwbSession uwbSession = getUwbSession(sessionId);
int currentSessionState = getCurrentSessionState(sessionId);
if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
mEventTask.execute(SESSION_STOP_RANGING, uwbSession);
} else if (currentSessionState == UwbUciConstants.UWB_SESSION_STATE_IDLE) {
Log.i(TAG, "session is already idle state");
mSessionNotificationManager.onRangingStopped(uwbSession,
REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS);
mUwbMetrics.longRangingStopEvent(uwbSession);
} else {
int status = UwbUciConstants.STATUS_CODE_REJECTED;
mSessionNotificationManager.onRangingStopFailed(uwbSession, status);
Log.i(TAG, "Not active session ID");
}
}
public UwbSession getUwbSession(int sessionId) {
return mSessionTable.get(sessionId);
}
public Integer getSessionId(SessionHandle sessionHandle) {
for (Map.Entry<Integer, UwbSession> sessionEntry : mSessionTable.entrySet()) {
UwbSession uwbSession = sessionEntry.getValue();
if ((uwbSession.getSessionHandle()).equals(sessionHandle)) {
return sessionEntry.getKey();
}
}
return null;
}
private int getActiveSessionCount() {
int count = 0;
for (Map.Entry<Integer, UwbSession> sessionEntry : mSessionTable.entrySet()) {
UwbSession uwbSession = sessionEntry.getValue();
if ((uwbSession.getSessionState() == UwbUciConstants.DEVICE_STATE_ACTIVE)) {
count++;
}
}
return count;
}
public boolean isExistedSession(SessionHandle sessionHandle) {
return (getSessionId(sessionHandle) != null);
}
public boolean isExistedSession(int sessionId) {
return mSessionTable.containsKey(sessionId);
}
public void stopAllRanging() {
Log.d(TAG, "stopAllRanging()");
for (Map.Entry<Integer, UwbSession> sessionEntry : mSessionTable.entrySet()) {
int status = mNativeUwbManager.stopRanging(sessionEntry.getKey());
if (status != UwbUciConstants.STATUS_CODE_OK) {
Log.i(TAG, "stopAllRanging() - Session " + sessionEntry.getKey()
+ " is failed to stop ranging");
} else {
UwbSession uwbSession = sessionEntry.getValue();
mUwbMetrics.longRangingStopEvent(uwbSession);
uwbSession.setSessionState(UwbUciConstants.UWB_SESSION_STATE_IDLE);
}
}
}
public synchronized void deinitAllSession() {
Log.d(TAG, "deinitAllSession()");
for (Map.Entry<Integer, UwbSession> sessionEntry : mSessionTable.entrySet()) {
UwbSession uwbSession = sessionEntry.getValue();
mSessionNotificationManager.onRangingClosed(uwbSession,
REASON_STATE_CHANGE_WITH_SESSION_MANAGEMENT_COMMANDS);
mUwbMetrics.logRangingCloseEvent(uwbSession, UwbUciConstants.STATUS_CODE_OK);
removeSession(uwbSession);
}
// Not resetting chip on UWB toggle off.
// mNativeUwbManager.resetDevice(UwbUciConstants.UWBS_RESET);
}
public void setCurrentSessionState(int sessionId, int state) {
UwbSession uwbSession = mSessionTable.get(sessionId);
if (uwbSession != null) {
uwbSession.setSessionState(state);
}
}
public int getCurrentSessionState(int sessionId) {
UwbSession uwbSession = mSessionTable.get(sessionId);
if (uwbSession != null) {
return uwbSession.getSessionState();
}
return UwbUciConstants.UWB_SESSION_STATE_ERROR;
}
public int getSessionCount() {
return mSessionTable.size();
}
public Set<Integer> getSessionIdSet() {
return mSessionTable.keySet();
}
public int reconfigure(SessionHandle sessionHandle, PersistableBundle params) {
Log.i(TAG, "reconfigure() - Session Handle : " + sessionHandle);
int status = UwbUciConstants.STATUS_CODE_ERROR_SESSION_NOT_EXIST;
if (!isExistedSession(sessionHandle)) {
Log.i(TAG, "Not initialized session ID");
return status;
}
/*
FiraRangingReconfigureParams is only implemented
TODO - CccRangingReconfiguredParams should be implemented
*/
FiraRangingReconfigureParams param = FiraRangingReconfigureParams.fromBundle(params);
Pair<SessionHandle, Params> info = new Pair<SessionHandle, Params>(sessionHandle, param);
mEventTask.execute(SESSION_RECONFIG_RANGING, info);
return 0;
}
void removeSession(UwbSession uwbSession) {
if (uwbSession != null) {
uwbSession.getBinder().unlinkToDeath(uwbSession, 0);
mSessionTable.remove(uwbSession.getSessionId());
}
}
private class EventTask extends Handler {
EventTask(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
int type = msg.what;
switch (type) {
case SESSION_OPEN_RANGING: {
UwbSession uwbSession = (UwbSession) msg.obj;
openRanging(uwbSession);
break;
}
case SESSION_START_RANGING: {
UwbSession uwbSession = (UwbSession) msg.obj;
startRanging(uwbSession);
break;
}
case SESSION_STOP_RANGING: {
UwbSession uwbSession = (UwbSession) msg.obj;
stopRanging(uwbSession);
break;
}
case SESSION_RECONFIG_RANGING: {
Log.d(TAG, "SESSION_RECONFIG_RANGING");
Pair<SessionHandle, Params> info = (Pair<SessionHandle, Params>) msg.obj;
reconfigure(info.first, info.second);
break;
}
case SESSION_CLOSE: {
UwbSession uwbSession = (UwbSession) msg.obj;
close(uwbSession);
break;
}
default: {
Log.d(TAG, "EventTask : Undefined Task");
break;
}
}
}
public void execute(int task, Object obj) {
Message msg = mEventTask.obtainMessage();
msg.what = task;
msg.obj = obj;
this.sendMessage(msg);
}
private void openRanging(UwbSession uwbSession) {
// TODO(b/211445008): Consolidate to a single uwb thread.
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<Integer> initSessionTask = new FutureTask<>(
() -> {
int status = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
status = mNativeUwbManager.initSession(
uwbSession.getSessionId(),
getSessionType(uwbSession.getParams().getProtocolName()));
if (status != UwbUciConstants.STATUS_CODE_OK) {
return status;
}
uwbSession.getWaitObj().blockingWait();
status = UwbUciConstants.STATUS_CODE_FAILED;
if (uwbSession.getSessionState()
== UwbUciConstants.UWB_SESSION_STATE_INIT) {
status = UwbSessionManager.this.setAppConfigurations(uwbSession);
if (status != UwbUciConstants.STATUS_CODE_OK) {
return status;
}
uwbSession.getWaitObj().blockingWait();
status = UwbUciConstants.STATUS_CODE_FAILED;
if (uwbSession.getSessionState()
== UwbUciConstants.UWB_SESSION_STATE_IDLE) {
mSessionNotificationManager.onRangingOpened(uwbSession);
status = UwbUciConstants.STATUS_CODE_OK;
} else {
status = UwbUciConstants.STATUS_CODE_FAILED;
}
return status;
}
return status;
}
});
executor.submit(initSessionTask);
int status = UwbUciConstants.STATUS_CODE_FAILED;
try {
status = initSessionTask.get(
IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
executor.shutdownNow();
Log.i(TAG, "Failed to initialize session - status : TIMEOUT");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
mUwbMetrics.logRangingInitEvent(uwbSession, status);
if (status != UwbUciConstants.STATUS_CODE_OK) {
Log.i(TAG, "Failed to initialize session - status : " + status);
mSessionNotificationManager.onRangingOpenFailed(uwbSession, status);
mNativeUwbManager.deInitSession(uwbSession.getSessionId());
removeSession(uwbSession);
}
Log.i(TAG, "sessionInit() : finish - sessionId : " + uwbSession.getSessionId());
}
private void startRanging(UwbSession uwbSession) {
// TODO(b/211445008): Consolidate to a single uwb thread.
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<Integer> startRangingTask = new FutureTask<>(
() -> {
int status = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
if (uwbSession.getParams().getProtocolName()
.equals(CccParams.PROTOCOL_NAME)) {
status = mConfigurationManager.setAppConfigurations(
uwbSession.getSessionId(),
uwbSession.getParams());
if (status != UwbUciConstants.STATUS_CODE_OK) {
mSessionNotificationManager.onRangingStartFailed(
uwbSession, status);
return status;
}
}
status = mNativeUwbManager.startRanging(uwbSession.getSessionId());
if (status != UwbUciConstants.STATUS_CODE_OK) {
mSessionNotificationManager.onRangingStartFailed(
uwbSession, status);
return status;
}
uwbSession.getWaitObj().blockingWait();
if (uwbSession.getSessionState()
== UwbUciConstants.UWB_SESSION_STATE_ACTIVE) {
// TODO: Ensure |rangingStartedParams| is valid for FIRA sessions
// as well.
Params rangingStartedParams = uwbSession.getParams();
// For CCC sessions, retrieve the app configs
if (uwbSession.getProtocolName().equals(CccParams.PROTOCOL_NAME)) {
Pair<Integer, CccRangingStartedParams> statusAndParams =
mConfigurationManager.getAppConfigurations(
uwbSession.getSessionId(),
CccParams.PROTOCOL_NAME,
new byte[0],
CccRangingStartedParams.class);
if (statusAndParams.first != UwbUciConstants.STATUS_CODE_OK) {
Log.e(TAG, "Failed to get CCC ranging started params");
}
rangingStartedParams = statusAndParams.second;
}
mSessionNotificationManager.onRangingStarted(
uwbSession, rangingStartedParams);
} else {
status = UwbUciConstants.STATUS_CODE_FAILED;
mSessionNotificationManager.onRangingStartFailed(
uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
}
}
return status;
});
executor.submit(startRangingTask);
int status = UwbUciConstants.STATUS_CODE_FAILED;
try {
status = startRangingTask.get(
IUwbAdapter.RANGING_SESSION_START_THRESHOLD_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.i(TAG, "Failed to Start Ranging - status : TIMEOUT");
executor.shutdownNow();
mSessionNotificationManager.onRangingStartFailed(
uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
mUwbMetrics.longRangingStartEvent(uwbSession, status);
}
private void stopRanging(UwbSession uwbSession) {
// TODO(b/211445008): Consolidate to a single uwb thread.
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<Integer> stopRangingTask = new FutureTask<>(
() -> {
int status = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
status = mNativeUwbManager.stopRanging(uwbSession.getSessionId());
if (status != UwbUciConstants.STATUS_CODE_OK) {
mSessionNotificationManager.onRangingStopFailed(uwbSession, status);
return status;
}
uwbSession.getWaitObj().blockingWait();
if (uwbSession.getSessionState()
== UwbUciConstants.UWB_SESSION_STATE_IDLE) {
mSessionNotificationManager.onRangingStopped(uwbSession, status);
} else {
status = UwbUciConstants.STATUS_CODE_FAILED;
mSessionNotificationManager.onRangingStopFailed(
uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
}
}
return status;
});
executor.submit(stopRangingTask);
int status = UwbUciConstants.STATUS_CODE_FAILED;
try {
status = stopRangingTask.get(
IUwbAdapter.RANGING_SESSION_START_THRESHOLD_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.i(TAG, "Failed to Stop Ranging - status : TIMEOUT");
executor.shutdownNow();
mSessionNotificationManager.onRangingStopFailed(
uwbSession, UwbUciConstants.STATUS_CODE_FAILED);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
if (status != UwbUciConstants.STATUS_CODE_FAILED) {
mUwbMetrics.longRangingStopEvent(uwbSession);
}
}
private void reconfigure(SessionHandle sessionHandle, Params param) {
FiraRangingReconfigureParams rangingReconfigureParams =
(FiraRangingReconfigureParams) param;
// TODO(b/211445008): Consolidate to a single uwb thread.
UwbSession uwbSession = getUwbSession(getSessionId(sessionHandle));
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<Integer> cmdTask = new FutureTask<>(
() -> {
int status = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
// Handle SESSION_UPDATE_CONTROLLER_MULTICAST_LIST_CMD
if (rangingReconfigureParams.getAction() != null) {
Log.d(TAG, "call multicastlist update");
int dstAddressListSize =
rangingReconfigureParams.getAddressList().length;
List<Short> dstAddressList = new ArrayList<>();
for (UwbAddress address :
rangingReconfigureParams.getAddressList()) {
dstAddressList.add(
ByteBuffer.wrap(address.toBytes()).getShort(0));
}
int[] subSessionIdList = null;
if (!ArrayUtils.isEmpty(
rangingReconfigureParams.getSubSessionIdList())) {
subSessionIdList =
rangingReconfigureParams.getSubSessionIdList();
} else {
// Set to 0's for the UCI stack.
subSessionIdList = new int[dstAddressListSize];
}
status = mNativeUwbManager.controllerMulticastListUpdate(
uwbSession.getSessionId(),
rangingReconfigureParams.getAction(),
subSessionIdList.length,
ArrayUtils.toPrimitive(dstAddressList),
subSessionIdList);
if (status != UwbUciConstants.STATUS_CODE_OK) {
return status;
}
uwbSession.getWaitObj().blockingWait();
UwbMulticastListUpdateStatus multicastList =
uwbSession.getMulticastListUpdateStatus();
if (multicastList != null) {
if (rangingReconfigureParams.getAction()
== FiraParams.MULTICAST_LIST_UPDATE_ACTION_ADD) {
for (int i = 0; i < multicastList.getNumOfControlee();
i++) {
if (multicastList.getStatus()[i]
!= UwbUciConstants.STATUS_CODE_OK) {
//TODO - Need implement for partial fail case.
return UwbUciConstants.STATUS_CODE_FAILED;
}
}
}
}
}
status = mConfigurationManager.setAppConfigurations(
uwbSession.getSessionId(), param);
Log.d(TAG, "status: " + status);
if (status != UwbUciConstants.STATUS_CODE_OK) {
return status;
}
mSessionNotificationManager.onRangingReconfigured(uwbSession);
return status;
}
});
executor.submit(cmdTask);
int status = UwbUciConstants.STATUS_CODE_FAILED;
try {
status = cmdTask.get(
IUwbAdapter.RANGING_SESSION_OPEN_THRESHOLD_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.i(TAG, "Failed to Reconfigure - status : TIMEOUT");
executor.shutdownNow();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
if (status != UwbUciConstants.STATUS_CODE_OK) {
Log.i(TAG, "Failed to Reconfigure : " + status);
mSessionNotificationManager.onRangingReconfigureFailed(uwbSession, status);
}
}
private void close(UwbSession uwbSession) {
// TODO(b/211445008): Consolidate to a single uwb thread.
ExecutorService executor = Executors.newSingleThreadExecutor();
FutureTask<Integer> closeTask = new FutureTask<>(
(Callable<Integer>) () -> {
int status = UwbUciConstants.STATUS_CODE_FAILED;
synchronized (uwbSession.getWaitObj()) {
status = mNativeUwbManager.deInitSession(uwbSession.getSessionId());
if (status != UwbUciConstants.STATUS_CODE_OK) {
mSessionNotificationManager.onRangingClosed(uwbSession, status);
return status;
}
uwbSession.getWaitObj().blockingWait();
Log.i(TAG, "onRangingClosed - status : " + status);
mSessionNotificationManager.onRangingClosed(uwbSession, status);
}
return status;
});
executor.submit(closeTask);
int status = UwbUciConstants.STATUS_CODE_FAILED;
try {
status = closeTask.get(
IUwbAdapter.RANGING_SESSION_CLOSE_THRESHOLD_MS, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.i(TAG, "Failed to Stop Ranging - status : TIMEOUT");
executor.shutdownNow();
mSessionNotificationManager.onRangingClosed(uwbSession, status);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
mUwbMetrics.logRangingCloseEvent(uwbSession, status);
removeSession(uwbSession);
Log.i(TAG, "deinit finish : status :" + status);
}
}
public class UwbSession implements IBinder.DeathRecipient {
private final AttributionSource mAttributionSource;
private final SessionHandle mSessionHandle;
private final int mSessionId;
private final IUwbRangingCallbacks mIUwbRangingCallbacks;
private final String mProtocolName;
private final IBinder mIBinder;
private final WaitObj mWaitObj;
public boolean isWait;
private Params mParams;
private int mSessionState;
private UwbMulticastListUpdateStatus mMulticastListUpdateStatus;
private final int mProfileType;
UwbSession(AttributionSource attributionSource, SessionHandle sessionHandle, int sessionId,
String protocolName, Params params, IUwbRangingCallbacks iUwbRangingCallbacks) {
this.mAttributionSource = attributionSource;
this.mSessionHandle = sessionHandle;
this.mSessionId = sessionId;
this.mProtocolName = protocolName;
this.mIUwbRangingCallbacks = iUwbRangingCallbacks;
this.mIBinder = iUwbRangingCallbacks.asBinder();
this.mSessionState = UwbUciConstants.UWB_SESSION_STATE_DEINIT;
this.mParams = params;
this.mWaitObj = new WaitObj();
this.isWait = false;
this.mProfileType = convertProtolNameToProfileType(protocolName);
}
public AttributionSource getAttributionSource() {
return this.mAttributionSource;
}
public int getSessionId() {
return this.mSessionId;
}
public SessionHandle getSessionHandle() {
return this.mSessionHandle;
}
public Params getParams() {
return this.mParams;
}
public void updateCccParamsOnStart(CccStartRangingParams rangingStartParams) {
// Need to update the RAN multiplier from the CccStartRangingParams for CCC session.
CccOpenRangingParams rangingOpenedParams = (CccOpenRangingParams) mParams;
CccOpenRangingParams newParams =
new CccOpenRangingParams.Builder()
.setProtocolVersion(rangingOpenedParams.getProtocolVersion())
.setUwbConfig(rangingOpenedParams.getUwbConfig())
.setPulseShapeCombo(rangingOpenedParams.getPulseShapeCombo())
.setSessionId(rangingOpenedParams.getSessionId())
.setRanMultiplier(rangingStartParams.getRanMultiplier())
.setChannel(rangingOpenedParams.getChannel())
.setNumChapsPerSlot(rangingOpenedParams.getNumChapsPerSlot())
.setNumResponderNodes(rangingOpenedParams.getNumResponderNodes())
.setNumSlotsPerRound(rangingOpenedParams.getNumSlotsPerRound())
.setSyncCodeIndex(rangingOpenedParams.getSyncCodeIndex())
.setHoppingConfigMode(rangingOpenedParams.getHoppingConfigMode())
.setHoppingSequence(rangingOpenedParams.getHoppingSequence())
.build();
this.mParams = newParams;
}
public String getProtocolName() {
return this.mProtocolName;
}
public IUwbRangingCallbacks getIUwbRangingCallbacks() {
return this.mIUwbRangingCallbacks;
}
public int getSessionState() {
return this.mSessionState;
}
public void setSessionState(int state) {
this.mSessionState = state;
}
public void setMulticastListUpdateStatus(
UwbMulticastListUpdateStatus multicastListUpdateStatus) {
mMulticastListUpdateStatus = multicastListUpdateStatus;
}
public UwbMulticastListUpdateStatus getMulticastListUpdateStatus() {
return mMulticastListUpdateStatus;
}
private int convertProtolNameToProfileType(String protocolName) {
if (protocolName.equals(FiraParams.PROTOCOL_NAME)) {
return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__FIRA;
} else if (protocolName.equals(CccParams.PROTOCOL_NAME)) {
return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__CCC;
} else {
return UwbStatsLog.UWB_SESSION_INITIATED__PROFILE__CUSTOMIZED;
}
}
public int getProfileType() {
return mProfileType;
}
public IBinder getBinder() {
return mIBinder;
}
public WaitObj getWaitObj() {
return mWaitObj;
}
@Override
public void binderDied() {
Log.i(TAG, "binderDied : getSessionId is getSessionId() " + getSessionId());
synchronized (UwbSessionManager.this) {
int status = mNativeUwbManager.deInitSession(getSessionId());
mUwbMetrics.logRangingCloseEvent(this, status);
if (status == UwbUciConstants.STATUS_CODE_OK) {
removeSession(this);
Log.i(TAG, "binderDied : Session count currently is " + getSessionCount());
} else {
Log.e(TAG,
"binderDied : sessionDeinit Failure because of NativeSessionDeinit "
+ "Error");
}
}
}
}
// TODO: refactor the async operation flow.
// Wrapper for unit test.
@VisibleForTesting
static class WaitObj {
WaitObj() {
}
void blockingWait() throws InterruptedException {
wait();
}
void blockingNotify() {
notify();
}
}
}