Merge "UWB: Add setUwbEnabled System API." am: 17b112aef8 am: ca5c906ab5

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1683089

Change-Id: I7936f0eb536424fa06a27d5c57bf8e3775863c74
This commit is contained in:
Joy Babafemi
2021-04-21 23:58:58 +00:00
committed by Automerger Merge Worker
7 changed files with 201 additions and 42 deletions

View File

@@ -12606,12 +12606,15 @@ package android.uwb {
}
public static interface UwbManager.AdapterStateCallback {
method public void onStateChanged(boolean, int);
method public void onStateChanged(int, int);
field public static final int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1; // 0x1
field public static final int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4; // 0x4
field public static final int STATE_CHANGED_REASON_SESSION_STARTED = 0; // 0x0
field public static final int STATE_CHANGED_REASON_SYSTEM_BOOT = 3; // 0x3
field public static final int STATE_CHANGED_REASON_SYSTEM_POLICY = 2; // 0x2
field public static final int STATE_DISABLED = 0; // 0x0
field public static final int STATE_ENABLED_ACTIVE = 2; // 0x2
field public static final int STATE_ENABLED_INACTIVE = 1; // 0x1
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 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 android.uwb;
/**
* @hide
*/
@Backing(type="int")
enum AdapterState {
/**
* The state when UWB is disabled.
*/
STATE_DISABLED,
/**
* The state when UWB is enabled but has no active sessions.
*/
STATE_ENABLED_INACTIVE,
/**
* The state when UWB is enabled and has active sessions.
*/
STATE_ENABLED_ACTIVE,
}

View File

@@ -21,6 +21,7 @@ import android.os.Binder;
import android.os.RemoteException;
import android.util.Log;
import android.uwb.UwbManager.AdapterStateCallback;
import android.uwb.UwbManager.AdapterStateCallback.State;
import android.uwb.UwbManager.AdapterStateCallback.StateChangedReason;
import java.util.HashMap;
@@ -40,7 +41,8 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
@StateChangedReason
private int mAdapterStateChangeReason = AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
private boolean mAdapterEnabledState = false;
@State
private int mAdapterState = AdapterStateCallback.STATE_DISABLED;
public AdapterStateListener(@NonNull IUwbAdapter adapter) {
mAdapter = adapter;
@@ -66,7 +68,7 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
mIsRegistered = true;
} catch (RemoteException e) {
Log.w(TAG, "Failed to register adapter state callback");
executor.execute(() -> callback.onStateChanged(false,
executor.execute(() -> callback.onStateChanged(mAdapterState,
AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN));
}
} else {
@@ -99,6 +101,42 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
}
}
/**
* Sets the adapter enabled state
*
* @param isEnabled value of new adapter state
*/
public void setEnabled(boolean isEnabled) {
synchronized (this) {
if (!mIsRegistered) {
return;
} else {
try {
mAdapter.setEnabled(isEnabled);
} catch (RemoteException e) {
Log.w(TAG, "Failed to set adapter state");
sendErrorState();
}
}
}
}
private void sendErrorState() {
synchronized (this) {
for (AdapterStateCallback callback: mCallbackMap.keySet()) {
Executor executor = mCallbackMap.get(callback);
final long identity = Binder.clearCallingIdentity();
try {
executor.execute(() -> callback.onStateChanged(
mAdapterState, mAdapterStateChangeReason));
} finally {
Binder.restoreCallingIdentity(identity);
}
}
}
}
private void sendCurrentState(@NonNull AdapterStateCallback callback) {
synchronized (this) {
Executor executor = mCallbackMap.get(callback);
@@ -106,7 +144,7 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
final long identity = Binder.clearCallingIdentity();
try {
executor.execute(() -> callback.onStateChanged(
mAdapterEnabledState, mAdapterStateChangeReason));
mAdapterState, mAdapterStateChangeReason));
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -114,12 +152,13 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
}
@Override
public void onAdapterStateChanged(boolean isEnabled, int reason) {
public void onAdapterStateChanged(int state, int reason) {
synchronized (this) {
@StateChangedReason int localReason =
convertToStateChangedReason(reason);
mAdapterEnabledState = isEnabled;
@State int localState = convertToState(state);
mAdapterStateChangeReason = localReason;
mAdapterState = localState;
for (AdapterStateCallback cb : mCallbackMap.keySet()) {
sendCurrentState(cb);
}
@@ -146,4 +185,18 @@ public class AdapterStateListener extends IUwbAdapterStateCallbacks.Stub {
return AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN;
}
}
private static @State int convertToState(@AdapterState int state) {
switch (state) {
case AdapterState.STATE_ENABLED_INACTIVE:
return AdapterStateCallback.STATE_ENABLED_INACTIVE;
case AdapterState.STATE_ENABLED_ACTIVE:
return AdapterStateCallback.STATE_ENABLED_ACTIVE;
case AdapterState.STATE_DISABLED:
default:
return AdapterStateCallback.STATE_DISABLED;
}
}
}

View File

@@ -145,6 +145,19 @@ interface IUwbAdapter {
*/
void closeRanging(in SessionHandle sessionHandle);
/**
* Disables or enables UWB for a user
*
* The provided callback's IUwbAdapterStateCallbacks#onAdapterStateChanged
* function must be called immediately following state change.
*
* @param enabled value representing intent to disable or enable UWB. If
* true, any subsequent calls to #openRanging will be allowed. If false,
* all active ranging sessions will be closed and subsequent calls to
* #openRanging will be disallowed.
*/
void setEnabled(boolean enabled);
/**
* The maximum allowed time to open a ranging session.
*/

View File

@@ -17,16 +17,17 @@
package android.uwb;
import android.uwb.StateChangeReason;
import android.uwb.AdapterState;
/**
* @hide
*/
interface IUwbAdapterStateCallbacks {
/**
* Called whenever the adapter state changes
*
* @param isEnabled true if the adapter is enabled, false otherwise
* @param reason the reason that the state has changed
*/
void onAdapterStateChanged(boolean isEnabled, StateChangeReason reason);
* Called whenever the adapter state changes
*
* @param state UWB state; enabled_active, enabled_inactive, or disabled.
* @param reason the reason that the state has changed
*/
void onAdapterStateChanged(AdapterState state, StateChangeReason reason);
}

View File

@@ -69,6 +69,16 @@ public final class UwbManager {
STATE_CHANGED_REASON_ERROR_UNKNOWN})
@interface StateChangedReason {}
/**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
STATE_ENABLED_INACTIVE,
STATE_ENABLED_ACTIVE,
STATE_DISABLED})
@interface State {}
/**
* Indicates that the state change was due to opening of first UWB session
*/
@@ -94,23 +104,42 @@ public final class UwbManager {
*/
int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4;
/**
* Indicates that UWB is disabled on device
*/
int STATE_DISABLED = 0;
/**
* Indicates that UWB is enabled on device but has no active ranging sessions
*/
int STATE_ENABLED_INACTIVE = 1;
/**
* Indicates that UWB is enabled and has active ranging session
*/
int STATE_ENABLED_ACTIVE = 2;
/**
* Invoked when underlying UWB adapter's state is changed
* <p>Invoked with the adapter's current state after registering an
* {@link AdapterStateCallback} using
* {@link UwbManager#registerAdapterStateCallback(Executor, AdapterStateCallback)}.
*
* <p>Possible values for the state to change are
* <p>Possible reasons for the state to change are
* {@link #STATE_CHANGED_REASON_SESSION_STARTED},
* {@link #STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED},
* {@link #STATE_CHANGED_REASON_SYSTEM_POLICY},
* {@link #STATE_CHANGED_REASON_SYSTEM_BOOT},
* {@link #STATE_CHANGED_REASON_ERROR_UNKNOWN}.
*
* @param isEnabled true when UWB adapter is enabled, false when it is disabled
* <p>Possible values for the UWB state are
* {@link #STATE_ENABLED_INACTIVE},
* {@link #STATE_ENABLED_ACTIVE},
* {@link #STATE_DISABLED}.
*
* @param state the UWB state; inactive, active or disabled
* @param reason the reason for the state change
*/
void onStateChanged(boolean isEnabled, @StateChangedReason int reason);
void onStateChanged(@State int state, @StateChangedReason int reason);
}
/**
@@ -241,4 +270,17 @@ public final class UwbManager {
@NonNull RangingSession.Callback callbacks) {
return mRangingManager.openSession(parameters, executor, callbacks);
}
/**
* Disables or enables UWB for a user
*
* @param enabled value representing intent to disable or enable UWB. If true any subsequent
* calls to IUwbAdapter#openRanging will be allowed. If false, all active ranging sessions will
* be closed and subsequent calls to IUwbAdapter#openRanging will be disallowed.
*
* @hide
*/
public void setUwbEnabled(boolean enabled) {
mAdapterStateListener.setEnabled(enabled);
}
}

View File

@@ -17,7 +17,6 @@
package android.uwb;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
@@ -55,7 +54,7 @@ public class AdapterStateListenerTest {
Object[] args = invocation.getArguments();
IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
try {
cb.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
cb.onAdapterStateChanged(AdapterState.STATE_DISABLED, StateChangeReason.UNKNOWN);
} catch (RemoteException e) {
// Nothing to do
}
@@ -76,7 +75,7 @@ public class AdapterStateListenerTest {
private static void verifyCallbackStateChangedInvoked(
AdapterStateCallback callback, int numTimes) {
verify(callback, times(numTimes)).onStateChanged(anyBoolean(), anyInt());
verify(callback, times(numTimes)).onStateChanged(anyInt(), anyInt());
}
@Test
@@ -151,7 +150,8 @@ public class AdapterStateListenerTest {
verifyCallbackStateChangedInvoked(callback, 1);
// Invoke a state change and ensure the callback is only called once
adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verifyCallbackStateChangedInvoked(callback, 2);
}
@@ -182,13 +182,15 @@ public class AdapterStateListenerTest {
verifyCallbackStateChangedInvoked(callback, 0);
// Manually invoke the callback and ensure callback is not invoked
adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verify(executor, times(2)).execute(any());
verifyCallbackStateChangedInvoked(callback, 0);
// Run the command that the executor receives
doAnswer(new ExecutorAnswer(true)).when(executor).execute(any());
adapterStateListener.onAdapterStateChanged(false, StateChangeReason.UNKNOWN);
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.UNKNOWN);
verify(executor, times(3)).execute(any());
verifyCallbackStateChangedInvoked(callback, 1);
}
@@ -227,7 +229,8 @@ public class AdapterStateListenerTest {
}
// Invoke a state change and ensure all callbacks are invoked
adapterStateListener.onAdapterStateChanged(true, StateChangeReason.ALL_SESSIONS_CLOSED);
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_DISABLED,
StateChangeReason.ALL_SESSIONS_CLOSED);
for (AdapterStateCallback callback : callbacks) {
verifyCallbackStateChangedInvoked(callback, 2);
}
@@ -242,32 +245,36 @@ public class AdapterStateListenerTest {
adapterStateListener.register(getExecutor(), callback);
runStateChangeValue(StateChangeReason.ALL_SESSIONS_CLOSED,
AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED);
AdapterState.STATE_ENABLED_INACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED,
AdapterStateCallback.STATE_ENABLED_INACTIVE);
runStateChangeValue(StateChangeReason.SESSION_STARTED,
AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED);
runStateChangeValue(StateChangeReason.SESSION_STARTED, AdapterState.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED,
AdapterStateCallback.STATE_ENABLED_ACTIVE);
runStateChangeValue(StateChangeReason.SYSTEM_BOOT,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT);
runStateChangeValue(StateChangeReason.SYSTEM_BOOT, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT,
AdapterStateCallback.STATE_DISABLED);
runStateChangeValue(StateChangeReason.SYSTEM_POLICY,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY);
runStateChangeValue(StateChangeReason.SYSTEM_POLICY, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_POLICY,
AdapterStateCallback.STATE_DISABLED);
runStateChangeValue(StateChangeReason.UNKNOWN,
AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN);
runStateChangeValue(StateChangeReason.UNKNOWN, AdapterState.STATE_DISABLED,
AdapterStateCallback.STATE_CHANGED_REASON_ERROR_UNKNOWN,
AdapterStateCallback.STATE_DISABLED);
}
private void runStateChangeValue(@StateChangeReason int reasonIn,
@AdapterStateCallback.StateChangedReason int reasonOut) {
private void runStateChangeValue(@StateChangeReason int reasonIn, @AdapterState int stateIn,
@AdapterStateCallback.StateChangedReason int reasonOut,
@AdapterStateCallback.State int stateOut) {
AdapterStateListener adapterStateListener = new AdapterStateListener(mUwbAdapter);
AdapterStateCallback callback = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback);
adapterStateListener.onAdapterStateChanged(false, reasonIn);
verify(callback, times(1)).onStateChanged(false, reasonOut);
adapterStateListener.onAdapterStateChanged(true, reasonIn);
verify(callback, times(1)).onStateChanged(true, reasonOut);
adapterStateListener.onAdapterStateChanged(stateIn, reasonIn);
verify(callback, times(1)).onStateChanged(stateOut, reasonOut);
}
@Test
@@ -280,7 +287,8 @@ public class AdapterStateListenerTest {
Object[] args = invocation.getArguments();
IUwbAdapterStateCallbacks cb = (IUwbAdapterStateCallbacks) args[0];
try {
cb.onAdapterStateChanged(true, StateChangeReason.SESSION_STARTED);
cb.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
StateChangeReason.SESSION_STARTED);
} catch (RemoteException e) {
// Nothing to do
}
@@ -291,7 +299,7 @@ public class AdapterStateListenerTest {
doAnswer(registerAnswer).when(mUwbAdapter).registerAdapterStateCallbacks(any());
adapterStateListener.register(getExecutor(), callback);
verify(callback).onStateChanged(true,
verify(callback).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SESSION_STARTED);
}
@@ -302,10 +310,11 @@ public class AdapterStateListenerTest {
AdapterStateCallback callback2 = mock(AdapterStateCallback.class);
adapterStateListener.register(getExecutor(), callback1);
adapterStateListener.onAdapterStateChanged(true, StateChangeReason.SYSTEM_BOOT);
adapterStateListener.onAdapterStateChanged(AdapterState.STATE_ENABLED_ACTIVE,
StateChangeReason.SYSTEM_BOOT);
adapterStateListener.register(getExecutor(), callback2);
verify(callback2).onStateChanged(true,
verify(callback2).onStateChanged(AdapterStateCallback.STATE_ENABLED_ACTIVE,
AdapterStateCallback.STATE_CHANGED_REASON_SYSTEM_BOOT);
}
}