如何让第三方Android应用仅在虚拟屏幕启动而不显示于物理屏幕?
问题描述
目标
希望运行第三方Android应用,但不在物理屏幕上显示。
条件
- 拥有完整的系统源码(AOSP)
- 具备root权限
尝试操作
尝试创建虚拟屏幕,并请求应用在虚拟屏幕上显示(以下代码以android.uid.system身份运行)
public static android.hardware.display.VirtualDisplay createDisplay( Context context, Surface surface, String name, int height, int width, int dpi ) { DisplayManager displayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); android.hardware.display.VirtualDisplay display = displayManager.createVirtualDisplay( name, width, height, dpi, surface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC | DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT | DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH | /*DisplayManager. VIRTUAL_DISPLAY_FLAG_OWN_FOCUS*/ 1 << 14, ); return display; } public static void startApplication(Context context, Display display, String packageName) { Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); ActivityOptions options = ActivityOptions.makeBasic() .setLaunchDisplayId(display.getDisplayId()); context.startActivity(intent, options.toBundle()); }
但应用仍会显示在物理屏幕上。
解决方案
1. 确保虚拟屏幕的Surface有效
传入的Surface如果为null或未正确初始化,系统会自动回退到物理屏幕显示。可以创建空的SurfaceTexture生成有效Surface:
// 创建空的SurfaceTexture并设置缓冲区大小 SurfaceTexture surfaceTexture = new SurfaceTexture(0); surfaceTexture.setDefaultBufferSize(width, height); Surface surface = new Surface(surfaceTexture); // 使用该Surface创建虚拟屏幕 VirtualDisplay display = createDisplay(context, surface, name, height, width, dpi);
2. 调整虚拟屏幕的创建参数
- 移除
VIRTUAL_DISPLAY_FLAG_PRESENTATION:该标记针对演示类应用,可能导致系统优先选择物理屏 - 直接使用
VIRTUAL_DISPLAY_FLAG_OWN_FOCUS常量,避免硬编码1 <<14的版本兼容问题 - 移除
VIRTUAL_DISPLAY_FLAG_PUBLIC:公共虚拟屏易被系统识别为辅助显示,而非独立显示
修正后的参数配置:
int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT | DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS; VirtualDisplay display = displayManager.createVirtualDisplay( name, width, height, dpi, surface, flags );
3. 强制将应用任务移至虚拟屏幕
若启动时的setLaunchDisplayId不生效,可在应用启动后通过ActivityManager强制迁移任务:
public static void moveAppToDisplay(Context context, String packageName, int displayId) { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(Integer.MAX_VALUE); for (ActivityManager.RunningTaskInfo task : tasks) { if (task.baseActivity.getPackageName().equals(packageName)) { am.moveTaskToDisplay(task.id, displayId); break; } } }
调用逻辑:先启动应用,延迟300-500毫秒后调用该方法(确保应用任务已创建)。
4. 系统层面修改(基于AOSP源码)
若上述方法仍无效,可修改系统源码实现强制限制:
- 在
ActivityStackSupervisor中添加逻辑,根据目标应用包名强制将其任务分配到指定虚拟屏 - 修改
DisplayManagerService,标记目标虚拟屏为该应用的专属显示
内容的提问来源于stack exchange,提问作者forDream
相关产品推荐
相关产品推荐

