| /* |
| * 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 android.uwb.UwbManager.AdapterStateCallback.STATE_DISABLED; |
| import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE; |
| |
| import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.junit.Assert.assertThrows; |
| 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 com.android.compatibility.common.util.ShellIdentityUtils; |
| |
| import com.google.uwb.support.multichip.ChipInfoParams; |
| |
| import org.junit.Before; |
| import org.junit.Ignore; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.List; |
| 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; |
| private String mDefaultChipId; |
| |
| @Before |
| public void setup() throws Exception { |
| mUwbManager = mContext.getSystemService(UwbManager.class); |
| assumeTrue(UwbTestUtils.isUwbSupported(mContext)); |
| assertThat(mUwbManager).isNotNull(); |
| |
| // Ensure UWB is toggled on. |
| ShellIdentityUtils.invokeWithShellPermissions(() -> { |
| if (!mUwbManager.isUwbEnabled()) { |
| try { |
| setUwbEnabledAndWaitForCompletion(true); |
| } catch (Exception e) { |
| fail("Exception while processing UWB toggle " + e); |
| } |
| } |
| mDefaultChipId = mUwbManager.getDefaultChipId(); |
| }); |
| } |
| |
| // Should be invoked with shell permissions. |
| private void setUwbEnabledAndWaitForCompletion(boolean enabled) throws Exception { |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| int adapterState = enabled ? STATE_ENABLED_INACTIVE : STATE_DISABLED; |
| AdapterStateCallback adapterStateCallback = |
| new AdapterStateCallback(countDownLatch, adapterState); |
| try { |
| mUwbManager.registerAdapterStateCallback( |
| Executors.newSingleThreadExecutor(), adapterStateCallback); |
| mUwbManager.setUwbEnabled(enabled); |
| assertThat(countDownLatch.await(2, TimeUnit.SECONDS)).isTrue(); |
| assertThat(mUwbManager.isUwbEnabled()).isEqualTo(enabled); |
| assertThat(adapterStateCallback.state).isEqualTo(adapterState); |
| } finally { |
| mUwbManager.unregisterAdapterStateCallback(adapterStateCallback); |
| } |
| } |
| |
| @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 testGetSpecificationInfoWithChipId() { |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| try { |
| // Needs UWB_PRIVILEGED permission which is held by shell. |
| uiAutomation.adoptShellPermissionIdentity(); |
| PersistableBundle persistableBundle = |
| mUwbManager.getSpecificationInfo(mDefaultChipId); |
| assertThat(persistableBundle).isNotNull(); |
| assertThat(persistableBundle.isEmpty()).isFalse(); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testGetChipInfos() { |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| try { |
| // Needs UWB_PRIVILEGED permission which is held by shell. |
| uiAutomation.adoptShellPermissionIdentity(); |
| List<PersistableBundle> chipInfos = mUwbManager.getChipInfos(); |
| assertThat(chipInfos).hasSize(1); |
| ChipInfoParams chipInfoParams = ChipInfoParams.fromBundle(chipInfos.get(0)); |
| assertThat(chipInfoParams.getChipId()).isEqualTo(mDefaultChipId); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testGetSpecificationInfoWithInvalidChipId() { |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| try { |
| // Needs UWB_PRIVILEGED permission which is held by shell. |
| uiAutomation.adoptShellPermissionIdentity(); |
| assertThrows(IllegalArgumentException.class, |
| () -> mUwbManager.getSpecificationInfo("invalidChipId")); |
| } 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 testGetSpecificationInfoWithChipIdWithoutUwbPrivileged() { |
| try { |
| mUwbManager.getSpecificationInfo(mDefaultChipId); |
| // 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 testElapsedRealtimeResolutionNanosWithChipId() { |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| try { |
| // Needs UWB_PRIVILEGED permission which is held by shell. |
| uiAutomation.adoptShellPermissionIdentity(); |
| assertThat(mUwbManager.elapsedRealtimeResolutionNanos(mDefaultChipId) >= 0L) |
| .isTrue(); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testElapsedRealtimeResolutionNanosWithInvalidChipId() { |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| try { |
| // Needs UWB_PRIVILEGED permission which is held by shell. |
| uiAutomation.adoptShellPermissionIdentity(); |
| assertThrows(IllegalArgumentException.class, |
| () -> mUwbManager.elapsedRealtimeResolutionNanos("invalidChipId")); |
| } 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); |
| } |
| } |
| |
| @Test |
| public void testElapsedRealtimeResolutionNanosWithChipIdWithoutUwbPrivileged() { |
| try { |
| mUwbManager.elapsedRealtimeResolutionNanos(mDefaultChipId); |
| // 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 testAddServiceProfileWithoutUwbPrivileged() { |
| try { |
| mUwbManager.addServiceProfile(new PersistableBundle()); |
| // 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 testRemoveServiceProfileWithoutUwbPrivileged() { |
| try { |
| mUwbManager.removeServiceProfile(new PersistableBundle()); |
| // 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 testGetAllServiceProfilesWithoutUwbPrivileged() { |
| try { |
| mUwbManager.getAllServiceProfiles(); |
| // 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 testGetAdfProvisioningAuthoritiesWithoutUwbPrivileged() { |
| try { |
| mUwbManager.getAdfProvisioningAuthorities(new PersistableBundle()); |
| // 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 testGetAdfCertificateInfoWithoutUwbPrivileged() { |
| try { |
| mUwbManager.getAdfCertificateInfo(new PersistableBundle()); |
| // 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 testGetChipInfosWithoutUwbPrivileged() { |
| try { |
| mUwbManager.getChipInfos(); |
| // 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 testSendVendorUciWithoutUwbPrivileged() { |
| try { |
| mUwbManager.sendVendorUciMessage(10, 0, new byte[0]); |
| // 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 AdfProvisionStateCallback extends UwbManager.AdfProvisionStateCallback { |
| private final CountDownLatch mCountDownLatch; |
| |
| public boolean onSuccessCalled; |
| public boolean onFailedCalled; |
| |
| AdfProvisionStateCallback(@NonNull CountDownLatch countDownLatch) { |
| mCountDownLatch = countDownLatch; |
| } |
| |
| @Override |
| public void onProfileAdfsProvisioned(@NonNull PersistableBundle params) { |
| onSuccessCalled = true; |
| mCountDownLatch.countDown(); |
| } |
| |
| @Override |
| public void onProfileAdfsProvisionFailed(int reason, @NonNull PersistableBundle params) { |
| onFailedCalled = true; |
| mCountDownLatch.countDown(); |
| } |
| } |
| |
| @Test |
| public void testProvisionProfileAdfByScriptWithoutUwbPrivileged() { |
| CountDownLatch countDownLatch = new CountDownLatch(1); |
| AdfProvisionStateCallback adfProvisionStateCallback = |
| new AdfProvisionStateCallback(countDownLatch); |
| try { |
| mUwbManager.provisionProfileAdfByScript( |
| new PersistableBundle(), |
| Executors.newSingleThreadExecutor(), |
| adfProvisionStateCallback); |
| // 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 testRemoveProfileAdfWithoutUwbPrivileged() { |
| try { |
| mUwbManager.removeProfileAdf(new PersistableBundle()); |
| // 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 UwbVendorUciCallback implements UwbManager.UwbVendorUciCallback { |
| @Override |
| public void onVendorUciMessage(int gid, int oid, byte[] payload) { } |
| } |
| |
| @Test |
| public void testRegisterVendorUciCallbackWithoutUwbPrivileged() { |
| UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback(); |
| try { |
| mUwbManager.registerUwbVendorUciCallback( |
| Executors.newSingleThreadExecutor(), cb); |
| // 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 |
| @Ignore("Not implemented yet") |
| public void testUnregisterVendorUciCallbackWithoutUwbPrivileged() { |
| UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback(); |
| try { |
| mUwbManager.registerUwbVendorUciCallback( |
| Executors.newSingleThreadExecutor(), cb); |
| } catch (SecurityException e) { |
| /* pass */ |
| } |
| try { |
| mUwbManager.unregisterUwbVendorUciCallback(cb); |
| // 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 testInvalidCallbackUnregisterVendorUciCallback() { |
| UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback(); |
| try { |
| mUwbManager.registerUwbVendorUciCallback( |
| Executors.newSingleThreadExecutor(), cb); |
| } catch (SecurityException e) { |
| /* registration failed */ |
| } |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| try { |
| // Needs UWB_PRIVILEGED permission which is held by shell. |
| uiAutomation.adoptShellPermissionIdentity(); |
| mUwbManager.unregisterUwbVendorUciCallback(cb); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| } |
| |
| 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 testOpenRangingSessionWithInvalidChipId() { |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| 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. |
| assertThrows(IllegalArgumentException.class, () -> mUwbManager.openRangingSession( |
| new PersistableBundle(), |
| Executors.newSingleThreadExecutor(), |
| rangingSessionCallback, |
| "invalidChipId")); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| } |
| |
| @Test |
| public void testOpenRangingSessionWithChipIdWithBadParams() 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, |
| mDefaultChipId); |
| // 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(); |
| } |
| } |
| |
| @Test |
| public void testOpenRangingSessionWithInvalidChipIdWithBadParams() 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, |
| mDefaultChipId); |
| // 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(); |
| } |
| } |
| |
| @Test |
| public void testOpenRangingSessionWithChipIdWithoutUwbPrivileged() { |
| 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)), |
| mDefaultChipId); |
| // 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(); |
| } |
| } |
| |
| @Test |
| public void testOpenRangingSessionWithChipIdWithoutUwbRanging() { |
| 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)), |
| mDefaultChipId); |
| // 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(); |
| } |
| } |
| |
| @Test |
| public void testOpenRangingSessionWithChipIdWithoutUwbRangingInNextAttributeSource() { |
| 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)), |
| mDefaultChipId); |
| // 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 class AdapterStateCallback implements UwbManager.AdapterStateCallback { |
| private final CountDownLatch mCountDownLatch; |
| private final @State Integer mWaitForState; |
| public int state; |
| public int reason; |
| |
| AdapterStateCallback(@NonNull CountDownLatch countDownLatch, |
| @Nullable @State Integer waitForState) { |
| mCountDownLatch = countDownLatch; |
| mWaitForState = waitForState; |
| } |
| |
| public void onStateChanged(@State int state, @StateChangedReason int reason) { |
| this.state = state; |
| this.reason = reason; |
| if (mWaitForState != null) { |
| if (mWaitForState == state) { |
| mCountDownLatch.countDown(); |
| } |
| } else { |
| mCountDownLatch.countDown(); |
| } |
| } |
| } |
| |
| @Test |
| public void testUwbStateToggle() throws Exception { |
| UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); |
| try { |
| // Needs UWB_PRIVILEGED permission which is held by shell. |
| uiAutomation.adoptShellPermissionIdentity(); |
| assertThat(mUwbManager.isUwbEnabled()).isTrue(); |
| assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_ENABLED_INACTIVE); |
| // Toggle the state |
| setUwbEnabledAndWaitForCompletion(false); |
| assertThat(mUwbManager.getAdapterState()).isEqualTo(STATE_DISABLED); |
| } finally { |
| uiAutomation.dropShellPermissionIdentity(); |
| } |
| } |
| } |