A phone freezes an app it cannot see, and the front end had nothing holding it: a turn that took minutes -- a long shell command, a provider that thinks, a build -- stopped quietly the moment the user pressed home, and the answer was only there if the window happened to come back. The engine's own /attach flow made this visible: an APK turn survived, a pocket did not. - app/android/src/xyz/whcself/cstagent/CstService.java: a foreground service started with an explicit component (nothing outside the app can reach it, no intent filter) for as long as a turn runs. Being a foreground service makes the process one Android neither freezes nor kills; a partial wake lock (30 min ceiling) keeps the CPU awake with the screen off. Its ongoing notification says what is running -- "CST-agent is working" plus the first line of the prompt -- on the same cst-agent channel the notification_send tool uses, and taps back into the app. A phone that refuses the foreground start (Android 12+ does, from the background) loses the protection, not the turn: the failure is caught and the service stops itself. - Manifest: FOREGROUND_SERVICE, FOREGROUND_SERVICE_DATA_SYNC (API 34+ wants the typed one) and WAKE_LOCK, plus the <service> with foregroundServiceType="dataSync". The first build of this failed with "Expected '>', but got ' '" -- a manifest comment cannot contain "--". - Front end: app::holdForeground(text) / app::releaseForeground() in ToolProvider.h, one call for every platform -- Android starts and stops the service through JNI, a desktop has nothing to hold (its window is the app). AgentCore::send() calls it when the turn starts (firstLine() of the prompt is what the notification says) and finishTurn() releases it, so the notification exists exactly as long as the work does. CST_APP_NO_SERVICE=1 keeps a test run from touching the phone. - On the NX789J, with the app sent home and the screen off: the service read back isForeground=true with its notification, the wake lock cst-agent:turn was taken at the start of the turn and dropped at its end, run_shell "sleep 60" finished with the screen dark, and the service was gone after. Desktop: build clean, --uitest all green, --selftest and cst-app-selftest OK (the hold is a no-op there, so nothing about the Qt side changed). Version 0.9.4.
105 lines
5.0 KiB
XML
105 lines
5.0 KiB
XML
<?xml version="1.0"?>
|
|
<!-- The APK's manifest (QT_ANDROID_PACKAGE_SOURCE_DIR in app/CMakeLists.txt):
|
|
androiddeployqt copies this file and res/ into its gradle project and
|
|
fills in the INSERT_* placeholders below (version code and name, library
|
|
name, app arguments). An XML comment may not contain a double hyphen, so
|
|
the placeholder syntax is only spelled out in the attributes themselves.
|
|
|
|
The SDK levels are not here on purpose: they come from
|
|
QT_ANDROID_MIN_SDK_VERSION and QT_ANDROID_TARGET_SDK_VERSION in
|
|
app/CMakeLists.txt, and androiddeployqt writes them into the generated
|
|
gradle.properties. The app name and icon come from res/ next to this
|
|
file (values/strings.xml, drawable/ic_launcher.xml).
|
|
|
|
WHAT THIS APP IS: the engine runs in this process (no server, no port) and
|
|
writes everything (workspace, session store, settings, credentials) under
|
|
the app's own data directory, which main.cpp points the engine at (see
|
|
androidRoot() there).
|
|
|
|
Permissions: INTERNET for the providers the user configures,
|
|
POST_NOTIFICATIONS for the app's notification_send tool (Android 13+
|
|
asks at runtime; the tool stays hidden until it is granted), and the
|
|
foreground-service permissions with WAKE_LOCK for the service that keeps
|
|
a turn going while the phone is in a pocket (CstService.java). No
|
|
storage permission: the file picker is the system one (SAF) and its
|
|
input is copied into the app's directory. No cleartext traffic: every
|
|
provider URL is https. -->
|
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
package="xyz.whcself.cstagent"
|
|
android:installLocation="auto"
|
|
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
|
android:versionName="-- %%INSERT_VERSION_NAME%% --">
|
|
|
|
<!-- %%INSERT_PERMISSIONS -->
|
|
<!-- %%INSERT_FEATURES -->
|
|
|
|
<uses-permission android:name="android.permission.INTERNET" />
|
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
|
|
<supports-screens
|
|
android:anyDensity="true"
|
|
android:largeScreens="true"
|
|
android:normalScreens="true"
|
|
android:smallScreens="true" />
|
|
|
|
<application
|
|
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
|
android:label="@string/app_name"
|
|
android:icon="@drawable/ic_launcher"
|
|
android:allowBackup="true"
|
|
android:hardwareAccelerated="true"
|
|
android:enableOnBackInvokedCallback="false">
|
|
|
|
<activity
|
|
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
|
android:label="@string/app_name"
|
|
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|keyboard|keyboardHidden|navigation|density|mcc|mnc"
|
|
android:launchMode="singleTop"
|
|
android:screenOrientation="unspecified"
|
|
android:exported="true"
|
|
android:windowSoftInputMode="adjustResize">
|
|
|
|
<intent-filter>
|
|
<action android:name="android.intent.action.MAIN" />
|
|
<category android:name="android.intent.category.LAUNCHER" />
|
|
</intent-filter>
|
|
|
|
<meta-data
|
|
android:name="android.app.lib_name"
|
|
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
|
|
<meta-data
|
|
android:name="android.app.arguments"
|
|
android:value="-- %%INSERT_APP_ARGUMENTS%% --" />
|
|
</activity>
|
|
|
|
<!-- The share tool hands a file out of the workspace, and a file
|
|
crossing an app boundary has to travel as a content:// uri. This
|
|
is Qt's own provider (androidx.core.content.FileProvider, paths
|
|
from res/xml/qtprovider_paths.xml): its files-path is the app's
|
|
files directory, which is where the workspace lives. -->
|
|
<provider
|
|
android:name="androidx.core.content.FileProvider"
|
|
android:authorities="${applicationId}.qtprovider"
|
|
android:exported="false"
|
|
android:grantUriPermissions="true">
|
|
<meta-data
|
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
|
android:resource="@xml/qtprovider_paths" />
|
|
</provider>
|
|
|
|
<!-- A turn can outlive the screen: while one is running the front end
|
|
starts this service (AgentCore::send -> app::holdForeground), which
|
|
makes the process a foreground one, so Android neither freezes nor
|
|
kills it in the background, and takes a partial wake lock so the
|
|
CPU keeps going with the phone in a pocket. dataSync is the type
|
|
that fits "the app is finishing some work". -->
|
|
<service
|
|
android:name="xyz.whcself.cstagent.CstService"
|
|
android:exported="false"
|
|
android:foregroundServiceType="dataSync" />
|
|
</application>
|
|
</manifest>
|