summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java15
-rw-r--r--services/core/java/com/android/server/biometrics/AuthService.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java11
-rw-r--r--services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceMockingTest.java96
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java40
8 files changed, 222 insertions, 4 deletions
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index faaa2e8cc0f5..d4807482f8a5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5392,11 +5392,11 @@
<string name="app_streaming_blocked_message" product="tablet">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your tablet instead.</string>
<!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
<string name="app_streaming_blocked_message" product="default">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your phone instead.</string>
- <!-- Message shown when the fingerprint permission is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <!-- Message shown when an app being streamed to another device requests authentication such as via the biometrics API, and the user needs to complete the on their device. [CHAR LIMIT=NONE] -->
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv">This app is requesting additional security. Try on your Android TV device instead.</string>
- <!-- Message shown when the fingerprint permission is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <!-- Message shown when an app being streamed to another device requests authentication such as via the biometrics API, and the user needs to complete the on their device. [CHAR LIMIT=NONE] -->
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet">This app is requesting additional security. Try on your tablet instead.</string>
- <!-- Message shown when the fingerprint permission is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <!-- Message shown when an app being streamed to another device requests authentication such as via the biometrics API, and the user needs to complete the on their device. [CHAR LIMIT=NONE] -->
<string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default">This app is requesting additional security. Try on your phone instead.</string>
<!-- Message shown when the settings is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g>. Try on your Android TV device instead.</string>
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index ad4c0bf26d62..e9b9980b93eb 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -55,6 +55,7 @@ import android.util.SparseArray;
import android.view.Display;
import android.widget.Toast;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
@@ -156,7 +157,7 @@ public class VirtualDeviceManagerService extends SystemService {
VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
virtualDevice.showToastWhereUidIsRunning(appUid,
getContext().getString(
- com.android.internal.R.string.vdm_camera_access_denied,
+ R.string.vdm_camera_access_denied,
virtualDevice.getDisplayName()),
Toast.LENGTH_LONG, Looper.myLooper());
}
@@ -623,6 +624,18 @@ public class VirtualDeviceManagerService extends SystemService {
}
@Override
+ public void onAuthenticationPrompt(int uid) {
+ synchronized (mVirtualDeviceManagerLock) {
+ for (int i = 0; i < mVirtualDevices.size(); i++) {
+ VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
+ device.showToastWhereUidIsRunning(uid,
+ R.string.app_streaming_blocked_message_for_fingerprint_dialog,
+ Toast.LENGTH_LONG, Looper.getMainLooper());
+ }
+ }
+ }
+
+ @Override
public int getBaseVirtualDisplayFlags(IVirtualDevice virtualDevice) {
return ((VirtualDeviceImpl) virtualDevice).getBaseVirtualDisplayFlags();
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index f8cb9e98c714..4538cad513d6 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -70,6 +70,7 @@ import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.server.SystemService;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import java.util.ArrayList;
import java.util.Arrays;
@@ -262,6 +263,11 @@ public class AuthService extends SystemService {
final long identity = Binder.clearCallingIdentity();
try {
+ VirtualDeviceManagerInternal vdm = getLocalService(
+ VirtualDeviceManagerInternal.class);
+ if (vdm != null) {
+ vdm.onAuthenticationPrompt(callingUid);
+ }
return mBiometricService.authenticate(
token, sessionId, userId, receiver, opPackageName, promptInfo);
} finally {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index 6c26e2b0ce99..ece35c522ec7 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -91,6 +91,7 @@ import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21;
import com.android.server.biometrics.sensors.fingerprint.hidl.Fingerprint21UdfpsMock;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.google.android.collect.Lists;
@@ -329,6 +330,16 @@ public class FingerprintService extends SystemService {
return -1;
}
}
+ final long identity2 = Binder.clearCallingIdentity();
+ try {
+ VirtualDeviceManagerInternal vdm = getLocalService(
+ VirtualDeviceManagerInternal.class);
+ if (vdm != null) {
+ vdm.onAuthenticationPrompt(callingUid);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity2);
+ }
return provider.second.scheduleAuthenticate(token, operationId,
0 /* cookie */, new ClientMonitorCallbackConverter(receiver), options,
restricted, statsClient, isKeyguard);
diff --git a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
index a33533841e89..4d12574fb6b0 100644
--- a/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
+++ b/services/core/java/com/android/server/companion/virtual/VirtualDeviceManagerInternal.java
@@ -70,6 +70,11 @@ public abstract class VirtualDeviceManagerInternal {
public abstract void onAppsOnVirtualDeviceChanged();
/**
+ * Notifies that an authentication prompt is about to be shown for an app with the given uid.
+ */
+ public abstract void onAuthenticationPrompt(int uid);
+
+ /**
* Gets the owner uid for a deviceId.
*
* @param deviceId which device we're asking about
diff --git a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceMockingTest.java
new file mode 100644
index 000000000000..a8853071abe8
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceMockingTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2023 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.companion.virtual;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.testing.TestableContext;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.R;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class VirtualDeviceManagerServiceMockingTest {
+ private static final int UID_1 = 0;
+ private static final int DEVICE_ID_1 = 42;
+ private static final int DEVICE_ID_2 = 43;
+
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getContext());
+
+ private VirtualDeviceManagerService mVdms;
+ private VirtualDeviceManagerInternal mLocalService;
+
+ @Before
+ public void setUp() {
+ mVdms = new VirtualDeviceManagerService(mContext);
+ mLocalService = mVdms.getLocalServiceInstance();
+ }
+
+ @Test
+ public void onAuthenticationPrompt_noDevices_noCrash() {
+ // This should not crash
+ mLocalService.onAuthenticationPrompt(UID_1);
+ }
+
+ @Test
+ public void onAuthenticationPrompt_oneDevice_showToastWhereUidIsRunningIsCalled() {
+ VirtualDeviceImpl device = mock(VirtualDeviceImpl.class);
+ mVdms.addVirtualDevice(device);
+
+ mLocalService.onAuthenticationPrompt(UID_1);
+
+ verify(device).showToastWhereUidIsRunning(eq(UID_1),
+ eq(R.string.app_streaming_blocked_message_for_fingerprint_dialog), anyInt(),
+ any(Looper.class));
+ }
+
+ @Test
+ public void onAuthenticationPrompt_twoDevices_showToastWhereUidIsRunningIsCalledOnBoth() {
+ VirtualDeviceImpl device1 = mock(VirtualDeviceImpl.class);
+ VirtualDeviceImpl device2 = mock(VirtualDeviceImpl.class);
+ when(device1.getDeviceId()).thenReturn(DEVICE_ID_1);
+ when(device2.getDeviceId()).thenReturn(DEVICE_ID_2);
+ mVdms.addVirtualDevice(device1);
+ mVdms.addVirtualDevice(device2);
+
+ mLocalService.onAuthenticationPrompt(UID_1);
+
+ verify(device1).showToastWhereUidIsRunning(eq(UID_1),
+ eq(R.string.app_streaming_blocked_message_for_fingerprint_dialog), anyInt(),
+ any(Looper.class));
+ verify(device2).showToastWhereUidIsRunning(eq(UID_1),
+ eq(R.string.app_streaming_blocked_message_for_fingerprint_dialog), anyInt(),
+ any(Looper.class));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 85d159c25be2..439a6efa6f1f 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -58,6 +58,8 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.server.LocalServices;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import org.junit.Before;
import org.junit.Rule;
@@ -102,6 +104,8 @@ public class AuthServiceTest {
IFaceService mFaceService;
@Mock
AppOpsManager mAppOpsManager;
+ @Mock
+ private VirtualDeviceManagerInternal mVdmInternal;
@Captor
private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor;
@Captor
@@ -115,6 +119,8 @@ public class AuthServiceTest {
"1:4:15", // ID1:Iris:Strong
"2:8:15", // ID2:Face:Strong
};
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ LocalServices.addService(VirtualDeviceManagerInternal.class, mVdmInternal);
when(mResources.getIntArray(eq(R.array.config_udfps_sensor_props))).thenReturn(new int[0]);
when(mResources.getBoolean(eq(R.bool.config_is_powerbutton_fps))).thenReturn(false);
@@ -272,6 +278,47 @@ public class AuthServiceTest {
}
@Test
+ public void testAuthenticate_noVdmInternalService_noCrash() throws Exception {
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final Binder token = new Binder();
+
+ // This should not crash
+ mAuthService.mImpl.authenticate(
+ token,
+ 0, /* sessionId */
+ 0, /* userId */
+ mReceiver,
+ TEST_OP_PACKAGE_NAME,
+ new PromptInfo());
+ waitForIdle();
+ }
+
+ @Test
+ public void testAuthenticate_callsVirtualDeviceManagerOnAuthenticationPrompt()
+ throws Exception {
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final Binder token = new Binder();
+
+ mAuthService.mImpl.authenticate(
+ token,
+ 0, /* sessionId */
+ 0, /* userId */
+ mReceiver,
+ TEST_OP_PACKAGE_NAME,
+ new PromptInfo());
+ waitForIdle();
+
+ ArgumentCaptor<Integer> uidCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mVdmInternal).onAuthenticationPrompt(uidCaptor.capture());
+ assertEquals((int) (uidCaptor.getValue()), Binder.getCallingUid());
+ }
+
+ @Test
public void testAuthenticate_throwsWhenUsingTestConfigurations() {
final PromptInfo promptInfo = mock(PromptInfo.class);
when(promptInfo.containsPrivateApiConfigurations()).thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
index 1089c07e6787..9ae56b1ed2f8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java
@@ -27,6 +27,8 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFP
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.assertEquals;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -44,6 +46,7 @@ import android.hardware.fingerprint.FingerprintAuthenticateOptions;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.os.Binder;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
@@ -54,8 +57,10 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.util.test.FakeSettingsProviderRule;
+import com.android.server.LocalServices;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider;
+import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import org.junit.Before;
import org.junit.Rule;
@@ -103,6 +108,8 @@ public class FingerprintServiceTest {
private IFingerprintServiceReceiver mServiceReceiver;
@Mock
private IBinder mToken;
+ @Mock
+ private VirtualDeviceManagerInternal mVdmInternal;
@Captor
private ArgumentCaptor<FingerprintAuthenticateOptions> mAuthenticateOptionsCaptor;
@@ -125,6 +132,9 @@ public class FingerprintServiceTest {
@Before
public void setup() throws Exception {
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ LocalServices.addService(VirtualDeviceManagerInternal.class, mVdmInternal);
+
when(mFingerprintDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault));
when(mFingerprintVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual));
when(mFingerprintDefault.containsSensor(anyInt()))
@@ -225,6 +235,36 @@ public class FingerprintServiceTest {
verifyNoAuthenticate(mFingerprintVirtual);
}
+ @Test
+ public void testAuthenticate_noVdmInternalService_noCrash() throws Exception {
+ initServiceWithAndWait(NAME_DEFAULT, NAME_VIRTUAL);
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+
+ final long operationId = 2;
+
+ // This should not crash
+ mService.mServiceWrapper.authenticate(mToken, operationId, mServiceReceiver,
+ new FingerprintAuthenticateOptions.Builder()
+ .setSensorId(SENSOR_ID_ANY)
+ .build());
+ }
+
+ @Test
+ public void testAuthenticate_callsVirtualDeviceManagerOnAuthenticationPrompt()
+ throws Exception {
+ initServiceWithAndWait(NAME_DEFAULT, NAME_VIRTUAL);
+
+ final long operationId = 2;
+ mService.mServiceWrapper.authenticate(mToken, operationId, mServiceReceiver,
+ new FingerprintAuthenticateOptions.Builder()
+ .setSensorId(SENSOR_ID_ANY)
+ .build());
+
+ ArgumentCaptor<Integer> uidCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mVdmInternal).onAuthenticationPrompt(uidCaptor.capture());
+ assertEquals((int) (uidCaptor.getValue()), Binder.getCallingUid());
+ }
+
private FingerprintAuthenticateOptions verifyAuthenticateWithNewRequestId(
FingerprintProvider provider, long operationId) {