AccessibilityService 如何利用其他应用程序?
How does AccessibilityService draw on top of other apps?
背景
SAW(系统警报window)权限可用于在其他应用程序之上绘制内容。
很久以前有人告诉我无障碍服务也可以做到这一点,但我从未找到任何教程、示例、文档甚至应用程序来做到这一点……直到最近:
https://play.google.com/store/apps/details?id=com.github.ericytsang.screenfilter.app.android
事实上,这个应用程序似乎可以随处绘制,而不是 SAW 权限。它甚至在设置应用程序和系统对话框之上绘制,而 SAW 权限不允许这样。
问题
遗憾的是,正如我所写的那样,可访问性是一种非常独特且很少使用的东西,我找不到这样的东西如何在其他应用程序之上绘图。
我发现了什么
我唯一知道的是这个应用程序以某种方式做到了,这是它在要求授予它时显示的内容:
但这还不够。我知道对于我使用辅助功能服务制作的一些旧 POC,它显示相同,并且检查它,我看不出是什么触发了它。可以肯定的是,对于任何类型的无障碍服务,用户都会看到最少的东西,所以这无济于事。
问题
AccessibilityService 如何利用其他应用程序?
和SAW权限一样吗?例如,你能处理它绘制的触摸事件吗?
如果有使用限制是什么?
从无障碍服务中添加视图时,在 WindowManager.LayoutParams
中使用 TYPE_ACCESSIBILITY_OVERLAY 作为 type
似乎可以解决问题。我做了一个快速测试,甚至在设置菜单中也显示了覆盖 window。叠加层 window 也接收到触摸事件。这在清单中没有 SYSTEM_ALERT_WINDOW
权限的情况下也有效,也没有用户以交互方式设置“在其他应用程序上显示”权限。我使用目标 SDK 29 进行了测试。
抱歉,我无法回答您关于适用哪些具体限制的第三个问题。
编辑:通过查看 Google here 的旧教程,这里有一个简短的示例:
GlobalActionBarService.java
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
@Override
protected void onServiceConnected() {
// Create an overlay and display the action bar
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
mLayout = new FrameLayout(this);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
lp.format = PixelFormat.TRANSLUCENT;
lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.TOP;
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.action_bar, mLayout);
wm.addView(mLayout, lp);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
清单
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="com.lb.myapplication">
<application
android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"
android:theme="@style/Theme.MyApplication" tools:ignore="AllowBackup">
<activity
android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".GlobalActionBarService" android:exported="false"
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/global_action_bar_service" />
</service>
</application>
</manifest>
global_action_bar_service.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault"
android:canPerformGestures="true" android:canRetrieveWindowContent="true" />
action_bar.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content" android:orientation="horizontal">
<Button
android:id="@+id/power" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/power" />
<Button
android:id="@+id/volume_up" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/volume" />
<Button
android:id="@+id/scroll" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/scroll" />
<Button
android:id="@+id/swipe" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/swipe" />
</LinearLayout>
答案是:您必须使用 WindowManager 将视图放入服务中的 window 以将其绘制在其他应用程序之上。
val typeApplication =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
else
WindowManager.LayoutParams.TYPE_PHONE
val layoutParams = WindowManager.LayoutParams(
windowWidth,
ViewGroup.LayoutParams.WRAP_CONTENT,
typeApplication,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
// inflater view with above layoutParams variable
并且应用程序授予了覆盖权限,并确保在设备设置中启用了辅助功能(设置-> 辅助功能-> 启用您的应用程序)。或者用这个意向去吧
Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
。您可以通过以下代码查看Overlay权限
// check
Settings.canDrawOverlays(applicationContext)
// Use this intent to enable permision
Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName"))
和SAW权限一样吗?例如,你能处理它绘制的触摸事件吗?
我以前从来没有用过SAW,所以我不确定这个问题。
希望能帮到你!
背景
SAW(系统警报window)权限可用于在其他应用程序之上绘制内容。
很久以前有人告诉我无障碍服务也可以做到这一点,但我从未找到任何教程、示例、文档甚至应用程序来做到这一点……直到最近:
https://play.google.com/store/apps/details?id=com.github.ericytsang.screenfilter.app.android
事实上,这个应用程序似乎可以随处绘制,而不是 SAW 权限。它甚至在设置应用程序和系统对话框之上绘制,而 SAW 权限不允许这样。
问题
遗憾的是,正如我所写的那样,可访问性是一种非常独特且很少使用的东西,我找不到这样的东西如何在其他应用程序之上绘图。
我发现了什么
我唯一知道的是这个应用程序以某种方式做到了,这是它在要求授予它时显示的内容:
但这还不够。我知道对于我使用辅助功能服务制作的一些旧 POC,它显示相同,并且检查它,我看不出是什么触发了它。可以肯定的是,对于任何类型的无障碍服务,用户都会看到最少的东西,所以这无济于事。
问题
AccessibilityService 如何利用其他应用程序?
和SAW权限一样吗?例如,你能处理它绘制的触摸事件吗?
如果有使用限制是什么?
从无障碍服务中添加视图时,在 WindowManager.LayoutParams
中使用 TYPE_ACCESSIBILITY_OVERLAY 作为 type
似乎可以解决问题。我做了一个快速测试,甚至在设置菜单中也显示了覆盖 window。叠加层 window 也接收到触摸事件。这在清单中没有 SYSTEM_ALERT_WINDOW
权限的情况下也有效,也没有用户以交互方式设置“在其他应用程序上显示”权限。我使用目标 SDK 29 进行了测试。
抱歉,我无法回答您关于适用哪些具体限制的第三个问题。
编辑:通过查看 Google here 的旧教程,这里有一个简短的示例:
GlobalActionBarService.java
public class GlobalActionBarService extends AccessibilityService {
FrameLayout mLayout;
@Override
protected void onServiceConnected() {
// Create an overlay and display the action bar
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
mLayout = new FrameLayout(this);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
lp.format = PixelFormat.TRANSLUCENT;
lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.TOP;
LayoutInflater inflater = LayoutInflater.from(this);
inflater.inflate(R.layout.action_bar, mLayout);
wm.addView(mLayout, lp);
}
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
清单
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="com.lb.myapplication">
<application
android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true"
android:theme="@style/Theme.MyApplication" tools:ignore="AllowBackup">
<activity
android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".GlobalActionBarService" android:exported="false"
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/global_action_bar_service" />
</service>
</application>
</manifest>
global_action_bar_service.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault"
android:canPerformGestures="true" android:canRetrieveWindowContent="true" />
action_bar.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content" android:orientation="horizontal">
<Button
android:id="@+id/power" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/power" />
<Button
android:id="@+id/volume_up" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/volume" />
<Button
android:id="@+id/scroll" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/scroll" />
<Button
android:id="@+id/swipe" android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="@string/swipe" />
</LinearLayout>
答案是:您必须使用 WindowManager 将视图放入服务中的 window 以将其绘制在其他应用程序之上。
val typeApplication =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
else
WindowManager.LayoutParams.TYPE_PHONE
val layoutParams = WindowManager.LayoutParams(
windowWidth,
ViewGroup.LayoutParams.WRAP_CONTENT,
typeApplication,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
)
// inflater view with above layoutParams variable
并且应用程序授予了覆盖权限,并确保在设备设置中启用了辅助功能(设置-> 辅助功能-> 启用您的应用程序)。或者用这个意向去吧
Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
。您可以通过以下代码查看Overlay权限
// check
Settings.canDrawOverlays(applicationContext)
// Use this intent to enable permision
Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:$packageName"))
和SAW权限一样吗?例如,你能处理它绘制的触摸事件吗? 我以前从来没有用过SAW,所以我不确定这个问题。
希望能帮到你!