blob: a887709bfbac55944177ae28b8d7ae1c4969bb6f [file] [log] [blame]
/*
* 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.cts;
import static android.Manifest.permission.UWB_PRIVILEGED;
import static android.Manifest.permission.UWB_RANGING;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.UiAutomation;
import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextParams;
import android.os.CancellationSignal;
import android.os.PersistableBundle;
import android.os.Process;
import android.permission.PermissionManager;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
import android.uwb.RangingReport;
import android.uwb.RangingSession;
import android.uwb.UwbManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* Test of {@link UwbManager}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "Cannot get UwbManager in instant app mode")
public class UwbManagerTest {
private static final String TAG = "UwbManagerTest";
private final Context mContext = InstrumentationRegistry.getContext();
private UwbManager mUwbManager;
@Before
public void setup() {
mUwbManager = mContext.getSystemService(UwbManager.class);
assumeTrue(UwbTestUtils.isUwbSupported(mContext));
assertThat(mUwbManager).isNotNull();
}
@Test
public void testGetSpecificationInfo() {
UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
try {
// Needs UWB_PRIVILEGED permission which is held by shell.
uiAutomation.adoptShellPermissionIdentity();
PersistableBundle persistableBundle = mUwbManager.getSpecificationInfo();
assertThat(persistableBundle).isNotNull();
assertThat(persistableBundle.isEmpty()).isFalse();
} finally {
uiAutomation.dropShellPermissionIdentity();
}
}
@Test
public void testGetSpecificationInfoWithoutUwbPrivileged() {
try {
mUwbManager.getSpecificationInfo();
// should fail if the call was successful without UWB_PRIVILEGED permission.
fail();
} catch (SecurityException e) {
/* pass */
Log.i(TAG, "Failed with expected security exception: " + e);
}
}
@Test
public void testElapsedRealtimeResolutionNanos() {
UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
try {
// Needs UWB_PRIVILEGED permission which is held by shell.
uiAutomation.adoptShellPermissionIdentity();
assertThat(mUwbManager.elapsedRealtimeResolutionNanos() >= 0L).isTrue();
} finally {
uiAutomation.dropShellPermissionIdentity();
}
}
@Test
public void testElapsedRealtimeResolutionNanosWithoutUwbPrivileged() {
try {
mUwbManager.elapsedRealtimeResolutionNanos();
// should fail if the call was successful without UWB_PRIVILEGED permission.
fail();
} catch (SecurityException e) {
/* pass */
Log.i(TAG, "Failed with expected security exception: " + e);
}
}
private class RangingSessionCallback implements RangingSession.Callback {
private final CountDownLatch mCountDownLatch;
public boolean onOpenedCalled;
public boolean onOpenFailedCalled;
public RangingSession rangingSession;
RangingSessionCallback(@NonNull CountDownLatch countDownLatch) {
mCountDownLatch = countDownLatch;
}
public void onOpened(@NonNull RangingSession session) {
onOpenedCalled = true;
rangingSession = session;
mCountDownLatch.countDown();
}
public void onOpenFailed(@Reason int reason, @NonNull PersistableBundle params) {
onOpenFailedCalled = true;
mCountDownLatch.countDown();
}
public void onStarted(@NonNull PersistableBundle sessionInfo) { }
public void onStartFailed(@Reason int reason, @NonNull PersistableBundle params) { }
public void onReconfigured(@NonNull PersistableBundle params) { }
public void onReconfigureFailed(@Reason int reason, @NonNull PersistableBundle params) { }
public void onStopped(@Reason int reason, @NonNull PersistableBundle parameters) { }
public void onStopFailed(@Reason int reason, @NonNull PersistableBundle params) { }
public void onClosed(@Reason int reason, @NonNull PersistableBundle parameters) { }
public void onReportReceived(@NonNull RangingReport rangingReport) { }
}
@Test
public void testOpenRangingSessionWithBadParams() throws Exception {
UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
CancellationSignal cancellationSignal = null;
CountDownLatch countDownLatch = new CountDownLatch(1);
RangingSessionCallback rangingSessionCallback = new RangingSessionCallback(countDownLatch);
try {
// Needs UWB_PRIVILEGED & UWB_RANGING permission which is held by shell.
uiAutomation.adoptShellPermissionIdentity();
// Try to start a ranging session with invalid params, should fail.
cancellationSignal = mUwbManager.openRangingSession(
new PersistableBundle(),
Executors.newSingleThreadExecutor(),
rangingSessionCallback);
// Wait for the on start failed callback.
assertThat(countDownLatch.await(1, TimeUnit.SECONDS)).isTrue();
assertThat(rangingSessionCallback.onOpenedCalled).isFalse();
assertThat(rangingSessionCallback.onOpenFailedCalled).isTrue();
} finally {
if (cancellationSignal != null) {
cancellationSignal.cancel();
}
uiAutomation.dropShellPermissionIdentity();
}
}
/**
* Simulates the app holding UWB_RANGING permission, but not UWB_PRIVILEGED.
*/
@Test
public void testOpenRangingSessionWithoutUwbPrivileged() {
UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
try {
// Only hold UWB_RANGING permission
uiAutomation.adoptShellPermissionIdentity(UWB_RANGING);
mUwbManager.openRangingSession(new PersistableBundle(),
Executors.newSingleThreadExecutor(),
new RangingSessionCallback(new CountDownLatch(1)));
// should fail if the call was successful without UWB_PRIVILEGED permission.
fail();
} catch (SecurityException e) {
/* pass */
Log.i(TAG, "Failed with expected security exception: " + e);
} finally {
uiAutomation.dropShellPermissionIdentity();
}
}
/**
* Simulates the app holding UWB_PRIVILEGED permission, but not UWB_RANGING.
*/
@Test
public void testOpenRangingSessionWithoutUwbRanging() {
UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
try {
// Needs UWB_PRIVILEGED permission which is held by shell.
uiAutomation.adoptShellPermissionIdentity(UWB_PRIVILEGED);
mUwbManager.openRangingSession(new PersistableBundle(),
Executors.newSingleThreadExecutor(),
new RangingSessionCallback(new CountDownLatch(1)));
// should fail if the call was successful without UWB_RANGING permission.
fail();
} catch (SecurityException e) {
/* pass */
Log.i(TAG, "Failed with expected security exception: " + e);
} finally {
uiAutomation.dropShellPermissionIdentity();
}
}
private AttributionSource getShellAttributionSourceWithRenouncedPermissions(
@Nullable Set<String> renouncedPermissions) {
try {
AttributionSource shellAttributionSource =
new AttributionSource.Builder(Process.SHELL_UID)
.setPackageName("com.android.shell")
.setRenouncedPermissions(renouncedPermissions)
.build();
PermissionManager permissionManager =
mContext.getSystemService(PermissionManager.class);
permissionManager.registerAttributionSource(shellAttributionSource);
return shellAttributionSource;
} catch (SecurityException e) {
fail("Failed to create shell attribution source" + e);
return null;
}
}
private Context createShellContextWithRenouncedPermissionsAndAttributionSource(
@Nullable Set<String> renouncedPermissions) {
return mContext.createContext(new ContextParams.Builder()
.setRenouncedPermissions(renouncedPermissions)
.setNextAttributionSource(
getShellAttributionSourceWithRenouncedPermissions(renouncedPermissions))
.build());
}
/**
* Simulates the calling app holding UWB_PRIVILEGED permission and UWB_RANGING permission, but
* the proxied app not holding UWB_RANGING permission.
*/
@Test
public void testOpenRangingSessionWithoutUwbRangingInNextAttributeSource() {
UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
try {
// Only hold UWB_PRIVILEGED permission
uiAutomation.adoptShellPermissionIdentity();
Context shellContextWithUwbRangingRenounced =
createShellContextWithRenouncedPermissionsAndAttributionSource(
Set.of(UWB_RANGING));
UwbManager uwbManagerWithUwbRangingRenounced =
shellContextWithUwbRangingRenounced.getSystemService(UwbManager.class);
uwbManagerWithUwbRangingRenounced.openRangingSession(new PersistableBundle(),
Executors.newSingleThreadExecutor(),
new RangingSessionCallback(new CountDownLatch(1)));
// should fail if the call was successful without UWB_RANGING permission.
fail();
} catch (SecurityException e) {
/* pass */
Log.i(TAG, "Failed with expected security exception: " + e);
} finally {
uiAutomation.dropShellPermissionIdentity();
}
}
}