Merge "Warn the user when a streamed app requests authentication" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
4ebdfbb31d
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user