当前位置: 首页 > news >正文

【SystemUI】启动屏幕录制会自动开启投屏

一、问题描述

基于 Android 14平台,下拉快速设置中打开屏幕录制,在 3 秒倒计时完成后,屏幕录制 Tile 变成 STATE_ACTIVE 状态,同时投屏 Tile 也变成STATE_ACTIVE 状态,会造成功能混乱的感觉,需要修改功能单独控制。

在这里插入图片描述

https://issuetracker.google.com/issues/328539170

二、问题分析

这个问题的核心在于,系统无法区分屏幕录制发起的 MediaProjection(屏幕内容捕获请求)和真正的投屏发起的 MediaProjection。当屏幕录制开始时,CastController (投屏控制器) 错误地认为一个投屏会话已经开始,因此点亮了投屏磁贴。投屏发起者一般是其他应用,包名不是 com.android.systemui

ScreenRecordTile -> RecordingController -> ScreenRecordDialog

开始录制点击事件

src/com/android/systemui/screenrecord/ScreenRecordDialog.java

TextView startBtn = findViewById(R.id.button_start);
startBtn.setOnClickListener(v -> {if (mOnStartRecordingClicked != null) {// Note that it is important to run this callback before dismissing, so that the// callback can disable the dialog exit animation if it wants to.mOnStartRecordingClicked.run();}// Start full-screen recordingrequestScreenCapture(/* captureTarget= */ null);dismiss();
});

开启屏幕录制请求

/*** Starts screen capture after some countdown* @param captureTarget target to capture (could be e.g. a task) or*                      null to record the whole screen*/
private void requestScreenCapture(@Nullable MediaProjectionCaptureTarget captureTarget) {Context userContext = mUserContextProvider.getUserContext();boolean showTaps = mTapsSwitch.isChecked();ScreenRecordingAudioSource audioMode = mAudioSwitch.isChecked()? (ScreenRecordingAudioSource) mOptions.getSelectedItem(): NONE;PendingIntent startIntent = PendingIntent.getForegroundService(userContext,RecordingService.REQUEST_CODE,RecordingService.getStartIntent(userContext, Activity.RESULT_OK,audioMode.ordinal(), showTaps, captureTarget),PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);PendingIntent stopIntent = PendingIntent.getService(userContext,RecordingService.REQUEST_CODE,RecordingService.getStopIntent(userContext),PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);mController.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent);
}

src/com/android/systemui/screenrecord/RecordingService.java

/*** Get an intent to start the recording service.** @param context    Context from the requesting activity* @param resultCode The result code from {@link android.app.Activity#onActivityResult(int, int,*                   android.content.Intent)}* @param audioSource   The ordinal value of the audio source*                      {@link com.android.systemui.screenrecord.ScreenRecordingAudioSource}* @param showTaps   True to make touches visible while recording* @param captureTarget   pass this parameter to capture a specific part instead*                        of the full screen*/
public static Intent getStartIntent(Context context, int resultCode,int audioSource, boolean showTaps,@Nullable MediaProjectionCaptureTarget captureTarget) {return new Intent(context, RecordingService.class).setAction(ACTION_START).putExtra(EXTRA_RESULT_CODE, resultCode).putExtra(EXTRA_AUDIO_SOURCE, audioSource).putExtra(EXTRA_SHOW_TAPS, showTaps).putExtra(EXTRA_CAPTURE_TARGET, captureTarget);
}

倒计时 3 秒后开始录制执行 startIntent

try {startIntent.send(mInteractiveBroadcastOption);mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);IntentFilter stateFilter = new IntentFilter(INTENT_UPDATE_STATE);mBroadcastDispatcher.registerReceiver(mStateChangeReceiver, stateFilter, null,UserHandle.ALL);Log.d(TAG, "sent start intent");
} catch (PendingIntent.CanceledException e) {Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
}

RecordingService 执行具体的录制流程

@Override
public int onStartCommand(Intent intent, int flags, int startId) {if (intent == null) {return Service.START_NOT_STICKY;}String action = intent.getAction();Log.d(TAG, "onStartCommand " + action);NotificationChannel channel = new NotificationChannel(CHANNEL_ID,getString(R.string.screenrecord_title),NotificationManager.IMPORTANCE_DEFAULT);channel.setDescription(getString(R.string.screenrecord_channel_description));channel.enableVibration(true);mNotificationManager.createNotificationChannel(channel);int currentUserId = mUserContextTracker.getUserContext().getUserId();UserHandle currentUser = new UserHandle(currentUserId);switch (action) {case ACTION_START:// Get a unique ID for this recording's notificationsmNotificationId = NOTIF_BASE_ID + (int) SystemClock.uptimeMillis();mAudioSource = ScreenRecordingAudioSource.values()[intent.getIntExtra(EXTRA_AUDIO_SOURCE, 0)];Log.d(TAG, "recording with audio source " + mAudioSource);mShowTaps = intent.getBooleanExtra(EXTRA_SHOW_TAPS, false);MediaProjectionCaptureTarget captureTarget =intent.getParcelableExtra(EXTRA_CAPTURE_TARGET,MediaProjectionCaptureTarget.class);mOriginalShowTaps = Settings.System.getInt(getApplicationContext().getContentResolver(),Settings.System.SHOW_TOUCHES, 0) != 0;setTapsVisible(mShowTaps);mRecorder = new ScreenMediaRecorder(mUserContextTracker.getUserContext(),mMainHandler,currentUserId,mAudioSource,captureTarget,this);if (startRecording()) {updateState(true);createRecordingNotification();mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START);} else {updateState(false);createErrorNotification();stopForeground(STOP_FOREGROUND_DETACH);stopSelf();return Service.START_NOT_STICKY;}break;...
}

三、解决方案

在投屏启动前根据包名进行拦截处理

src/com/android/systemui/statusbar/policy/CastControllerImpl.java

private final MediaProjectionManager.Callback mProjectionCallback= new MediaProjectionManager.Callback() {@Overridepublic void onStart(MediaProjectionInfo info) {+ if (info != null && "com.android.systemui".equals(info.getPackageName())) {+     if (DEBUG) Log.d(TAG, "Ignoring projection from screen recording (com.android.systemui)");+     return;+ }setProjection(info, true);}@Overridepublic void onStop(MediaProjectionInfo info) {setProjection(info, false);}
};
http://www.dtcms.com/a/354241.html

相关文章:

  • 主流配置中心对比
  • 百度测试岗位--面试真题分析
  • JS逆向-反调试绕过事件检测无限Debug篡改猴Hook替换指向匹配修改条件断点
  • 泊松分布知识点讲解
  • Android WPS Office 18.20
  • 【Win软件 - 系统 - 网络】 Windows怎么禁止某个应用联网
  • 洛谷P13849 [CERC 2023] Equal Schedules题解
  • python接口自动化测试报告插件使用
  • CSS扩大点击热区示例
  • 明远智睿 RK3506 核心板:高集成度与强实时性的嵌入式解决方案
  • 【小白笔记】Hugging Face 下载:Git 到镜像网站的
  • 4-3.Python 数据容器 - 集合 set(集合 set 概述、集合的定义、集合的遍历、集合的常用方法)
  • Yolo系列 —— 使用自制数据集训练yolo模型
  • 2021/07 JLPT听力原文 问题一 1番
  • MQTT broker 安装与基础配置实战指南(一)
  • Java:IO流——增强篇
  • anaconda本身有一个python环境(base),想用别的环境就是用anaconda命令行往anaconda里创建虚拟环境
  • 英伟达 spectrum xgs 以太网 的含义和解释
  • 互联网大厂AI面试:从大模型原理到场景应用的深度解析
  • WPF 程序用户权限模块利用MarkupExtension实现控制控件显示
  • 嵌入式分层架构下的AT指令模块设计与实现
  • 使用Nginx搭建图片传输服务:配置与优化指南
  • Content-Type是application/x-www-form-urlencoded表示从前端到后端提交的是表单的形式
  • 微服务的编程测评系统17-判题功能-代码沙箱
  • 除自身以外数组的乘积是什么意思
  • 算法刷题常见错误
  • Linux 打包及压缩基础知识总结
  • 车间生产管理遇到的问题及改善方案有哪些?
  • 在 Windows 上部署 Go 语言开发环境
  • Go语言与Docker 开发的核心应用领域