Android后台检测是否有窗口设置LayoutParams.FLAG_SECURE的方法
检测前台窗口是否设置了FLAG_SECURE的正确姿势
嘿,你之前的代码踩了个小坑——你用Display.FLAG_SECURE来判断窗口的安全标志,这完全不对!这个标志是用来标记整个显示设备是否需要防截屏/录屏的全局设置,和单个窗口的WindowManager.LayoutParams.FLAG_SECURE半毛钱关系都没有,所以它才会一直返回true。
要准确检测某个窗口是否设置了FLAG_SECURE,得针对单个窗口的LayoutParams来判断,但这里要注意Android的权限和版本限制,下面给你两种可行的方案:
方案1:用AccessibilityService(后台检测首选)
AccessibilityService是目前后台检测其他窗口状态最可靠的方式,不过需要用户手动给你的应用开启辅助功能权限。
第一步:配置AccessibilityService
先在AndroidManifest.xml里注册服务:
<service android:name=".MySecureWindowCheckerService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_config" /> </service>
然后在res/xml/accessibility_config.xml里写配置:
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeWindowStateChanged" android:accessibilityFeedbackType="feedbackGeneric" android:canRetrieveWindowContent="true" android:description="@string/accessibility_desc" android:accessibilityFlags="flagIncludeNotImportantViews" />
(记得在strings.xml里加accessibility_desc,说明你的服务用途,比如“检测窗口是否设置了安全保护标志”)
第二步:实现检测逻辑
写一个继承AccessibilityService的类,监听窗口切换事件,拿到当前窗口的LayoutParams:
public class MySecureWindowCheckerService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { // 只处理窗口切换事件 if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) { return; } AccessibilityNodeInfo rootNode = getRootInActiveWindow(); if (rootNode == null) { return; } // 从窗口节点的 extras 里取出LayoutParams WindowManager.LayoutParams windowParams = (WindowManager.LayoutParams) rootNode.getExtras().get("android.view.WindowManager.LayoutParams"); if (windowParams != null) { boolean isSecure = (windowParams.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0; Log.d("WindowSecureCheck", "当前窗口是否设置FLAG_SECURE: " + isSecure); // 这里可以把结果传递给你的后台逻辑 } rootNode.recycle(); // 别忘了回收节点,避免内存泄漏 } @Override public void onInterrupt() { // 服务被中断时的处理,比如打个log Log.d("WindowSecureCheck", "辅助服务被中断"); } }
方案2:用UsageStatsManager(局限性大,仅低版本可用)
如果你只是想知道前台应用的情况,可以先通过UsageStatsManager拿到前台包名,但注意:Android 10+后台根本拿不到其他应用的窗口LayoutParams,这个方案只能作为低版本的补充。
第一步:申请权限
在Manifest里加权限:
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" tools:ignore="ProtectedPermissions" />
然后引导用户到设置页面授权:
private void requestUsageStatsPermission(Context context) { Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS); context.startActivity(intent); }
第二步:获取前台包名(但无法直接检测窗口FLAG_SECURE)
public String getForegroundPackage(Context context) { UsageStatsManager statsManager = (UsageStatsManager) context.getSystemService(Context.USAGE_STATS_SERVICE); long now = System.currentTimeMillis(); // 查询最近1小时的应用使用记录 List<UsageStats> statsList = statsManager.queryUsageStats( UsageStatsManager.INTERVAL_HOURLY, now - 3600000, now); if (statsList == null || statsList.isEmpty()) { return null; } String foregroundPkg = null; long lastActiveTime = 0; for (UsageStats stats : statsList) { if (stats.getLastTimeUsed() > lastActiveTime) { lastActiveTime = stats.getLastTimeUsed(); foregroundPkg = stats.getPackageName(); } } return foregroundPkg; }
但还是那句话,拿到包名也没用,高版本后台根本拿不到别人的窗口参数。
重要提醒
- Android 10(API 29)及以上:后台应用想获取其他应用的窗口信息,几乎只有AccessibilityService这一条路,而且必须用户手动授权辅助功能。
- 系统应用除外:如果你的应用是系统级别的,那可以用更直接的方法,但一般第三方应用没这个权限。
内容的提问来源于stack exchange,提问作者CamHart
相关产品推荐
相关产品推荐

