Merge "Addition of native handoff of an intercepted intent matched according to a provided IntentFilter."

This commit is contained in:
Shaun Corkran
2022-12-16 09:37:16 +00:00
committed by Android (Google) Code Review
12 changed files with 346 additions and 10 deletions

View File

@@ -2991,6 +2991,10 @@ package android.companion.virtual {
method public void onTopActivityChanged(int, @NonNull android.content.ComponentName);
}
public static interface VirtualDeviceManager.IntentInterceptorCallback {
method public void onIntentIntercepted(@NonNull android.content.Intent);
}
public static class VirtualDeviceManager.VirtualDevice implements java.lang.AutoCloseable {
method public void addActivityListener(@NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
@@ -3009,8 +3013,10 @@ package android.companion.virtual {
method public int getDeviceId();
method @Nullable public android.companion.virtual.sensor.VirtualSensor getVirtualSensor(int, @NonNull String);
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.IntConsumer);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void registerIntentInterceptor(@NonNull java.util.concurrent.Executor, @NonNull android.content.IntentFilter, @NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void unregisterIntentInterceptor(@NonNull android.companion.virtual.VirtualDeviceManager.IntentInterceptorCallback);
}
public final class VirtualDeviceParams implements android.os.Parcelable {

View File

@@ -17,11 +17,13 @@
package android.companion.virtual;
import android.app.PendingIntent;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
import android.companion.virtual.sensor.IVirtualSensorStateChangeCallback;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.IntentFilter;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.input.VirtualDpadConfig;
@@ -125,4 +127,15 @@ interface IVirtualDevice {
/** Sets whether to show or hide the cursor while this virtual device is active. */
void setShowPointerIcon(boolean showPointerIcon);
/**
* Registers an intent interceptor that will intercept an intent attempting to launch
* when matching the provided IntentFilter and calls the callback with the intercepted
* intent.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)")
void registerIntentInterceptor(
in IVirtualDeviceIntentInterceptor intentInterceptor, in IntentFilter filter);
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)")
void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2022 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.companion.virtual;
import android.content.Intent;
/**
* Interceptor interface to be called when an intent matches the IntentFilter passed into {@link
* VirtualDevice#registerIntentInterceptor}. When the interceptor is called after matching the
* IntentFilter, the intended activity launch will be aborted and alternatively replaced by
* the interceptor's receiver.
*
* @hide
*/
oneway interface IVirtualDeviceIntentInterceptor {
/**
* Called when an intent that matches the IntentFilter registered in {@link
* VirtualDevice#registerIntentInterceptor} is intercepted for the virtual device to
* handle.
*
* @param intent The intent that has been intercepted by the interceptor.
*/
void onIntentIntercepted(in Intent intent);
}

View File

@@ -35,6 +35,8 @@ import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Point;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.display.DisplayManager;
@@ -269,6 +271,9 @@ public final class VirtualDeviceManager {
private final IVirtualDevice mVirtualDevice;
private final ArrayMap<ActivityListener, ActivityListenerDelegate> mActivityListeners =
new ArrayMap<>();
private final ArrayMap<IntentInterceptorCallback,
VirtualIntentInterceptorDelegate> mIntentInterceptorListeners =
new ArrayMap<>();
private final IVirtualDeviceActivityListener mActivityListenerBinder =
new IVirtualDeviceActivityListener.Stub() {
@@ -857,6 +862,53 @@ public final class VirtualDeviceManager {
public void removeActivityListener(@NonNull ActivityListener listener) {
mActivityListeners.remove(listener);
}
/**
* Registers an intent interceptor that will intercept an intent attempting to launch
* when matching the provided IntentFilter and calls the callback with the intercepted
* intent.
*
* @param executor The executor where the interceptor is executed on.
* @param interceptorFilter The filter to match intents intended for interception.
* @param interceptorCallback The callback called when an intent matching interceptorFilter
* is intercepted.
* @see #unregisterIntentInterceptor(IntentInterceptorCallback)
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void registerIntentInterceptor(
@CallbackExecutor @NonNull Executor executor,
@NonNull IntentFilter interceptorFilter,
@NonNull IntentInterceptorCallback interceptorCallback) {
Objects.requireNonNull(executor);
Objects.requireNonNull(interceptorFilter);
Objects.requireNonNull(interceptorCallback);
final VirtualIntentInterceptorDelegate delegate =
new VirtualIntentInterceptorDelegate(executor, interceptorCallback);
try {
mVirtualDevice.registerIntentInterceptor(delegate, interceptorFilter);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mIntentInterceptorListeners.put(interceptorCallback, delegate);
}
/**
* Unregisters the intent interceptorCallback previously registered with
* {@link #registerIntentInterceptor}.
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void unregisterIntentInterceptor(
@NonNull IntentInterceptorCallback interceptorCallback) {
Objects.requireNonNull(interceptorCallback);
final VirtualIntentInterceptorDelegate delegate =
mIntentInterceptorListeners.get(interceptorCallback);
try {
mVirtualDevice.unregisterIntentInterceptor(delegate);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mIntentInterceptorListeners.remove(interceptorCallback);
}
}
/**
@@ -908,4 +960,51 @@ public final class VirtualDeviceManager {
mExecutor.execute(() -> mActivityListener.onDisplayEmpty(displayId));
}
}
/**
* Interceptor interface to be called when an intent matches the IntentFilter passed into {@link
* VirtualDevice#registerIntentInterceptor}. When the interceptor is called after matching the
* IntentFilter, the intended activity launch will be aborted and alternatively replaced by
* the interceptor's receiver.
*
* @hide
*/
@SystemApi
public interface IntentInterceptorCallback {
/**
* Called when an intent that matches the IntentFilter registered in {@link
* VirtualDevice#registerIntentInterceptor} is intercepted for the virtual device to
* handle.
*
* @param intent The intent that has been intercepted by the interceptor.
*/
void onIntentIntercepted(@NonNull Intent intent);
}
/**
* A wrapper for {@link IntentInterceptorCallback} that executes callbacks on the
* the given executor.
*/
private static class VirtualIntentInterceptorDelegate
extends IVirtualDeviceIntentInterceptor.Stub {
@NonNull private final IntentInterceptorCallback mIntentInterceptorCallback;
@NonNull private final Executor mExecutor;
private VirtualIntentInterceptorDelegate(Executor executor,
IntentInterceptorCallback interceptorCallback) {
mExecutor = executor;
mIntentInterceptorCallback = interceptorCallback;
}
@Override
public void onIntentIntercepted(Intent intent) {
final long token = Binder.clearCallingIdentity();
try {
mExecutor.execute(() -> mIntentInterceptorCallback.onIntentIntercepted(intent));
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
}

View File

@@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import android.annotation.NonNull;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.util.ArraySet;
@@ -114,7 +115,7 @@ public abstract class DisplayWindowPolicyController {
/**
* Returns {@code true} if the given new task can be launched on this virtual display.
*/
public abstract boolean canActivityBeLaunched(@NonNull ActivityInfo activityInfo,
public abstract boolean canActivityBeLaunched(@NonNull ActivityInfo activityInfo, Intent intent,
@WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
boolean isNewTask);

View File

@@ -32,6 +32,7 @@ import android.companion.virtual.VirtualDeviceParams.RecentsPolicy;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Handler;
@@ -93,6 +94,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
void onEnteringPipBlocked(int uid);
}
/** Interface to listen for interception of intents. */
public interface IntentListenerCallback {
/** Returns true when an intent should be intercepted */
boolean shouldInterceptIntent(Intent intent);
}
/**
* If required, allow the secure activity to display on remote device since
* {@link android.os.Build.VERSION_CODES#TIRAMISU}.
@@ -121,6 +128,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
final ArraySet<Integer> mRunningUids = new ArraySet<>();
@Nullable private final ActivityListener mActivityListener;
@Nullable private final PipBlockedCallback mPipBlockedCallback;
@Nullable private final IntentListenerCallback mIntentListenerCallback;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
@@ -155,6 +163,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
* launching.
* @param secureWindowCallback Callback that is called when a secure window shows on the
* virtual display.
* @param intentListenerCallback Callback that is called to intercept intents when matching
* passed in filters.
* @param defaultRecentsPolicy a policy to indicate how to handle activities in recents.
*/
public GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
@@ -168,6 +178,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@NonNull PipBlockedCallback pipBlockedCallback,
@NonNull ActivityBlockedCallback activityBlockedCallback,
@NonNull SecureWindowCallback secureWindowCallback,
@NonNull IntentListenerCallback intentListenerCallback,
@NonNull List<String> displayCategories,
@RecentsPolicy int defaultRecentsPolicy) {
super();
@@ -182,6 +193,7 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
mActivityListener = activityListener;
mPipBlockedCallback = pipBlockedCallback;
mSecureWindowCallback = secureWindowCallback;
mIntentListenerCallback = intentListenerCallback;
mDisplayCategories = displayCategories;
mDefaultRecentsPolicy = defaultRecentsPolicy;
}
@@ -227,8 +239,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
@Override
public boolean canActivityBeLaunched(ActivityInfo activityInfo,
@WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
boolean isNewTask) {
Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
int launchingFromDisplayId, boolean isNewTask) {
if (!isWindowingModeSupported(windowingMode)) {
return false;
}
@@ -261,6 +273,12 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController
return false;
}
if (mIntentListenerCallback != null && intent != null
&& mIntentListenerCallback.shouldInterceptIntent(intent)) {
Slog.d(TAG, "Virtual device has intercepted intent");
return false;
}
return true;
}

View File

@@ -33,6 +33,7 @@ import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDevice;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.VirtualDeviceManager;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.companion.virtual.VirtualDeviceParams;
@@ -43,6 +44,7 @@ import android.companion.virtual.sensor.VirtualSensorEvent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.graphics.PointF;
import android.hardware.display.DisplayManager;
@@ -114,6 +116,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
private final VirtualDeviceParams mParams;
private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
private final IVirtualDeviceActivityListener mActivityListener;
@GuardedBy("mVirtualDeviceLock")
private final Map<IBinder, IntentFilter> mIntentInterceptors = new ArrayMap<>();
@NonNull
private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback;
// The default setting for showing the pointer on new displays.
@@ -680,6 +684,31 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
@Override // Binder call
public void registerIntentInterceptor(IVirtualDeviceIntentInterceptor intentInterceptor,
IntentFilter filter) {
Objects.requireNonNull(intentInterceptor);
Objects.requireNonNull(filter);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"Permission required to register intent interceptor");
synchronized (mVirtualDeviceLock) {
mIntentInterceptors.put(intentInterceptor.asBinder(), filter);
}
}
@Override // Binder call
public void unregisterIntentInterceptor(
@NonNull IVirtualDeviceIntentInterceptor intentInterceptor) {
Objects.requireNonNull(intentInterceptor);
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"Permission required to unregister intent interceptor");
synchronized (mVirtualDeviceLock) {
mIntentInterceptors.remove(intentInterceptor.asBinder());
}
}
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
fout.println(" VirtualDevice: ");
@@ -713,6 +742,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
this::onEnteringPipBlocked,
this::onActivityBlocked,
this::onSecureWindowShown,
this::shouldInterceptIntent,
displayCategories,
mParams.getDefaultRecentsPolicy());
gwpc.registerRunningAppsChangedListener(/* listener= */ this);
@@ -872,6 +902,34 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
Toast.LENGTH_LONG, mContext.getMainLooper());
}
/**
* Intercepts intent when matching any of the IntentFilter of any interceptor. Returns true if
* the intent matches any filter notifying the DisplayPolicyController to abort the
* activity launch to be replaced by the interception.
*/
private boolean shouldInterceptIntent(Intent intent) {
synchronized (mVirtualDeviceLock) {
boolean hasInterceptedIntent = false;
for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) {
if (interceptor.getValue().match(
intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
intent.getCategories(), TAG) >= 0) {
try {
// For privacy reasons, only returning the intents action and data. Any
// other required field will require a review.
IVirtualDeviceIntentInterceptor.Stub.asInterface(interceptor.getKey())
.onIntentIntercepted(new Intent(intent.getAction(), intent.getData()));
hasInterceptedIntent = true;
} catch (RemoteException e) {
Slog.w(TAG, "Unable to call mVirtualDeviceIntentInterceptor", e);
}
}
}
return hasInterceptedIntent;
}
}
interface OnDeviceCloseListener {
void onClose(int deviceId);
}

View File

@@ -1829,8 +1829,8 @@ class ActivityStarter {
final int launchingFromDisplayId =
mSourceRecord != null ? mSourceRecord.getDisplayId() : DEFAULT_DISPLAY;
if (!displayContent.mDwpcHelper
.canActivityBeLaunched(r.info, targetWindowingMode, launchingFromDisplayId,
newTask)) {
.canActivityBeLaunched(r.info, r.intent, targetWindowingMode,
launchingFromDisplayId, newTask)) {
Slog.w(TAG, "Abort to launch " + r.info.getComponentName()
+ " on display area " + mPreferredTaskDisplayArea);
return START_ABORTED;

View File

@@ -19,6 +19,7 @@ package com.android.server.wm;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -91,8 +92,8 @@ class DisplayWindowPolicyControllerHelper {
* @see DisplayWindowPolicyController#canActivityBeLaunched(ActivityInfo, int, int, boolean)
*/
public boolean canActivityBeLaunched(ActivityInfo activityInfo,
@WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
boolean isNewTask) {
Intent intent, @WindowConfiguration.WindowingMode int windowingMode,
int launchingFromDisplayId, boolean isNewTask) {
if (mDisplayWindowPolicyController == null) {
if (activityInfo.requiredDisplayCategory != null) {
Slog.e(TAG,
@@ -104,8 +105,8 @@ class DisplayWindowPolicyControllerHelper {
}
return true;
}
return mDisplayWindowPolicyController.canActivityBeLaunched(activityInfo, windowingMode,
launchingFromDisplayId, isNewTask);
return mDisplayWindowPolicyController.canActivityBeLaunched(activityInfo, intent,
windowingMode, launchingFromDisplayId, isNewTask);
}
/**

View File

@@ -21,6 +21,7 @@ import static android.companion.virtual.VirtualDeviceManager.DEVICE_ID_INVALID;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_CUSTOM;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES;
import static com.google.common.truth.Truth.assertThat;
@@ -37,6 +38,7 @@ import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -49,6 +51,7 @@ import android.app.WindowConfiguration;
import android.app.admin.DevicePolicyManager;
import android.companion.AssociationInfo;
import android.companion.virtual.IVirtualDeviceActivityListener;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
@@ -57,6 +60,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.hardware.Sensor;
@@ -73,6 +77,7 @@ import android.hardware.input.VirtualNavigationTouchpadConfig;
import android.hardware.input.VirtualTouchEvent;
import android.hardware.input.VirtualTouchscreenConfig;
import android.net.MacAddress;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -184,6 +189,7 @@ public class VirtualDeviceManagerServiceTest {
.setInputDeviceName(DEVICE_NAME)
.setAssociatedDisplayId(DISPLAY_ID)
.build();
private static final String TEST_SITE = "http://test";
private Context mContext;
private InputManagerMockHelper mInputManagerMockHelper;
@@ -1339,6 +1345,99 @@ public class VirtualDeviceManagerServiceTest {
assertThat(gwpc.getRunningAppsChangedListenersSizeForTesting()).isEqualTo(0);
}
@Test
public void canActivityBeLaunched_activityCanLaunch() {
Intent intent = new Intent(ACTION_VIEW, Uri.parse(TEST_SITE));
mDeviceImpl.onVirtualDisplayCreatedLocked(
mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
/* displayOnRemoveDevices */ true,
/* targetDisplayCategory */ null);
assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
.isTrue();
}
@Test
public void canActivityBeLaunched_intentInterceptedWhenRegistered_activityNoLaunch()
throws RemoteException {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(TEST_SITE));
IVirtualDeviceIntentInterceptor.Stub interceptor =
mock(IVirtualDeviceIntentInterceptor.Stub.class);
doNothing().when(interceptor).onIntentIntercepted(any());
doReturn(interceptor).when(interceptor).asBinder();
doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
mDeviceImpl.onVirtualDisplayCreatedLocked(
mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
/* displayOnRemoveDevices */ true,
/* targetDisplayCategory */ null);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
intentFilter.addDataScheme(IntentFilter.SCHEME_HTTP);
intentFilter.addDataScheme(IntentFilter.SCHEME_HTTPS);
// register interceptor and intercept intent
mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter);
assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
.isFalse();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(interceptor).onIntentIntercepted(intentCaptor.capture());
Intent cIntent = intentCaptor.getValue();
assertThat(cIntent).isNotNull();
assertThat(cIntent.getAction()).isEqualTo(Intent.ACTION_VIEW);
assertThat(cIntent.getData().toString()).isEqualTo(TEST_SITE);
// unregister interceptor and launch activity
mDeviceImpl.unregisterIntentInterceptor(interceptor);
assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
.isTrue();
}
@Test
public void canActivityBeLaunched_noMatchIntentFilter_activityLaunches()
throws RemoteException {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("testing"));
IVirtualDeviceIntentInterceptor.Stub interceptor =
mock(IVirtualDeviceIntentInterceptor.Stub.class);
doNothing().when(interceptor).onIntentIntercepted(any());
doReturn(interceptor).when(interceptor).asBinder();
doReturn(interceptor).when(interceptor).queryLocalInterface(anyString());
mDeviceImpl.onVirtualDisplayCreatedLocked(
mDeviceImpl.createWindowPolicyController(new ArrayList<>()), DISPLAY_ID);
GenericWindowPolicyController gwpc = mDeviceImpl.getWindowPolicyControllersForTesting().get(
DISPLAY_ID);
ArrayList<ActivityInfo> activityInfos = getActivityInfoList(
NONBLOCKED_APP_PACKAGE_NAME,
NONBLOCKED_APP_PACKAGE_NAME,
/* displayOnRemoveDevices */ true,
/* targetDisplayCategory */ null);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_VIEW);
intentFilter.addDataScheme("mailto");
// register interceptor with different filter
mDeviceImpl.registerIntentInterceptor(interceptor, intentFilter);
assertThat(gwpc.canActivityBeLaunched(activityInfos.get(0), intent,
WindowConfiguration.WINDOWING_MODE_FULLSCREEN, DISPLAY_ID, /*isNewTask=*/false))
.isTrue();
}
@Test
public void nonRestrictedActivityOnRestrictedVirtualDisplay_startBlockedAlertActivity() {
Intent blockedAppIntent = createRestrictedActivityBlockedIntent(List.of("abc"),

View File

@@ -85,6 +85,7 @@ public class VirtualAudioControllerTest {
/* pipBlockedCallback= */ null,
/* activityBlockedCallback= */ null,
/* secureWindowCallback= */ null,
/* intentListenerCallback= */ null,
/* displayCategories= */ new ArrayList<>(),
/* recentsPolicy= */
VirtualDeviceParams.RECENTS_POLICY_ALLOW_IN_HOST_DEVICE_RECENTS);

View File

@@ -31,6 +31,7 @@ import static org.mockito.Mockito.mock;
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -227,7 +228,7 @@ public class DisplayWindowPolicyControllerTests extends WindowTestsBase {
ArraySet<Integer> mRunningUids = new ArraySet<>();
@Override
public boolean canActivityBeLaunched(@NonNull ActivityInfo activity,
public boolean canActivityBeLaunched(@NonNull ActivityInfo activity, Intent intent,
@WindowConfiguration.WindowingMode int windowingMode, int launchingFromDisplayId,
boolean isNewTask) {
return false;