Merge "Warn the user when a streamed app requests authentication" into udc-dev

This commit is contained in:
Antony Sargent
2023-05-11 19:52:41 +00:00
committed by Android (Google) Code Review
8 changed files with 222 additions and 4 deletions

View File

@@ -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>

View File

@@ -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());
}
@@ -622,6 +623,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();

View File

@@ -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 {

View File

@@ -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);

View File

@@ -69,6 +69,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.
*

View File

@@ -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));
}
}

View File

@@ -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);
@@ -271,6 +277,47 @@ public class AuthServiceTest {
verify(mReceiver).onError(eq(TYPE_NONE), eq(BIOMETRIC_ERROR_CANCELED), anyInt());
}
@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);

View File

@@ -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) {