The place where random ideas get written down and lost in time.
2024-09-15 - Analysis: DroidVNC-NG VNC Server
Category DEVhttps://play.google.com/store/apps/details?id=net.christianbeier.droidvnc_ng
and
https://github.com/bk138/droidVNC-NG
There are 2 things that interest me here:
- This app auto-starts using the Accessibility API. I want to understand that API so that I can use it in TCM, since Android 14 does not support the old “Boot Receiver” method.
- What are the steps needed to embed a VNC server in an app?
- A use case would be TCM sharing its own view, without access to the entire tablet. Is it worth it to even consider that, or just install DroidVNC-NG on the side?
Accessibility Service API
https://developer.android.com/guide/topics/ui/accessibility/service
What it is:
- Android 8 (API 26) and above.
- A service, declared in the manifest.
- It can listen to events, and inspect the view hierarchy.
- “Explore by Touch”: respond to gestures by interacting with screen elements.
- AccessibilityService#performGlobalAction to perform an action.
- AccessibilityService#getSystemActions to figure available actions.
It doesn’t seem like an AccessibilityService could start an app directly per se.
However it could potentially use GLOBAL_ACTION_HOME to invoke the Home app, then explore the hierarchy and click on the relevant app icon to trigger it. It’s not clear whether the service can do that on its own; it’s likely it needs to respond to a user gesture first.
ACTION_BOOT_COMPLETED and RECEIVE_BOOT_COMPLETED
The debate is up on whether an app can still “receive” ACTION_BOOT_COMPLETED.
The documentation in Permission.RECEIVE_BOOT_COMPLETED indicates it is still sent.
The problem is what can be done with it:
https://developer.android.com/about/versions/15/behavior-changes-15#fgs-boot-completed clearly indicates that the action can no longer start some types of foreground services, and also I don’t see it directly documented as such the problem in RTAC and TCM was that I cannot run a start activity anymore from the boot receiver.
This is what logcat looks like:
onReceive: intent=Intent { act=android.intent.action.BOOT_COMPLETED flg=0x89000010 cmp=com.alflabs.tcm/.app.BootReceiver (has extras) }
ActivityTaskManager system_server I START u0 {flg=0x10000000 cmp=com.alflabs.tcm/.activity.MainActivity} from uid 10151
Background activity start [callingPackage: com.alflabs.tcm; callingUid: 10151; appSwitchState: 2; isCallingUidForeground: false; callingUidHasAnyVisibleWindow: false; callingUidProcState: RECEIVER; isCallingUidPersistentSystemProcess: false; realCallingUid: 10151; isRealCallingUidForeground: false; realCallingUidHasAnyVisibleWindow: false; realCallingUidProcState: RECEIVER; isRealCallingUidPersistentSystemProcess: false; originatingPendingIntent: null; allowBackgroundActivityStart: false; intent: Intent { flg=0x10000000 cmp=com.alflabs.tcm/.activity.MainActivity }; callerApp: ProcessRecord{e668429 4343:com.alflabs.tcm/u0a151}; inVisibleTask: false]
ActivityTaskManager system_server E Abort background activity starts from 10151
The one in DroidVNC is not different -- they actually just start a background service.
Android Accessibility Service
https://developer.android.com/reference/android/accessibilityservice/AccessibilityService
- Requires BIND_ACCESSIBILITY_SERVICE permission.
- Create a service with that permission + an intent filter AccessibilityService
- Configure the service using a meta-data AccessibilityService XML.
In DroidVNC case:
<service
android:name=".InputService"
android:label="@string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/input_service_config"/>
</service>
There are 2 versions of xml/input_service_config: one in xml/ and one in xml-v30/.
xml/:
<?xml version="1.0" encoding="utf-8"?>
<!-- accessibilityFeedbackType seems needed pre API 30 so onAccessibilityEvent() triggers,
but it seems we don't need flagRetrieveInteractiveWindows -->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeViewFocused|typeViewClicked|typeViewSelected"
android:accessibilityFlags="flagDefault|flagIncludeNotImportantViews"
android:notificationTimeout="30"
android:description="@string/input_a11y_service_description"
android:canPerformGestures="true"
android:canRetrieveWindowContent="true"
android:accessibilityFeedbackType="feedbackVisual"
/>
and xml-v30/:
<?xml version="1.0" encoding="utf-8"?>
<!-- On API 30 and later flagRetrieveInteractiveWindows canRetrieveWindowContent
are needed for onAccessibilityEvent() to trigger -->
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeViewFocused|typeViewClicked|typeViewSelected"
android:accessibilityFlags=
"flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews"
android:notificationTimeout="30"
android:description="@string/input_a11y_service_description"
android:canPerformGestures="true"
android:canTakeScreenshot="true"
android:canRetrieveWindowContent="true"
/>
Bottom line ⇒ This cannot be used to start the activity.
TL;DR: DroidVNC uses the Boot Receiver event, which does work in Android 14. The only difference is that it’s not possible to start a foreground activity. But in their case they don’t do that, they just run the background service.