Merge changes from topic "revert-3408863-TXTIFSOPUR" into main
* changes:
Revert "libvmclient: Take VmCallback in VmInstance::start()"
Revert "libavf: Add callback support"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 8cb01d7..5dd060a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -49,6 +49,12 @@
},
{
"name": "vm_accessor_test"
+ },
+ {
+ "name": "avf_backcompat_tests"
+ },
+ {
+ "name": "old_images_avf_test"
}
],
"avf-postsubmit": [
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 59f18df..2bac412 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -11,12 +11,17 @@
asset_dirs: ["assets"],
resource_dirs: ["res"],
static_libs: [
+ // TODO(b/330257000): will be removed when binder RPC is used
+ "android.system.virtualizationservice_internal-java",
"androidx-constraintlayout_constraintlayout",
"androidx.window_window",
"apache-commons-compress",
+ "avf_aconfig_flags_java",
"com.google.android.material_material",
"debian-service-grpclib-lite",
"gson",
+ // TODO(b/331708504): will be removed when AVF framework handles surface
+ "libcrosvm_android_display_service-java",
"VmTerminalApp.aidl-java",
"MicrodroidTestHelper", // for DeviceProperties class
],
@@ -36,6 +41,7 @@
//optimize: true,
proguard_flags_files: ["proguard.flags"],
shrink_resources: true,
+ keep_runtime_invisible_annotations: true,
},
apex_available: [
"com.android.virt",
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index 726004c..c11b1a0 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -35,7 +35,8 @@
android:theme="@style/VmTerminalAppTheme"
android:usesCleartextTraffic="true"
android:supportsRtl="true"
- android:enabled="false">
+ android:enabled="false"
+ android:name=".Application">
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode|screenLayout|smallestScreenSize"
android:exported="true">
@@ -46,6 +47,11 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name=".DisplayActivity"
+ android:screenOrientation="landscape"
+ android:resizeableActivity="false"
+ android:theme="@style/FullscreenTheme"
+ android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode|screenLayout|smallestScreenSize" />
<activity android:name=".SettingsActivity"
android:label="@string/action_settings" />
<activity android:name=".SettingsDiskResizeActivity"
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/Application.kt b/android/TerminalApp/java/com/android/virtualization/terminal/Application.kt
new file mode 100644
index 0000000..efe651e
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/Application.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.virtualization.terminal
+
+import android.app.Application as AndroidApplication
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.content.Context
+
+public class Application : AndroidApplication() {
+ override fun onCreate() {
+ super.onCreate()
+ setupNotificationChannels()
+ }
+
+ private fun setupNotificationChannels() {
+ val nm = getSystemService<NotificationManager>(NotificationManager::class.java)
+
+ nm.createNotificationChannel(
+ NotificationChannel(
+ CHANNEL_LONG_RUNNING_ID,
+ getString(R.string.notification_channel_long_running_name),
+ NotificationManager.IMPORTANCE_DEFAULT,
+ )
+ )
+
+ nm.createNotificationChannel(
+ NotificationChannel(
+ CHANNEL_SYSTEM_EVENTS_ID,
+ getString(R.string.notification_channel_system_events_name),
+ NotificationManager.IMPORTANCE_HIGH,
+ )
+ )
+ }
+
+ companion object {
+ const val CHANNEL_LONG_RUNNING_ID = "long_running"
+ const val CHANNEL_SYSTEM_EVENTS_ID = "system_events"
+
+ fun getInstance(c: Context): Application = c.getApplicationContext() as Application
+ }
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.kt
index e7ac8d9..70bc5e4 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/BaseActivity.kt
@@ -16,8 +16,6 @@
package com.android.virtualization.terminal
import android.Manifest
-import android.app.NotificationChannel
-import android.app.NotificationManager
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
@@ -25,18 +23,6 @@
abstract class BaseActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- val notificationManager =
- getSystemService<NotificationManager>(NotificationManager::class.java)
- if (notificationManager.getNotificationChannel(this.packageName) == null) {
- val channel =
- NotificationChannel(
- this.packageName,
- getString(R.string.app_name),
- NotificationManager.IMPORTANCE_HIGH,
- )
- notificationManager.createNotificationChannel(channel)
- }
-
if (this !is ErrorActivity) {
val currentThread = Thread.currentThread()
if (currentThread.uncaughtExceptionHandler !is TerminalExceptionHandler) {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/DisplayActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/DisplayActivity.kt
new file mode 100644
index 0000000..290cf5a
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/DisplayActivity.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.virtualization.terminal
+
+import android.os.Bundle
+import android.system.virtualmachine.VirtualMachineManager
+import android.view.SurfaceView
+import android.view.WindowInsets
+import android.view.WindowInsetsController
+
+class DisplayActivity : BaseActivity() {
+ private lateinit var displayProvider: DisplayProvider
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_display)
+ val mainView = findViewById<SurfaceView>(R.id.surface_view)
+ val cursorView = findViewById<SurfaceView>(R.id.cursor_surface_view)
+ makeFullscreen()
+ // Connect the views to the VM
+ displayProvider = DisplayProvider(mainView, cursorView)
+ val vmm =
+ applicationContext.getSystemService<VirtualMachineManager>(
+ VirtualMachineManager::class.java
+ )
+ val debianVm = vmm.get("debian")
+ if (debianVm != null) {
+ InputForwarder(
+ this,
+ debianVm,
+ findViewById(R.id.background_touch_view),
+ findViewById(R.id.surface_view),
+ findViewById(R.id.surface_view),
+ )
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ displayProvider.notifyDisplayIsGoingToInvisible()
+ }
+
+ private fun makeFullscreen() {
+ val w = window
+ w.setDecorFitsSystemWindows(false)
+ val insetsCtrl = w.insetsController
+ insetsCtrl?.hide(WindowInsets.Type.systemBars())
+ insetsCtrl?.setSystemBarsBehavior(
+ WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+ )
+ }
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/DisplayProvider.kt b/android/TerminalApp/java/com/android/virtualization/terminal/DisplayProvider.kt
new file mode 100644
index 0000000..a04e056
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/DisplayProvider.kt
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.virtualization.terminal
+
+import android.crosvm.ICrosvmAndroidDisplayService
+import android.graphics.PixelFormat
+import android.os.ParcelFileDescriptor
+import android.os.RemoteException
+import android.os.ServiceManager
+import android.system.virtualizationservice_internal.IVirtualizationServiceInternal
+import android.util.Log
+import android.view.SurfaceControl
+import android.view.SurfaceHolder
+import android.view.SurfaceView
+import com.android.virtualization.terminal.DisplayProvider.CursorHandler
+import com.android.virtualization.terminal.MainActivity.Companion.TAG
+import java.io.IOException
+import java.lang.Exception
+import java.lang.RuntimeException
+import java.nio.ByteBuffer
+import java.nio.ByteOrder
+import libcore.io.IoBridge
+
+/** Provides Android-side surface from given SurfaceView to a VM instance as a display for that */
+internal class DisplayProvider(
+ private val mainView: SurfaceView,
+ private val cursorView: SurfaceView,
+) {
+ private val virtService: IVirtualizationServiceInternal by lazy {
+ val b = ServiceManager.waitForService("android.system.virtualizationservice")
+ IVirtualizationServiceInternal.Stub.asInterface(b)
+ }
+ private var cursorHandler: CursorHandler? = null
+
+ init {
+ mainView.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT)
+ mainView.holder.addCallback(Callback(SurfaceKind.MAIN))
+ cursorView.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT)
+ cursorView.holder.addCallback(Callback(SurfaceKind.CURSOR))
+ cursorView.holder.setFormat(PixelFormat.RGBA_8888)
+ // TODO: do we need this z-order?
+ cursorView.setZOrderMediaOverlay(true)
+ }
+
+ fun notifyDisplayIsGoingToInvisible() {
+ // When the display is going to be invisible (by putting in the background), save the frame
+ // of the main surface so that we can re-draw it next time the display becomes visible. This
+ // is to save the duration of time where nothing is drawn by VM.
+ try {
+ getDisplayService().saveFrameForSurface(false /* forCursor */)
+ } catch (e: RemoteException) {
+ throw RuntimeException("Failed to save frame for the main surface", e)
+ }
+ }
+
+ @Synchronized
+ private fun getDisplayService(): ICrosvmAndroidDisplayService {
+ try {
+ val b = virtService.waitDisplayService()
+ return ICrosvmAndroidDisplayService.Stub.asInterface(b)
+ } catch (e: Exception) {
+ throw RuntimeException("Error while getting display service", e)
+ }
+ }
+
+ enum class SurfaceKind {
+ MAIN,
+ CURSOR,
+ }
+
+ inner class Callback(private val surfaceKind: SurfaceKind) : SurfaceHolder.Callback {
+ fun isForCursor(): Boolean {
+ return surfaceKind == SurfaceKind.CURSOR
+ }
+
+ override fun surfaceCreated(holder: SurfaceHolder) {
+ try {
+ getDisplayService().setSurface(holder.getSurface(), isForCursor())
+ } catch (e: Exception) {
+ // TODO: don't consume this exception silently. For some unknown reason, setSurface
+ // call above throws IllegalArgumentException and that fails the surface
+ // configuration.
+ Log.e(TAG, "Failed to present surface $surfaceKind to VM", e)
+ }
+ try {
+ when (surfaceKind) {
+ SurfaceKind.MAIN -> getDisplayService().drawSavedFrameForSurface(isForCursor())
+ SurfaceKind.CURSOR -> {
+ val stream = createNewCursorStream()
+ getDisplayService().setCursorStream(stream)
+ }
+ }
+ } catch (e: Exception) {
+ // TODO: don't consume exceptions here too
+ Log.e(TAG, "Failed to configure surface $surfaceKind", e)
+ }
+ }
+
+ override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
+ // TODO: support resizeable display. We could actually change the display size that the
+ // VM sees, or keep the size and render it by fitting it in the new surface.
+ }
+
+ override fun surfaceDestroyed(holder: SurfaceHolder) {
+ try {
+ getDisplayService().removeSurface(isForCursor())
+ } catch (e: RemoteException) {
+ throw RuntimeException("Error while destroying surface for $surfaceKind", e)
+ }
+ }
+ }
+
+ private fun createNewCursorStream(): ParcelFileDescriptor? {
+ cursorHandler?.interrupt()
+ var pfds: Array<ParcelFileDescriptor> =
+ try {
+ ParcelFileDescriptor.createSocketPair()
+ } catch (e: IOException) {
+ throw RuntimeException("Failed to create socketpair for cursor stream", e)
+ }
+ cursorHandler = CursorHandler(pfds[0]).also { it.start() }
+ return pfds[1]
+ }
+
+ /**
+ * Thread reading cursor coordinate from a stream, and updating the position of the cursor
+ * surface accordingly.
+ */
+ private inner class CursorHandler(private val stream: ParcelFileDescriptor) : Thread() {
+ private val cursor: SurfaceControl = this@DisplayProvider.cursorView.surfaceControl
+ private val transaction: SurfaceControl.Transaction = SurfaceControl.Transaction()
+
+ init {
+ val main = this@DisplayProvider.mainView.surfaceControl
+ transaction.reparent(cursor, main).apply()
+ }
+
+ override fun run() {
+ try {
+ val byteBuffer = ByteBuffer.allocate(8 /* (x: u32, y: u32) */)
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
+ while (true) {
+ if (interrupted()) {
+ Log.d(TAG, "CursorHandler thread interrupted!")
+ return
+ }
+ byteBuffer.clear()
+ val bytes =
+ IoBridge.read(
+ stream.fileDescriptor,
+ byteBuffer.array(),
+ 0,
+ byteBuffer.array().size,
+ )
+ if (bytes == -1) {
+ Log.e(TAG, "cannot read from cursor stream, stop the handler")
+ return
+ }
+ val x = (byteBuffer.getInt() and -0x1).toFloat()
+ val y = (byteBuffer.getInt() and -0x1).toFloat()
+ transaction.setPosition(cursor, x, y).apply()
+ }
+ } catch (e: IOException) {
+ Log.e(TAG, "failed to run CursorHandler", e)
+ }
+ }
+ }
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InputForwarder.kt b/android/TerminalApp/java/com/android/virtualization/terminal/InputForwarder.kt
new file mode 100644
index 0000000..117ce94
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InputForwarder.kt
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.virtualization.terminal
+
+import android.content.Context
+import android.hardware.input.InputManager
+import android.os.Handler
+import android.system.virtualmachine.VirtualMachine
+import android.util.Log
+import android.view.InputDevice
+import android.view.KeyEvent
+import android.view.MotionEvent
+import android.view.View
+import com.android.virtualization.terminal.MainActivity.Companion.TAG
+
+/** Forwards input events (touch, mouse, ...) from Android to VM */
+internal class InputForwarder(
+ private val context: Context,
+ vm: VirtualMachine,
+ touchReceiver: View,
+ mouseReceiver: View,
+ keyReceiver: View,
+) {
+ private val virtualMachine: VirtualMachine = vm
+ private var inputDeviceListener: InputManager.InputDeviceListener? = null
+ private var isTabletMode = false
+
+ init {
+ val config = vm.config.customImageConfig
+
+ checkNotNull(config)
+
+ if (config.useTouch() == true) {
+ setupTouchReceiver(touchReceiver)
+ }
+ if (config.useMouse() || config.useTrackpad()) {
+ setupMouseReceiver(mouseReceiver)
+ }
+ if (config.useKeyboard()) {
+ setupKeyReceiver(keyReceiver)
+ }
+ if (config.useSwitches()) {
+ // Any view's handler is fine.
+ setupTabletModeHandler(touchReceiver.getHandler())
+ }
+ }
+
+ fun cleanUp() {
+ if (inputDeviceListener != null) {
+ val im = context.getSystemService<InputManager>(InputManager::class.java)
+ im.unregisterInputDeviceListener(inputDeviceListener)
+ inputDeviceListener = null
+ }
+ }
+
+ private fun setupTouchReceiver(receiver: View) {
+ receiver.setOnTouchListener(
+ View.OnTouchListener { v: View?, event: MotionEvent? ->
+ virtualMachine.sendMultiTouchEvent(event)
+ }
+ )
+ }
+
+ private fun setupMouseReceiver(receiver: View) {
+ receiver.requestUnbufferedDispatch(InputDevice.SOURCE_ANY)
+ receiver.setOnCapturedPointerListener { v: View?, event: MotionEvent? ->
+ val eventSource = event!!.source
+ if ((eventSource and InputDevice.SOURCE_CLASS_POSITION) != 0) {
+ return@setOnCapturedPointerListener virtualMachine.sendTrackpadEvent(event)
+ }
+ virtualMachine.sendMouseEvent(event)
+ }
+ }
+
+ private fun setupKeyReceiver(receiver: View) {
+ receiver.setOnKeyListener { v: View?, code: Int, event: KeyEvent? ->
+ // TODO: this is guest-os specific. It shouldn't be handled here.
+ if (isVolumeKey(code)) {
+ return@setOnKeyListener false
+ }
+ virtualMachine.sendKeyEvent(event)
+ }
+ }
+
+ private fun setupTabletModeHandler(handler: Handler?) {
+ val im = context.getSystemService<InputManager?>(InputManager::class.java)
+ inputDeviceListener =
+ object : InputManager.InputDeviceListener {
+ override fun onInputDeviceAdded(deviceId: Int) {
+ setTabletModeConditionally()
+ }
+
+ override fun onInputDeviceRemoved(deviceId: Int) {
+ setTabletModeConditionally()
+ }
+
+ override fun onInputDeviceChanged(deviceId: Int) {
+ setTabletModeConditionally()
+ }
+ }
+ im!!.registerInputDeviceListener(inputDeviceListener, handler)
+ }
+
+ fun setTabletModeConditionally() {
+ val tabletModeNeeded = !hasPhysicalKeyboard()
+ if (tabletModeNeeded != isTabletMode) {
+ val mode = if (tabletModeNeeded) "tablet mode" else "desktop mode"
+ Log.d(TAG, "switching to $mode")
+ isTabletMode = tabletModeNeeded
+ virtualMachine.sendTabletModeEvent(tabletModeNeeded)
+ }
+ }
+
+ companion object {
+ private fun isVolumeKey(keyCode: Int): Boolean {
+ return keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+ keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
+ keyCode == KeyEvent.KEYCODE_VOLUME_MUTE
+ }
+
+ private fun hasPhysicalKeyboard(): Boolean {
+ for (id in InputDevice.getDeviceIds()) {
+ val d = InputDevice.getDevice(id)
+ if (!d!!.isVirtual && d.isEnabled && d.isFullKeyboard) {
+ return true
+ }
+ }
+ return false
+ }
+ }
+}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.kt
index 423d66b..7180e87 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstallerService.kt
@@ -71,7 +71,7 @@
PendingIntent.FLAG_IMMUTABLE,
)
notification =
- Notification.Builder(this, this.packageName)
+ Notification.Builder(this, Application.CHANNEL_LONG_RUNNING_ID)
.setSilent(true)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(getString(R.string.installer_notif_title_text))
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
index bf2f573..71f10f9 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
@@ -56,6 +56,7 @@
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import com.android.internal.annotations.VisibleForTesting
import com.android.microdroid.test.common.DeviceProperties
+import com.android.system.virtualmachine.flags.Flags.terminalGuiSupport
import com.android.virtualization.terminal.CertificateUtils.createOrGetKey
import com.android.virtualization.terminal.CertificateUtils.writeCertificateToFile
import com.android.virtualization.terminal.ErrorActivity.Companion.start
@@ -87,6 +88,7 @@
private val bootCompleted = ConditionVariable()
private lateinit var manageExternalStorageActivityResultLauncher: ActivityResultLauncher<Intent>
private lateinit var modifierKeysController: ModifierKeysController
+ private var displayMenu: MenuItem? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -209,7 +211,10 @@
view: WebView?,
request: WebResourceRequest?,
): Boolean {
- return false
+ val intent = Intent(Intent.ACTION_VIEW, request?.url)
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ startActivity(intent)
+ return true
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
@@ -253,6 +258,10 @@
Trace.endAsyncSection("executeTerminal", 0)
findViewById<View?>(R.id.boot_progress).visibility = View.GONE
terminalContainer.visibility = View.VISIBLE
+ if (terminalGuiSupport()) {
+ displayMenu?.setVisible(true)
+ displayMenu?.setEnabled(true)
+ }
bootCompleted.open()
modifierKeysController.update()
terminalView.mapTouchToMouseEvent()
@@ -293,6 +302,8 @@
info,
executorService,
object : NsdManager.ServiceInfoCallback {
+ var loaded: Boolean = false
+
override fun onServiceInfoCallbackRegistrationFailed(errorCode: Int) {}
override fun onServiceInfoCallbackUnregistered() {}
@@ -300,13 +311,15 @@
override fun onServiceLost() {}
override fun onServiceUpdated(info: NsdServiceInfo) {
- nsdManager.unregisterServiceInfoCallback(this)
-
Log.i(TAG, "Service found: $info")
val ipAddress = info.hostAddresses[0].hostAddress
val port = info.port
val url = getTerminalServiceUrl(ipAddress, port)
- runOnUiThread(Runnable { terminalView.loadUrl(url.toString()) })
+ if (!loaded) {
+ loaded = true
+ nsdManager.unregisterServiceInfoCallback(this)
+ runOnUiThread(Runnable { terminalView.loadUrl(url.toString()) })
+ }
}
},
)
@@ -337,6 +350,11 @@
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
+ displayMenu =
+ menu?.findItem(R.id.menu_item_display).also {
+ it?.setVisible(terminalGuiSupport())
+ it?.setEnabled(false)
+ }
return true
}
@@ -346,6 +364,10 @@
val intent = Intent(this, SettingsActivity::class.java)
this.startActivity(intent)
return true
+ } else if (id == R.id.menu_item_display) {
+ val intent = Intent(this, DisplayActivity::class.java)
+ this.startActivity(intent)
+ return true
}
return super.onOptionsItemSelected(item)
}
@@ -409,7 +431,7 @@
)
val icon = Icon.createWithResource(resources, R.drawable.ic_launcher_foreground)
val notification: Notification =
- Notification.Builder(this, this.packageName)
+ Notification.Builder(this, Application.CHANNEL_LONG_RUNNING_ID)
.setSilent(true)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(resources.getString(R.string.service_notification_title))
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/PortNotifier.kt b/android/TerminalApp/java/com/android/virtualization/terminal/PortNotifier.kt
index 7c48303..7e58b36 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/PortNotifier.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/PortNotifier.kt
@@ -100,7 +100,7 @@
)
.build()
val notification: Notification =
- Notification.Builder(context, context.getPackageName())
+ Notification.Builder(context, Application.CHANNEL_SYSTEM_EVENTS_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(title)
.setContentText(content)
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index 8c0368d..346056a 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -36,6 +36,7 @@
import android.system.virtualmachine.VirtualMachineException
import android.util.Log
import android.widget.Toast
+import com.android.system.virtualmachine.flags.Flags.terminalGuiSupport
import com.android.virtualization.terminal.MainActivity.Companion.TAG
import com.android.virtualization.terminal.Runner.Companion.create
import com.android.virtualization.terminal.VmLauncherService.VmLauncherServiceCallback
@@ -149,6 +150,8 @@
info,
executorService!!,
object : NsdManager.ServiceInfoCallback {
+ var started: Boolean = false
+
override fun onServiceInfoCallbackRegistrationFailed(errorCode: Int) {}
override fun onServiceInfoCallbackUnregistered() {}
@@ -156,9 +159,12 @@
override fun onServiceLost() {}
override fun onServiceUpdated(info: NsdServiceInfo) {
- nsdManager.unregisterServiceInfoCallback(this)
Log.i(TAG, "Service found: $info")
- startDebianServer(info.hostAddresses[0].hostAddress)
+ if (!started) {
+ started = true
+ nsdManager.unregisterServiceInfoCallback(this)
+ startDebianServer(info.hostAddresses[0].hostAddress)
+ }
}
},
)
@@ -182,7 +188,7 @@
resources.getString(R.string.service_notification_force_quit_action)
val stopNotificationTitle: String? =
resources.getString(R.string.service_notification_close_title)
- return Notification.Builder(this, this.packageName)
+ return Notification.Builder(this, Application.CHANNEL_SYSTEM_EVENTS_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(stopNotificationTitle)
.setOngoing(true)
@@ -212,6 +218,21 @@
changed = true
}
+ // TODO(jeongik): let it configurable
+ if (terminalGuiSupport()) {
+ builder
+ .setDisplayConfig(
+ VirtualMachineCustomImageConfig.DisplayConfig.Builder()
+ .setWidth(1920)
+ .setHeight(1080)
+ .build()
+ )
+ .useKeyboard(true)
+ .useMouse(true)
+ .useTouch(true)
+ changed = true
+ }
+
val image = InstalledImage.getDefault(this)
if (image.hasBackup()) {
val backup = image.backupFile
diff --git a/android/TerminalApp/proguard.flags b/android/TerminalApp/proguard.flags
index 88b8a9c..04a2140 100644
--- a/android/TerminalApp/proguard.flags
+++ b/android/TerminalApp/proguard.flags
@@ -4,7 +4,10 @@
-keepattributes Signature
# For using GSON @Expose annotation
--keepattributes *Annotation*
+-keepattributes RuntimeVisibleAnnotations,
+ RuntimeVisibleParameterAnnotations,
+ RuntimeVisibleTypeAnnotations,
+ AnnotationDefault
# Gson specific classes
-dontwarn sun.misc.**
diff --git a/android/TerminalApp/res/drawable/ic_display.xml b/android/TerminalApp/res/drawable/ic_display.xml
new file mode 100644
index 0000000..86bdb5d
--- /dev/null
+++ b/android/TerminalApp/res/drawable/ic_display.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2025 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M240,840L240,760L280,720L160,720Q127,720 103.5,696.5Q80,673 80,640L80,200Q80,167 103.5,143.5Q127,120 160,120L800,120Q833,120 856.5,143.5Q880,167 880,200L880,640Q880,673 856.5,696.5Q833,720 800,720L680,720L720,760L720,840L240,840ZM160,640L800,640Q800,640 800,640Q800,640 800,640L800,200Q800,200 800,200Q800,200 800,200L160,200Q160,200 160,200Q160,200 160,200L160,640Q160,640 160,640Q160,640 160,640ZM160,640Q160,640 160,640Q160,640 160,640L160,200Q160,200 160,200Q160,200 160,200L160,200Q160,200 160,200Q160,200 160,200L160,640Q160,640 160,640Q160,640 160,640Z" />
+</vector>
\ No newline at end of file
diff --git a/android/TerminalApp/res/layout/activity_display.xml b/android/TerminalApp/res/layout/activity_display.xml
new file mode 100644
index 0000000..48e49fe
--- /dev/null
+++ b/android/TerminalApp/res/layout/activity_display.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".DisplayActivity">
+ <View
+ android:id="@+id/background_touch_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+ <!-- the size should be match_parent -->
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="1920px"
+ android:layout_height="1080px"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:focusedByDefault="true"
+ android:defaultFocusHighlightEnabled="true">
+ <requestFocus />
+ </SurfaceView>
+ <!-- A cursor size in virtio-gpu spec is always 64x64 -->
+ <SurfaceView
+ android:id="@+id/cursor_surface_view"
+ android:layout_width="64px"
+ android:layout_height="64px">
+ </SurfaceView>
+
+</merge>
\ No newline at end of file
diff --git a/android/TerminalApp/res/menu/main_menu.xml b/android/TerminalApp/res/menu/main_menu.xml
index 42ba85d..dbb788c 100644
--- a/android/TerminalApp/res/menu/main_menu.xml
+++ b/android/TerminalApp/res/menu/main_menu.xml
@@ -20,4 +20,9 @@
android:icon="@drawable/ic_settings"
android:title="@string/action_settings"
app:showAsAction="always"/>
+ <item android:id="@+id/menu_item_display"
+ android:icon="@drawable/ic_display"
+ android:enabled="false"
+ android:title="@string/action_display"
+ app:showAsAction="always"/>
</menu>
diff --git a/android/TerminalApp/res/values-af/strings.xml b/android/TerminalApp/res/values-af/strings.xml
index 1e8d8ff..faae858 100644
--- a/android/TerminalApp/res/values-af/strings.xml
+++ b/android/TerminalApp/res/values-af/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminaal maak toe"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Verplig toemaak"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> is geaktiveer"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-am/strings.xml b/android/TerminalApp/res/values-am/strings.xml
index faa7339..dba31dd 100644
--- a/android/TerminalApp/res/values-am/strings.xml
+++ b/android/TerminalApp/res/values-am/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ተርሚናል በመዝጋት ላይ ነው"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"በግድ ዝጋ"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ነቅቷል"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ar/strings.xml b/android/TerminalApp/res/values-ar/strings.xml
index 9d1f2b7..bee6195 100644
--- a/android/TerminalApp/res/values-ar/strings.xml
+++ b/android/TerminalApp/res/values-ar/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"جارٍ إغلاق الوحدة الطرفية"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"فرض الإغلاق"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"تم تفعيل <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"المهام الطويلة المدى"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"أحداث النظام"</string>
</resources>
diff --git a/android/TerminalApp/res/values-as/strings.xml b/android/TerminalApp/res/values-as/strings.xml
index 630efc4..3ceccd7 100644
--- a/android/TerminalApp/res/values-as/strings.xml
+++ b/android/TerminalApp/res/values-as/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"টাৰ্মিনেলটো বন্ধ কৰি থকা হৈছে"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"বলেৰে বন্ধ কৰক"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> সক্ষম কৰা আছে"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"দীঘলীয়া সময় জুৰি চলা কাৰ্য"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"ছিষ্টেমৰ ঘটনা"</string>
</resources>
diff --git a/android/TerminalApp/res/values-az/strings.xml b/android/TerminalApp/res/values-az/strings.xml
index 7c134f0..6b7c8a2 100644
--- a/android/TerminalApp/res/values-az/strings.xml
+++ b/android/TerminalApp/res/values-az/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal bağlanır"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Məcburi bağlanma"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> aktivləşdirilib"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-b+sr+Latn/strings.xml b/android/TerminalApp/res/values-b+sr+Latn/strings.xml
index 0e77081..dd33e33 100644
--- a/android/TerminalApp/res/values-b+sr+Latn/strings.xml
+++ b/android/TerminalApp/res/values-b+sr+Latn/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal se zatvara"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Prinudno zatvori"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> je omogućen"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Dugotrajni zadaci"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Sistemski događaji"</string>
</resources>
diff --git a/android/TerminalApp/res/values-be/strings.xml b/android/TerminalApp/res/values-be/strings.xml
index 31d1341..77d26e5 100644
--- a/android/TerminalApp/res/values-be/strings.xml
+++ b/android/TerminalApp/res/values-be/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Тэрмінал закрываецца"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Закрыць прымусова"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Модуль <xliff:g id="ID_1">VirGL</xliff:g> уключаны"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Працяглыя задачы"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Сістэмныя падзеі"</string>
</resources>
diff --git a/android/TerminalApp/res/values-bg/strings.xml b/android/TerminalApp/res/values-bg/strings.xml
index 5112c08..d4767bd 100644
--- a/android/TerminalApp/res/values-bg/strings.xml
+++ b/android/TerminalApp/res/values-bg/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Терминалът се затваря"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Принудително затваряне"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> е активирано"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Продължителни задачи"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Системни събития"</string>
</resources>
diff --git a/android/TerminalApp/res/values-bn/strings.xml b/android/TerminalApp/res/values-bn/strings.xml
index fdf59b3..e2fffee 100644
--- a/android/TerminalApp/res/values-bn/strings.xml
+++ b/android/TerminalApp/res/values-bn/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"টার্মিনাল বন্ধ হচ্ছে"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"জোর করে বন্ধ করুন"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> চালু করা আছে"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"দীর্ঘ সময় ধরে চালানো টাস্ক"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"সিস্টেম ইভেন্ট"</string>
</resources>
diff --git a/android/TerminalApp/res/values-bs/strings.xml b/android/TerminalApp/res/values-bs/strings.xml
index b59bdf5..f74dac0 100644
--- a/android/TerminalApp/res/values-bs/strings.xml
+++ b/android/TerminalApp/res/values-bs/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal je zatvoren"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Prisilno zatvori"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Omogućeno: <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ca/strings.xml b/android/TerminalApp/res/values-ca/strings.xml
index 58fdeaf..f35bcad 100644
--- a/android/TerminalApp/res/values-ca/strings.xml
+++ b/android/TerminalApp/res/values-ca/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"El terminal s\'està tancant"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Força el tancament"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> està activat"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-cs/strings.xml b/android/TerminalApp/res/values-cs/strings.xml
index 563ad9f..135db1a 100644
--- a/android/TerminalApp/res/values-cs/strings.xml
+++ b/android/TerminalApp/res/values-cs/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminál se zavírá"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Vynutit ukončení"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Modul <xliff:g id="ID_1">VirGL</xliff:g> je aktivován"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Dlouho spuštěné úlohy"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Systémové události"</string>
</resources>
diff --git a/android/TerminalApp/res/values-da/strings.xml b/android/TerminalApp/res/values-da/strings.xml
index 2382c4b..f781cfb 100644
--- a/android/TerminalApp/res/values-da/strings.xml
+++ b/android/TerminalApp/res/values-da/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminalen lukker"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Tving til at lukke"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> er aktiveret"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Længerevarende opgaver"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Systemhændelser"</string>
</resources>
diff --git a/android/TerminalApp/res/values-de/strings.xml b/android/TerminalApp/res/values-de/strings.xml
index bb71de3..1390479 100644
--- a/android/TerminalApp/res/values-de/strings.xml
+++ b/android/TerminalApp/res/values-de/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal wird geschlossen"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Schließen erzwingen"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ist aktiviert"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-el/strings.xml b/android/TerminalApp/res/values-el/strings.xml
index 72882b8..6666316 100644
--- a/android/TerminalApp/res/values-el/strings.xml
+++ b/android/TerminalApp/res/values-el/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Το τερματικό κλείνει"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Αναγκαστικό κλείσιμο"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Το <xliff:g id="ID_1">VirGL</xliff:g> είναι ενεργοποιημένο"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Εργασίες μεγάλης διάρκειας"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Συμβάντα συστήματος"</string>
</resources>
diff --git a/android/TerminalApp/res/values-en-rAU/strings.xml b/android/TerminalApp/res/values-en-rAU/strings.xml
index f208e38..be24320 100644
--- a/android/TerminalApp/res/values-en-rAU/strings.xml
+++ b/android/TerminalApp/res/values-en-rAU/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal is closing"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Force close"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> is enabled"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Long-running tasks"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"System events"</string>
</resources>
diff --git a/android/TerminalApp/res/values-en-rCA/strings.xml b/android/TerminalApp/res/values-en-rCA/strings.xml
index f208e38..78fec92 100644
--- a/android/TerminalApp/res/values-en-rCA/strings.xml
+++ b/android/TerminalApp/res/values-en-rCA/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal is closing"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Force close"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> is enabled"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Long running tasks"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"System events"</string>
</resources>
diff --git a/android/TerminalApp/res/values-en-rGB/strings.xml b/android/TerminalApp/res/values-en-rGB/strings.xml
index f208e38..be24320 100644
--- a/android/TerminalApp/res/values-en-rGB/strings.xml
+++ b/android/TerminalApp/res/values-en-rGB/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal is closing"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Force close"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> is enabled"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Long-running tasks"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"System events"</string>
</resources>
diff --git a/android/TerminalApp/res/values-en-rIN/strings.xml b/android/TerminalApp/res/values-en-rIN/strings.xml
index f208e38..be24320 100644
--- a/android/TerminalApp/res/values-en-rIN/strings.xml
+++ b/android/TerminalApp/res/values-en-rIN/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal is closing"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Force close"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> is enabled"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Long-running tasks"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"System events"</string>
</resources>
diff --git a/android/TerminalApp/res/values-es-rUS/strings.xml b/android/TerminalApp/res/values-es-rUS/strings.xml
index ff2ae6d..dab18f8 100644
--- a/android/TerminalApp/res/values-es-rUS/strings.xml
+++ b/android/TerminalApp/res/values-es-rUS/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Se está cerrando la terminal"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forzar cierre"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Se habilitó <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-es/strings.xml b/android/TerminalApp/res/values-es/strings.xml
index 0a8932e..d901d9a 100644
--- a/android/TerminalApp/res/values-es/strings.xml
+++ b/android/TerminalApp/res/values-es/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"El terminal se está cerrando"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forzar cierre"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> se ha habilitado"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-et/strings.xml b/android/TerminalApp/res/values-et/strings.xml
index d6754d8..5276dc1 100644
--- a/android/TerminalApp/res/values-et/strings.xml
+++ b/android/TerminalApp/res/values-et/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal suletakse"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Rakenda sulgemine"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> on lubatud"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-eu/strings.xml b/android/TerminalApp/res/values-eu/strings.xml
index 0cdcca5..e8d5a30 100644
--- a/android/TerminalApp/res/values-eu/strings.xml
+++ b/android/TerminalApp/res/values-eu/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal ixten ari da"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Behartu ixtera"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> gaituta dago"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-fa/strings.xml b/android/TerminalApp/res/values-fa/strings.xml
index 4ec4d0a..8824242 100644
--- a/android/TerminalApp/res/values-fa/strings.xml
+++ b/android/TerminalApp/res/values-fa/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"پایانه درحال بسته شدن است"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"بستن اجباری"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> فعال شد"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-fi/strings.xml b/android/TerminalApp/res/values-fi/strings.xml
index aec6a47..e84c912 100644
--- a/android/TerminalApp/res/values-fi/strings.xml
+++ b/android/TerminalApp/res/values-fi/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal sulkeutuu"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Pakota sulkeutumaan"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> on käytössä"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-fr-rCA/strings.xml b/android/TerminalApp/res/values-fr-rCA/strings.xml
index 7d54372..9a0489c 100644
--- a/android/TerminalApp/res/values-fr-rCA/strings.xml
+++ b/android/TerminalApp/res/values-fr-rCA/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Le terminal se fermera"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forcer la fermeture"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> est activé"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Tâches de longue durée"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Événements système"</string>
</resources>
diff --git a/android/TerminalApp/res/values-fr/strings.xml b/android/TerminalApp/res/values-fr/strings.xml
index 3fe8772..0031db5 100644
--- a/android/TerminalApp/res/values-fr/strings.xml
+++ b/android/TerminalApp/res/values-fr/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal se ferme"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forcer la fermeture"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> est activé"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Tâches de longue durée"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Événements système"</string>
</resources>
diff --git a/android/TerminalApp/res/values-gl/strings.xml b/android/TerminalApp/res/values-gl/strings.xml
index ec98910..e0bc7a8 100644
--- a/android/TerminalApp/res/values-gl/strings.xml
+++ b/android/TerminalApp/res/values-gl/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"A aplicación Terminal estase pechando"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forzar peche"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Activouse <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-gu/strings.xml b/android/TerminalApp/res/values-gu/strings.xml
index d53d3eb..68d9146 100644
--- a/android/TerminalApp/res/values-gu/strings.xml
+++ b/android/TerminalApp/res/values-gu/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ટર્મિનલ ઍપ બંધ થઈ રહી છે"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"ફરજિયાત બંધ કરો"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ચાલુ કરેલું છે"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"લાંબો સમય ચાલનારા કાર્યો"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"સિસ્ટમ ઇવેન્ટ"</string>
</resources>
diff --git a/android/TerminalApp/res/values-hi/strings.xml b/android/TerminalApp/res/values-hi/strings.xml
index b644e1f..0d9297c 100644
--- a/android/TerminalApp/res/values-hi/strings.xml
+++ b/android/TerminalApp/res/values-hi/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"टर्मिनल ऐप्लिकेशन बंद हो रहा है"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"ज़बरदस्ती बंद करें"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> चालू है"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"लंबे समय तक चलने वाले टास्क"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"सिस्टम इवेंट"</string>
</resources>
diff --git a/android/TerminalApp/res/values-hr/strings.xml b/android/TerminalApp/res/values-hr/strings.xml
index 8ce5af5..1704900 100644
--- a/android/TerminalApp/res/values-hr/strings.xml
+++ b/android/TerminalApp/res/values-hr/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal se zatvara"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Prisilno zatvori"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Omogućeno je: <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-hu/strings.xml b/android/TerminalApp/res/values-hu/strings.xml
index f3058d0..fb6859f 100644
--- a/android/TerminalApp/res/values-hu/strings.xml
+++ b/android/TerminalApp/res/values-hu/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"A terminál bezárul"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Bezárás kényszerítése"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"A(z) <xliff:g id="ID_1">VirGL</xliff:g> engedélyezve van"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Hosszan futó feladatok"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Rendszeresemények"</string>
</resources>
diff --git a/android/TerminalApp/res/values-hy/strings.xml b/android/TerminalApp/res/values-hy/strings.xml
index f11469b..31e6dae 100644
--- a/android/TerminalApp/res/values-hy/strings.xml
+++ b/android/TerminalApp/res/values-hy/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Տերմինալը փակվում է"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Ստիպողաբար փակել"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g>-ը միացված է"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Երկար աշխատող առաջադրանքներ"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Համակարգի իրադարձություններ"</string>
</resources>
diff --git a/android/TerminalApp/res/values-in/strings.xml b/android/TerminalApp/res/values-in/strings.xml
index a2c4b01..23ea0ed 100644
--- a/android/TerminalApp/res/values-in/strings.xml
+++ b/android/TerminalApp/res/values-in/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal ditutup"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Tutup paksa"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> diaktifkan"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-is/strings.xml b/android/TerminalApp/res/values-is/strings.xml
index 2e97d0c..026e575 100644
--- a/android/TerminalApp/res/values-is/strings.xml
+++ b/android/TerminalApp/res/values-is/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal er að loka"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Þvinga fram lokun"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Kveikt er á <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Langvarandi verkefni"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Kerfistilvik"</string>
</resources>
diff --git a/android/TerminalApp/res/values-it/strings.xml b/android/TerminalApp/res/values-it/strings.xml
index 1cfd2b5..44a58ad 100644
--- a/android/TerminalApp/res/values-it/strings.xml
+++ b/android/TerminalApp/res/values-it/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Chiusura del terminale in corso…"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Termina"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> è abilitata"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Attività di lunga durata"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Eventi di sistema"</string>
</resources>
diff --git a/android/TerminalApp/res/values-iw/strings.xml b/android/TerminalApp/res/values-iw/strings.xml
index 8859e5f..b47b6ed 100644
--- a/android/TerminalApp/res/values-iw/strings.xml
+++ b/android/TerminalApp/res/values-iw/strings.xml
@@ -20,7 +20,7 @@
<string name="terminal_display" msgid="4810127497644015237">"תצוגת טרמינל"</string>
<string name="terminal_input" msgid="4602512831433433551">"סמן"</string>
<string name="empty_line" msgid="5012067143408427178">"שורה ריקה"</string>
- <string name="double_tap_to_edit_text" msgid="2344363097580051316">"כדי להקליד טקסט צריך להקיש הקשה כפולה"</string>
+ <string name="double_tap_to_edit_text" msgid="2344363097580051316">"כדי להקליד טקסט צריך ללחוץ לחיצה כפולה"</string>
<string name="installer_title_text" msgid="500663060973466805">"התקנה של טרמינל Linux"</string>
<string name="installer_desc_text_format" msgid="5935117404303982823">"כדי להפעיל את טרמינל Linux, צריך להוריד נתונים בנפח של בערך <xliff:g id="EXPECTED_SIZE">%1$s</xliff:g> דרך הרשת.\nלהמשיך?"</string>
<string name="installer_wait_for_wifi_checkbox_text" msgid="5812378362605046639">"הורדה רק באמצעות Wi-Fi"</string>
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"הטרמינל בתהליך סגירה"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"אילוץ סגירה"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> מופעל"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"משימות ממושכות"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"אירועי מערכת"</string>
</resources>
diff --git a/android/TerminalApp/res/values-ja/strings.xml b/android/TerminalApp/res/values-ja/strings.xml
index 19c4af8..61a9b98 100644
--- a/android/TerminalApp/res/values-ja/strings.xml
+++ b/android/TerminalApp/res/values-ja/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ターミナルを閉じています"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"強制終了"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g>は有効です"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"長時間実行タスク"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"システム イベント"</string>
</resources>
diff --git a/android/TerminalApp/res/values-ka/strings.xml b/android/TerminalApp/res/values-ka/strings.xml
index be79f8b..2903892 100644
--- a/android/TerminalApp/res/values-ka/strings.xml
+++ b/android/TerminalApp/res/values-ka/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ტერმინალი იხურება"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"იძულებით დახურვა"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ჩართულია"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"ხანგრძლივად გაშვებული ამოცანები"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"სისტემური მოვლენები"</string>
</resources>
diff --git a/android/TerminalApp/res/values-kk/strings.xml b/android/TerminalApp/res/values-kk/strings.xml
index 5477c04..276447b 100644
--- a/android/TerminalApp/res/values-kk/strings.xml
+++ b/android/TerminalApp/res/values-kk/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Терминал жабылып жатыр"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Қолмен жабу"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> қосылды."</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Ұзақ орындалатын тапсырмалар"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Жүйе оқиғалары"</string>
</resources>
diff --git a/android/TerminalApp/res/values-km/strings.xml b/android/TerminalApp/res/values-km/strings.xml
index 1ec9a0a..a31fec9 100644
--- a/android/TerminalApp/res/values-km/strings.xml
+++ b/android/TerminalApp/res/values-km/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ទែមីណាល់កំពុងបិទ"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"បង្ខំឱ្យបិទ"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ត្រូវបានបើក"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-kn/strings.xml b/android/TerminalApp/res/values-kn/strings.xml
index 3c53299..e5fe3d1 100644
--- a/android/TerminalApp/res/values-kn/strings.xml
+++ b/android/TerminalApp/res/values-kn/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ಟರ್ಮಿನಲ್ ಮುಚ್ಚಲಾಗುತ್ತಿದೆ"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"ಬಲವಂತವಾಗಿ ಮುಚ್ಚಿ"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ಅನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"ದೀರ್ಘಾವಧಿಯ ಕಾರ್ಯಗಳು"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"ಸಿಸ್ಟಮ್ ಈವೆಂಟ್ಗಳು"</string>
</resources>
diff --git a/android/TerminalApp/res/values-ko/strings.xml b/android/TerminalApp/res/values-ko/strings.xml
index 4ec1613..3481865 100644
--- a/android/TerminalApp/res/values-ko/strings.xml
+++ b/android/TerminalApp/res/values-ko/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"터미널 앱 종료 중"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"강제 종료"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g>이(가) 사용 설정되었습니다."</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ky/strings.xml b/android/TerminalApp/res/values-ky/strings.xml
index 17fc4ea..ebce614 100644
--- a/android/TerminalApp/res/values-ky/strings.xml
+++ b/android/TerminalApp/res/values-ky/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Терминал жабылып жатат"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Мажбурлап жабуу"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> иштетилди"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-lo/strings.xml b/android/TerminalApp/res/values-lo/strings.xml
index cb8bc0c..a6afb05 100644
--- a/android/TerminalApp/res/values-lo/strings.xml
+++ b/android/TerminalApp/res/values-lo/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ເທີມິນອນກຳລັງຈະປິດ"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"ບັງຄັບປິດ"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"ເປີດການນຳໃຊ້ <xliff:g id="ID_1">VirGL</xliff:g> ແລ້ວ"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"ວຽກທີ່ດຳເນີນເປັນເວລາດົນ"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"ກິດຈະກຳລະບົບ"</string>
</resources>
diff --git a/android/TerminalApp/res/values-lt/strings.xml b/android/TerminalApp/res/values-lt/strings.xml
index 76f5158..81e00a4 100644
--- a/android/TerminalApp/res/values-lt/strings.xml
+++ b/android/TerminalApp/res/values-lt/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminalas uždaromas"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Priverstinai uždaryti"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"„<xliff:g id="ID_1">VirGL</xliff:g>“ įgalinta"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Ilgai vykdomos užduotys"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Sistemos įvykiai"</string>
</resources>
diff --git a/android/TerminalApp/res/values-lv/strings.xml b/android/TerminalApp/res/values-lv/strings.xml
index a0730a1..3e07e56 100644
--- a/android/TerminalApp/res/values-lv/strings.xml
+++ b/android/TerminalApp/res/values-lv/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminālis tiek aizvērts"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Veikt piespiedu aizvēršanu"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ir iespējots"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-mk/strings.xml b/android/TerminalApp/res/values-mk/strings.xml
index ac328d7..1da4930 100644
--- a/android/TerminalApp/res/values-mk/strings.xml
+++ b/android/TerminalApp/res/values-mk/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Терминалот се затвора"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Затвори присилно"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Овозможено: <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ml/strings.xml b/android/TerminalApp/res/values-ml/strings.xml
index 1ff795d..08eb518 100644
--- a/android/TerminalApp/res/values-ml/strings.xml
+++ b/android/TerminalApp/res/values-ml/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ടെർമിനൽ അടയ്ക്കുകയാണ്"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"നിർബന്ധിതമായി അടയ്ക്കുക"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> പ്രവർത്തനക്ഷമമാക്കി"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"ദീർഘസമയം റൺ ചെയ്യുന്ന ടാസ്ക്കുകൾ"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"സിസ്റ്റം ഇവന്റുകൾ"</string>
</resources>
diff --git a/android/TerminalApp/res/values-mn/strings.xml b/android/TerminalApp/res/values-mn/strings.xml
index 5b6ce4a..f24c88e 100644
--- a/android/TerminalApp/res/values-mn/strings.xml
+++ b/android/TerminalApp/res/values-mn/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Терминал хаагдаж байна"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Хүчээр хаах"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> идэвхэжсэн"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-mr/strings.xml b/android/TerminalApp/res/values-mr/strings.xml
index 1701983..306bc78 100644
--- a/android/TerminalApp/res/values-mr/strings.xml
+++ b/android/TerminalApp/res/values-mr/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"टर्मिनल बंद होत आहे"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"सक्तीने बंद करा"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> सुरू केले आहे"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ms/strings.xml b/android/TerminalApp/res/values-ms/strings.xml
index 1709e51..6ea5268 100644
--- a/android/TerminalApp/res/values-ms/strings.xml
+++ b/android/TerminalApp/res/values-ms/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal ditutup"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Tutup paksa"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> didayakan"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Tugasan yang memakan masa yang lama"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Peristiwa sistem"</string>
</resources>
diff --git a/android/TerminalApp/res/values-my/strings.xml b/android/TerminalApp/res/values-my/strings.xml
index dc3e555..a163922 100644
--- a/android/TerminalApp/res/values-my/strings.xml
+++ b/android/TerminalApp/res/values-my/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"တာမီနယ် ပိတ်နေသည်"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"မဖြစ်မနေပိတ်ရန်"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ဖွင့်ထားသည်"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-nb/strings.xml b/android/TerminalApp/res/values-nb/strings.xml
index c0a84e5..3dcd9c2 100644
--- a/android/TerminalApp/res/values-nb/strings.xml
+++ b/android/TerminalApp/res/values-nb/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminalen lukkes"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Tving avslutning"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> er aktivert"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ne/strings.xml b/android/TerminalApp/res/values-ne/strings.xml
index 8cbda87..580a0fb 100644
--- a/android/TerminalApp/res/values-ne/strings.xml
+++ b/android/TerminalApp/res/values-ne/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"टर्मिनल एप बन्द हुँदै छ"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"बलपूर्वक बन्द गर्नुहोस्"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> अन गरिएको छ"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"लामो समयसम्म चलिरहने कार्यहरू"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"सिस्टमसम्बन्धी गतिविधिहरू"</string>
</resources>
diff --git a/android/TerminalApp/res/values-nl/strings.xml b/android/TerminalApp/res/values-nl/strings.xml
index 27fbc26..dc1066b 100644
--- a/android/TerminalApp/res/values-nl/strings.xml
+++ b/android/TerminalApp/res/values-nl/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal wordt gesloten"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Geforceerd sluiten"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> staat aan"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Langlopende taken"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Systeemgebeurtenissen"</string>
</resources>
diff --git a/android/TerminalApp/res/values-or/strings.xml b/android/TerminalApp/res/values-or/strings.xml
index 034849f..67d7ff4 100644
--- a/android/TerminalApp/res/values-or/strings.xml
+++ b/android/TerminalApp/res/values-or/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ଟର୍ମିନାଲ ବନ୍ଦ ହେବାକୁ ଯାଉଛି"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"ଫୋର୍ସ କ୍ଲୋଜ କରନ୍ତୁ"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g>କୁ ସକ୍ଷମ କରାଯାଇଛି"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-pa/strings.xml b/android/TerminalApp/res/values-pa/strings.xml
index b6af472..fed62c1 100644
--- a/android/TerminalApp/res/values-pa/strings.xml
+++ b/android/TerminalApp/res/values-pa/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ਟਰਮੀਨਲ ਬੰਦ ਹੋ ਰਿਹਾ ਹੈ"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"ਜ਼ਬਰਦਸਤੀ ਬੰਦ ਕਰੋ"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ਚਾਲੂ ਹੈ"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"ਲੰਬੇ ਸਮੇਂ ਤੱਕ ਚੱਲਣ ਵਾਲੇ ਕਾਰਜ"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"ਸਿਸਟਮ ਇਵੈਂਟ"</string>
</resources>
diff --git a/android/TerminalApp/res/values-pl/strings.xml b/android/TerminalApp/res/values-pl/strings.xml
index 92a6dea..0143203 100644
--- a/android/TerminalApp/res/values-pl/strings.xml
+++ b/android/TerminalApp/res/values-pl/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal się zamyka"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Wymuś zamknięcie"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Układ <xliff:g id="ID_1">VirGL</xliff:g> jest włączony"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Długotrwałe zadania"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Zdarzenia systemowe"</string>
</resources>
diff --git a/android/TerminalApp/res/values-pt-rPT/strings.xml b/android/TerminalApp/res/values-pt-rPT/strings.xml
index 337e355..99caceb 100644
--- a/android/TerminalApp/res/values-pt-rPT/strings.xml
+++ b/android/TerminalApp/res/values-pt-rPT/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"A app Terminal está a ser fechada"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forçar fecho"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"A <xliff:g id="ID_1">VirGL</xliff:g> está ativada"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Tarefas de longa duração"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Eventos do sistema"</string>
</resources>
diff --git a/android/TerminalApp/res/values-pt/strings.xml b/android/TerminalApp/res/values-pt/strings.xml
index b223c9f..587b4bc 100644
--- a/android/TerminalApp/res/values-pt/strings.xml
+++ b/android/TerminalApp/res/values-pt/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"O terminal está fechando"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forçar fechamento"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"O <xliff:g id="ID_1">VirGL</xliff:g> está ativado"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ro/strings.xml b/android/TerminalApp/res/values-ro/strings.xml
index 9cfa4bc..1b4bb17 100644
--- a/android/TerminalApp/res/values-ro/strings.xml
+++ b/android/TerminalApp/res/values-ro/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminalul se închide"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Forțează închiderea"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> este activat"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ru/strings.xml b/android/TerminalApp/res/values-ru/strings.xml
index c3dfec3..1ad2fe7 100644
--- a/android/TerminalApp/res/values-ru/strings.xml
+++ b/android/TerminalApp/res/values-ru/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Терминал закрывается"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Закрыть принудительно"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g>: включено."</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-si/strings.xml b/android/TerminalApp/res/values-si/strings.xml
index 1b4ffc3..9d3f4d2 100644
--- a/android/TerminalApp/res/values-si/strings.xml
+++ b/android/TerminalApp/res/values-si/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ටර්මිනලය වැසෙමින් පවතී"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"බලෙන් වසන්න"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> සබලයි"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-sk/strings.xml b/android/TerminalApp/res/values-sk/strings.xml
index d77433e..68267e9 100644
--- a/android/TerminalApp/res/values-sk/strings.xml
+++ b/android/TerminalApp/res/values-sk/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminál sa zatvára"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Vynútiť zavretie"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Procesor <xliff:g id="ID_1">VirGL</xliff:g> je aktivovaný"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-sl/strings.xml b/android/TerminalApp/res/values-sl/strings.xml
index 0583a0f..0a38237 100644
--- a/android/TerminalApp/res/values-sl/strings.xml
+++ b/android/TerminalApp/res/values-sl/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal se zapira"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Vsili zapiranje"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> je omogočen"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-sq/strings.xml b/android/TerminalApp/res/values-sq/strings.xml
index 3bac355..057cd8d 100644
--- a/android/TerminalApp/res/values-sq/strings.xml
+++ b/android/TerminalApp/res/values-sq/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"\"Terminali\" po mbyllet"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Ndalo me forcë"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> është aktivizuar"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-sr/strings.xml b/android/TerminalApp/res/values-sr/strings.xml
index 97a2e4b..57ac862 100644
--- a/android/TerminalApp/res/values-sr/strings.xml
+++ b/android/TerminalApp/res/values-sr/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Терминал се затвара"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Принудно затвори"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> је омогућен"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Дуготрајни задаци"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Системски догађаји"</string>
</resources>
diff --git a/android/TerminalApp/res/values-sv/strings.xml b/android/TerminalApp/res/values-sv/strings.xml
index 3b1b4f0..94af12b 100644
--- a/android/TerminalApp/res/values-sv/strings.xml
+++ b/android/TerminalApp/res/values-sv/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminalen stängs av"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Tvinga avstängning"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> har aktiverats"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-sw/strings.xml b/android/TerminalApp/res/values-sw/strings.xml
index c8ffd12..529e473 100644
--- a/android/TerminalApp/res/values-sw/strings.xml
+++ b/android/TerminalApp/res/values-sw/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Kituo kinafungwa"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Lazimisha kufunga"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> imewashwa"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Majukumu yanayodumu zaidi"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Matukio ya mfumo"</string>
</resources>
diff --git a/android/TerminalApp/res/values-ta/strings.xml b/android/TerminalApp/res/values-ta/strings.xml
index 44d0fad..7649103 100644
--- a/android/TerminalApp/res/values-ta/strings.xml
+++ b/android/TerminalApp/res/values-ta/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"டெர்மினல் மூடப்படுகிறது"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"உடனே மூடு"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> இயக்கப்பட்டது"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"நீண்ட நேரம் இயங்கும் பணிகள்"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"சிஸ்டம் நிகழ்வுகள்"</string>
</resources>
diff --git a/android/TerminalApp/res/values-te/strings.xml b/android/TerminalApp/res/values-te/strings.xml
index 6a3d646..42678c7 100644
--- a/android/TerminalApp/res/values-te/strings.xml
+++ b/android/TerminalApp/res/values-te/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal మూసివేయబడుతోంది"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"బలవంతంగా మూసివేయండి"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> ప్రారంభించబడింది"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-th/strings.xml b/android/TerminalApp/res/values-th/strings.xml
index 66716ec..768c906 100644
--- a/android/TerminalApp/res/values-th/strings.xml
+++ b/android/TerminalApp/res/values-th/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"เทอร์มินัลกำลังจะปิด"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"บังคับปิด"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"เปิดใช้งาน <xliff:g id="ID_1">VirGL</xliff:g> แล้ว"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"งานที่ใช้เวลานาน"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"เหตุการณ์ของระบบ"</string>
</resources>
diff --git a/android/TerminalApp/res/values-tl/strings.xml b/android/TerminalApp/res/values-tl/strings.xml
index 56b2679..cd69834 100644
--- a/android/TerminalApp/res/values-tl/strings.xml
+++ b/android/TerminalApp/res/values-tl/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Nagsasara ang terminal"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Sapilitang isara"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"Na-enable ang <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Mga gawaing matagal gawin"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Mga event ng system"</string>
</resources>
diff --git a/android/TerminalApp/res/values-tr/strings.xml b/android/TerminalApp/res/values-tr/strings.xml
index 4374923..de2f90f 100644
--- a/android/TerminalApp/res/values-tr/strings.xml
+++ b/android/TerminalApp/res/values-tr/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal kapanıyor"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Uygulamayı kapat"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> etkinleştirildi"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-uk/strings.xml b/android/TerminalApp/res/values-uk/strings.xml
index bd2d574..a1f0434 100644
--- a/android/TerminalApp/res/values-uk/strings.xml
+++ b/android/TerminalApp/res/values-uk/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Термінал закривається"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Примусово закрити"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> увімкнено"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-ur/strings.xml b/android/TerminalApp/res/values-ur/strings.xml
index 3ad7c39..9278add 100644
--- a/android/TerminalApp/res/values-ur/strings.xml
+++ b/android/TerminalApp/res/values-ur/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"ٹرمینل بند ہو رہا ہے"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"زبردستی بند کریں"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> فعال ہے"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"لمبے وقت تک چلنے والے ٹاسکس"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"سسٹم ایونٹس"</string>
</resources>
diff --git a/android/TerminalApp/res/values-uz/strings.xml b/android/TerminalApp/res/values-uz/strings.xml
index 159d04c..27ac9f4 100644
--- a/android/TerminalApp/res/values-uz/strings.xml
+++ b/android/TerminalApp/res/values-uz/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal yopilmoqda"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Majburiy yopish"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> yoniq"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"Uzoq davom etuvchi vazifalar"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"Tizim tadbirlari"</string>
</resources>
diff --git a/android/TerminalApp/res/values-vi/strings.xml b/android/TerminalApp/res/values-vi/strings.xml
index d7d6c93..55b4070 100644
--- a/android/TerminalApp/res/values-vi/strings.xml
+++ b/android/TerminalApp/res/values-vi/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Terminal đang đóng"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Buộc đóng"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> đã được bật"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-zh-rCN/strings.xml b/android/TerminalApp/res/values-zh-rCN/strings.xml
index 31cf746..c57f64c 100644
--- a/android/TerminalApp/res/values-zh-rCN/strings.xml
+++ b/android/TerminalApp/res/values-zh-rCN/strings.xml
@@ -87,4 +87,6 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"终端即将关闭"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"强行关闭"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> 已启用"</string>
+ <string name="notification_channel_long_running_name" msgid="7916541360369402952">"长时间运行的任务"</string>
+ <string name="notification_channel_system_events_name" msgid="1004951444029742137">"系统事件"</string>
</resources>
diff --git a/android/TerminalApp/res/values-zh-rHK/strings.xml b/android/TerminalApp/res/values-zh-rHK/strings.xml
index 1284ecc..0a07a92 100644
--- a/android/TerminalApp/res/values-zh-rHK/strings.xml
+++ b/android/TerminalApp/res/values-zh-rHK/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"終端機正在關閉"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"強制關閉"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"已啟用 <xliff:g id="ID_1">VirGL</xliff:g>"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-zh-rTW/strings.xml b/android/TerminalApp/res/values-zh-rTW/strings.xml
index 7391300..fdbdee0 100644
--- a/android/TerminalApp/res/values-zh-rTW/strings.xml
+++ b/android/TerminalApp/res/values-zh-rTW/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"終端機正在關閉"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"強制關閉"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"<xliff:g id="ID_1">VirGL</xliff:g> 已啟用"</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values-zu/strings.xml b/android/TerminalApp/res/values-zu/strings.xml
index 4036ec1..28d8e7b 100644
--- a/android/TerminalApp/res/values-zu/strings.xml
+++ b/android/TerminalApp/res/values-zu/strings.xml
@@ -87,4 +87,8 @@
<string name="service_notification_close_title" msgid="1442526433361428844">"Itheminali iyavalwa"</string>
<string name="service_notification_force_quit_action" msgid="3462226330416157775">"Phoqelela ukuvala"</string>
<string name="virgl_enabled" msgid="5242525588039698086">"I-<xliff:g id="ID_1">VirGL</xliff:g> inikwe amandla."</string>
+ <!-- no translation found for notification_channel_long_running_name (7916541360369402952) -->
+ <skip />
+ <!-- no translation found for notification_channel_system_events_name (1004951444029742137) -->
+ <skip />
</resources>
diff --git a/android/TerminalApp/res/values/strings.xml b/android/TerminalApp/res/values/strings.xml
index d3440d3..44009c3 100644
--- a/android/TerminalApp/res/values/strings.xml
+++ b/android/TerminalApp/res/values/strings.xml
@@ -55,6 +55,9 @@
<!-- Action bar icon name for the settings view CHAR LIMIT=none] -->
<string name="action_settings">Settings</string>
+ <!-- Action bar icon name for showing the display activity CHAR LIMIT=none] -->
+ <string name="action_display">Display</string>
+
<!-- Toast message to notify that preparing terminal to start [CHAR LIMIT=none] -->
<string name="vm_creation_message">Preparing terminal</string>
<!-- Toast message to notify that terminal is stopping [CHAR LIMIT=none] -->
@@ -172,4 +175,10 @@
<!-- This string is for toast message to notify that VirGL is enabled. [CHAR LIMIT=40] -->
<string name="virgl_enabled"><xliff:g>VirGL</xliff:g> is enabled</string>
+
+ <!-- This is the name of the notification channel for long-runnint tasks [CHAR LIMIT=none] -->
+ <string name="notification_channel_long_running_name">Long running tasks</string>
+
+ <!-- This is the name of the notification channel for system events [CHAR LIMIT=none] -->
+ <string name="notification_channel_system_events_name">System events</string>
</resources>
diff --git a/android/TerminalApp/res/values/styles.xml b/android/TerminalApp/res/values/styles.xml
index 3fb8e7d..13f070f 100644
--- a/android/TerminalApp/res/values/styles.xml
+++ b/android/TerminalApp/res/values/styles.xml
@@ -27,4 +27,15 @@
<style name="VmTerminalAppTheme" parent="@style/Theme.Material3.DayNight.NoActionBar">
<item name="android:windowLightStatusBar" tools:targetApi="m">?android:attr/isLightTheme</item>
</style>
+ <style name="FullscreenTheme" parent="@style/Theme.Material3.DayNight.NoActionBar">
+ <item name="android:navigationBarColor">
+ @android:color/transparent
+ </item>
+ <item name="android:statusBarColor">
+ @android:color/transparent
+ </item>
+ <item name="android:windowLayoutInDisplayCutoutMode">
+ always
+ </item>
+ </style>
</resources>
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 57779bf..33f3be1 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -2033,7 +2033,7 @@
Owned(T),
}
-impl<'a, T> AsRef<T> for BorrowedOrOwned<'a, T> {
+impl<T> AsRef<T> for BorrowedOrOwned<'_, T> {
fn as_ref(&self) -> &T {
match self {
Self::Borrowed(b) => b,
diff --git a/build/debian/fai_config/files/etc/avahi/services/ttyd.service/AVF b/build/debian/fai_config/files/etc/avahi/services/ttyd.service/AVF
deleted file mode 100644
index 64f9d0a..0000000
--- a/build/debian/fai_config/files/etc/avahi/services/ttyd.service/AVF
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" standalone='no'?><!--*-nxml-*-->
-<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
-
-<service-group>
-
- <name>ttyd</name>
-
- <service protocol="ipv4">
- <type>_http._tcp</type>
- <port>7681</port>
- </service>
-
-</service-group>
diff --git a/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
index 4a32f2b..d86bab0 100644
--- a/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
+++ b/build/debian/fai_config/files/etc/systemd/system/ttyd.service/AVF
@@ -3,11 +3,14 @@
After=syslog.target
After=network.target
After=virtiofs_internal.service
+
[Service]
ExecStart=/usr/local/bin/ttyd --ssl --ssl-cert /etc/ttyd/server.crt --ssl-key /etc/ttyd/server.key --ssl-ca /mnt/internal/ca.crt -t disableLeaveAlert=true -W login -f droid
+ExecStartPost=/usr/bin/avahi-publish-service ttyd _http._tcp 7681
Type=simple
Restart=always
User=root
Group=root
+
[Install]
WantedBy=multi-user.target
diff --git a/build/debian/fai_config/package_config/AVF b/build/debian/fai_config/package_config/AVF
index 98b558b..bf51fdb 100644
--- a/build/debian/fai_config/package_config/AVF
+++ b/build/debian/fai_config/package_config/AVF
@@ -8,3 +8,5 @@
forwarder-guest
forwarder-guest-launcher
shutdown-runner
+weston
+xwayland
diff --git a/guest/apkdmverity/src/main.rs b/guest/apkdmverity/src/main.rs
index d2f88ae..2fc964b 100644
--- a/guest/apkdmverity/src/main.rs
+++ b/guest/apkdmverity/src/main.rs
@@ -27,6 +27,7 @@
use apkverify::{HashAlgorithm, V4Signature};
use clap::{arg, Arg, ArgAction, Command};
use dm::loopdevice;
+use dm::loopdevice::LoopConfigOptions;
use dm::util;
use dm::verity::{DmVerityHashAlgorithm, DmVerityTargetBuilder};
use itertools::Itertools;
@@ -109,9 +110,13 @@
}
(
loopdevice::attach(
- &apk, 0, apk_size, /* direct_io */ true, /* writable */ false,
+ &apk,
+ 0,
+ apk_size,
+ &LoopConfigOptions { direct_io: true, ..Default::default() },
)
- .context("Failed to attach APK to a loop device")?,
+ .context("Failed to attach APK to a loop device")?
+ .path,
apk_size,
)
};
@@ -125,10 +130,9 @@
// Due to unknown reason(b/191344832), we can't enable "direct IO" for the IDSIG file (backing
// the hash). For now we don't use "direct IO" but it seems OK since the IDSIG file is very
// small and the benefit of direct-IO would be negliable.
- let hash_device = loopdevice::attach(
- &idsig, offset, size, /* direct_io */ false, /* writable */ false,
- )
- .context("Failed to attach idsig to a loop device")?;
+ let hash_device = loopdevice::attach(&idsig, offset, size, &LoopConfigOptions::default())
+ .context("Failed to attach idsig to a loop device")?
+ .path;
// Build a dm-verity target spec from the information from the idsig file. The apk and the
// idsig files are used as the data device and the hash device, respectively.
@@ -347,18 +351,17 @@
// of the data device is done in the scopeguard for the return value of `enable_verity`
// below. Only the idsig_loop_device needs detatching.
let apk_loop_device = loopdevice::attach(
- &apk_path, 0, apk_size, /* direct_io */ true, /* writable */ false,
+ &apk_path,
+ 0,
+ apk_size,
+ &LoopConfigOptions { direct_io: true, ..Default::default() },
)
- .unwrap();
+ .unwrap()
+ .path;
let idsig_loop_device = scopeguard::guard(
- loopdevice::attach(
- &idsig_path,
- 0,
- idsig_size,
- /* direct_io */ false,
- /* writable */ false,
- )
- .unwrap(),
+ loopdevice::attach(&idsig_path, 0, idsig_size, &LoopConfigOptions::default())
+ .unwrap()
+ .path,
|dev| loopdevice::detach(dev).unwrap(),
);
diff --git a/guest/authfs/src/common.rs b/guest/authfs/src/common.rs
index 6556fde..fc5af89 100644
--- a/guest/authfs/src/common.rs
+++ b/guest/authfs/src/common.rs
@@ -18,7 +18,7 @@
pub const CHUNK_SIZE: u64 = 4096;
pub fn divide_roundup(dividend: u64, divisor: u64) -> u64 {
- (dividend + divisor - 1) / divisor
+ dividend.div_ceil(divisor)
}
/// Given `offset` and `length`, generates (offset, size) tuples that together form the same length,
diff --git a/guest/authfs/src/fsverity/metadata/metadata.rs b/guest/authfs/src/fsverity/metadata/metadata.rs
index 54d0145..2e78190 100644
--- a/guest/authfs/src/fsverity/metadata/metadata.rs
+++ b/guest/authfs/src/fsverity/metadata/metadata.rs
@@ -131,8 +131,7 @@
};
// merkle tree is at the next 4K boundary
- let merkle_tree_offset =
- (metadata_file.stream_position()? + CHUNK_SIZE - 1) / CHUNK_SIZE * CHUNK_SIZE;
+ let merkle_tree_offset = (metadata_file.stream_position()?).div_ceil(CHUNK_SIZE) * CHUNK_SIZE;
Ok(Box::new(FSVerityMetadata { header, digest, signature, metadata_file, merkle_tree_offset }))
}
diff --git a/guest/authfs/src/fusefs.rs b/guest/authfs/src/fusefs.rs
index fa4076d..9e49046 100644
--- a/guest/authfs/src/fusefs.rs
+++ b/guest/authfs/src/fusefs.rs
@@ -816,7 +816,7 @@
// FUSE ioctl is limited, thus we can't implement fs-verity ioctls without a
// kernel change (see b/196635431). Until it's possible, use
// xattr to expose what we need as an authfs specific API.
- if name != CStr::from_bytes_with_nul(b"authfs.fsverity.digest\0").unwrap() {
+ if name != c"authfs.fsverity.digest" {
return Err(io::Error::from_raw_os_error(libc::ENODATA));
}
diff --git a/guest/microdroid_launcher/Android.bp b/guest/microdroid_launcher/Android.bp
index 42c18cb..893ee98 100644
--- a/guest/microdroid_launcher/Android.bp
+++ b/guest/microdroid_launcher/Android.bp
@@ -12,5 +12,10 @@
"libdl_android",
"liblog",
],
+ static_libs: [
+ "libapexutil",
+ "libprotobuf-cpp-lite",
+ "lib_apex_manifest_proto_lite",
+ ],
header_libs: ["vm_payload_headers"],
}
diff --git a/guest/microdroid_launcher/main.cpp b/guest/microdroid_launcher/main.cpp
index 5ae9956..8e3d4e4 100644
--- a/guest/microdroid_launcher/main.cpp
+++ b/guest/microdroid_launcher/main.cpp
@@ -16,7 +16,9 @@
#include <android-base/logging.h>
#include <android-base/result.h>
+#include <android-base/strings.h>
#include <android/dlext.h>
+#include <apexutil.h>
#include <dlfcn.h>
#include <cstdlib>
@@ -25,8 +27,11 @@
#include "vm_main.h"
+using android::apex::GetActivePackages;
using android::base::Error;
+using android::base::Join;
using android::base::Result;
+using android::base::StringReplace;
extern "C" {
enum {
@@ -43,6 +48,8 @@
extern bool android_link_namespaces(struct android_namespace_t* from,
struct android_namespace_t* to,
const char* shared_libs_sonames);
+
+extern struct android_namespace_t* android_get_exported_namespace(const char* name);
} // extern "C"
static Result<void*> load(const std::string& libname);
@@ -108,6 +115,32 @@
return Error() << "Failed to link namespace: " << dlerror();
}
+ // Make libraries provided by APEXes available to the payload
+ for (const auto& [_path, manifest] : GetActivePackages("/apex")) {
+ std::string namespace_name = StringReplace(manifest.name(), ".", "_", /* all */ true);
+ android_namespace_t* apex_ns = android_get_exported_namespace(namespace_name.c_str());
+ if (apex_ns == nullptr) {
+ // This means the namespace is configured as 'visible=false'. We can't link to an
+ // invisible namespace.
+ continue;
+ }
+
+ std::vector<std::string> libs = {manifest.providenativelibs().begin(),
+ manifest.providenativelibs().end()};
+ if (libs.size() == 0) {
+ continue;
+ }
+ std::string shared_lib_sonames = Join(libs, ':');
+
+ if (!android_link_namespaces(new_ns, apex_ns, shared_lib_sonames.c_str())) {
+ return Error() << "Failed to link APEX namespace " << namespace_name << ": "
+ << dlerror();
+ }
+
+ LOG(INFO) << "Linked APEX namespace " << namespace_name << " with shared libs "
+ << shared_lib_sonames;
+ }
+
const android_dlextinfo info = {
.flags = ANDROID_DLEXT_USE_NAMESPACE,
.library_namespace = new_ns,
diff --git a/guest/microdroid_manager/src/vm_secret.rs b/guest/microdroid_manager/src/vm_secret.rs
index 56b3482..5999122 100644
--- a/guest/microdroid_manager/src/vm_secret.rs
+++ b/guest/microdroid_manager/src/vm_secret.rs
@@ -171,7 +171,11 @@
return Err(anyhow!("Rollback protected data is not available with V1 secrets"));
};
let payload_id = sha::sha512(instance_id);
- secretkeeper_session.get_secret(payload_id)
+ secretkeeper_session.get_secret(payload_id).or_else(|e| {
+ log::info!("Secretkeeper get failed with {e:?}. Refreshing connection & retrying!");
+ secretkeeper_session.refresh()?;
+ secretkeeper_session.get_secret(payload_id)
+ })
}
pub fn write_payload_data_rp(&self, data: &[u8; SECRET_SIZE]) -> Result<()> {
@@ -180,7 +184,12 @@
return Err(anyhow!("Rollback protected data is not available with V1 secrets"));
};
let payload_id = sha::sha512(instance_id);
- secretkeeper_session.store_secret(payload_id, data)
+ if let Err(e) = secretkeeper_session.store_secret(payload_id, data.clone()) {
+ log::info!("Secretkeeper store failed with {e:?}. Refreshing connection & retrying!");
+ secretkeeper_session.refresh()?;
+ secretkeeper_session.store_secret(payload_id, data)?;
+ }
+ Ok(())
}
}
@@ -276,6 +285,11 @@
Ok(Self { session, sealing_policy })
}
+ fn refresh(&self) -> Result<()> {
+ let mut session = self.session.lock().unwrap();
+ Ok(session.refresh()?)
+ }
+
fn store_secret(&self, id: [u8; ID_SIZE], secret: Zeroizing<[u8; SECRET_SIZE]>) -> Result<()> {
let store_request = StoreSecretRequest {
id: Id(id),
diff --git a/guest/pvmfw/avb/tests/api_test.rs b/guest/pvmfw/avb/tests/api_test.rs
index 23e05d4..29a6277 100644
--- a/guest/pvmfw/avb/tests/api_test.rs
+++ b/guest/pvmfw/avb/tests/api_test.rs
@@ -23,7 +23,6 @@
use std::{
fs,
mem::{offset_of, size_of},
- ptr,
};
use utils::*;
@@ -414,9 +413,9 @@
// vbmeta_header is unaligned; copy flags to local variable
let vbmeta_header_flags = vbmeta_header.flags;
assert_eq!(0, vbmeta_header_flags, "The disable flag should not be set in the latest kernel.");
- let flags_addr = ptr::addr_of!(vbmeta_header.flags) as *const u8;
+ let flags_addr = (&raw const vbmeta_header.flags).cast::<u8>();
// SAFETY: It is safe as both raw pointers `flags_addr` and `vbmeta_header` are not null.
- let flags_offset = unsafe { flags_addr.offset_from(ptr::addr_of!(vbmeta_header) as *const u8) };
+ let flags_offset = unsafe { flags_addr.offset_from((&raw const vbmeta_header).cast::<u8>()) };
let flags_offset = usize::try_from(footer.vbmeta_offset)? + usize::try_from(flags_offset)?;
// Act.
diff --git a/guest/pvmfw/src/dice.rs b/guest/pvmfw/src/dice.rs
index 78bd6b8..4df10b3 100644
--- a/guest/pvmfw/src/dice.rs
+++ b/guest/pvmfw/src/dice.rs
@@ -156,9 +156,7 @@
fn generate_config_descriptor(&self, instance_hash: Option<Hash>) -> Result<Vec<u8>> {
let mut config = Vec::with_capacity(4);
config.push((cbor!(COMPONENT_NAME_KEY)?, cbor!("vm_entry")?));
- if cfg!(dice_changes) {
- config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
- }
+ config.push((cbor!(SECURITY_VERSION_KEY)?, cbor!(self.security_version)?));
if self.rkp_vm_marker {
config.push((cbor!(RKP_VM_MARKER_KEY)?, Value::Null))
}
@@ -245,14 +243,7 @@
assert_eq!(config_map.get(&COMPONENT_NAME_KEY).unwrap().as_text().unwrap(), "vm_entry");
assert_eq!(config_map.get(&COMPONENT_VERSION_KEY), None);
assert_eq!(config_map.get(&RESETTABLE_KEY), None);
- if cfg!(dice_changes) {
- assert_eq!(
- config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(),
- 42.into()
- );
- } else {
- assert_eq!(config_map.get(&SECURITY_VERSION_KEY), None);
- }
+ assert_eq!(config_map.get(&SECURITY_VERSION_KEY).unwrap().as_integer().unwrap(), 42.into());
assert_eq!(config_map.get(&RKP_VM_MARKER_KEY), None);
}
diff --git a/guest/pvmfw/src/fdt.rs b/guest/pvmfw/src/fdt.rs
index 29212f9..818d342 100644
--- a/guest/pvmfw/src/fdt.rs
+++ b/guest/pvmfw/src/fdt.rs
@@ -112,6 +112,24 @@
Ok(None)
}
+/// Read /avf/untrusted/instance-id, if present.
+pub fn read_instance_id(fdt: &Fdt) -> libfdt::Result<Option<&[u8]>> {
+ read_avf_untrusted_prop(fdt, c"instance-id")
+}
+
+/// Read /avf/untrusted/defer-rollback-protection, if present.
+pub fn read_defer_rollback_protection(fdt: &Fdt) -> libfdt::Result<Option<&[u8]>> {
+ read_avf_untrusted_prop(fdt, c"defer-rollback-protection")
+}
+
+fn read_avf_untrusted_prop<'a>(fdt: &'a Fdt, prop: &CStr) -> libfdt::Result<Option<&'a [u8]>> {
+ if let Some(node) = fdt.node(c"/avf/untrusted")? {
+ node.getprop(prop)
+ } else {
+ Ok(None)
+ }
+}
+
fn patch_initrd_range(fdt: &mut Fdt, initrd_range: &Range<usize>) -> libfdt::Result<()> {
let start = u32::try_from(initrd_range.start).unwrap();
let end = u32::try_from(initrd_range.end).unwrap();
diff --git a/guest/pvmfw/src/main.rs b/guest/pvmfw/src/main.rs
index 0a3dca6..afa64e0 100644
--- a/guest/pvmfw/src/main.rs
+++ b/guest/pvmfw/src/main.rs
@@ -35,22 +35,20 @@
use crate::bcc::Bcc;
use crate::dice::PartialInputs;
use crate::entry::RebootReason;
-use crate::fdt::{modify_for_next_stage, sanitize_device_tree};
+use crate::fdt::{modify_for_next_stage, read_instance_id, sanitize_device_tree};
use crate::rollback::perform_rollback_protection;
use alloc::borrow::Cow;
use alloc::boxed::Box;
use bssl_avf::Digester;
use diced_open_dice::{bcc_handover_parse, DiceArtifacts, DiceContext, Hidden, VM_KEY_ALGORITHM};
-use libfdt::{Fdt, FdtNode};
+use libfdt::Fdt;
use log::{debug, error, info, trace, warn};
use pvmfw_avb::verify_payload;
use pvmfw_avb::DebugLevel;
use pvmfw_embedded_key::PUBLIC_KEY;
-use vmbase::fdt::pci::{PciError, PciInfo};
use vmbase::heap;
-use vmbase::memory::{flush, init_shared_pool, SIZE_4KB};
+use vmbase::memory::{flush, SIZE_4KB};
use vmbase::rand;
-use vmbase::virtio::pci;
fn main<'a>(
untrusted_fdt: &mut Fdt,
@@ -77,8 +75,6 @@
})?;
trace!("BCC: {bcc_handover:x?}");
- let cdi_seal = bcc_handover.cdi_seal();
-
let bcc = Bcc::new(bcc_handover.bcc()).map_err(|e| {
error!("{e}");
RebootReason::InvalidBcc
@@ -102,19 +98,8 @@
}
let guest_page_size = verified_boot_data.page_size.unwrap_or(SIZE_4KB);
- let fdt_info = sanitize_device_tree(untrusted_fdt, vm_dtbo, vm_ref_dt, guest_page_size)?;
+ let _ = sanitize_device_tree(untrusted_fdt, vm_dtbo, vm_ref_dt, guest_page_size)?;
let fdt = untrusted_fdt; // DT has now been sanitized.
- let pci_info = PciInfo::from_fdt(fdt).map_err(handle_pci_error)?;
- debug!("PCI: {:#x?}", pci_info);
- // Set up PCI bus for VirtIO devices.
- let mut pci_root = pci::initialize(pci_info).map_err(|e| {
- error!("Failed to initialize PCI: {e}");
- RebootReason::InternalError
- })?;
- init_shared_pool(fdt_info.swiotlb_info.fixed_range()).map_err(|e| {
- error!("Failed to initialize shared pool: {e}");
- RebootReason::InternalError
- })?;
let next_bcc_size = guest_page_size;
let next_bcc = heap::aligned_boxed_slice(next_bcc_size, guest_page_size).ok_or_else(|| {
@@ -129,13 +114,12 @@
RebootReason::InternalError
})?;
- let instance_hash = Some(salt_from_instance_id(fdt)?);
+ let instance_hash = salt_from_instance_id(fdt)?;
let (new_instance, salt, defer_rollback_protection) = perform_rollback_protection(
fdt,
&verified_boot_data,
&dice_inputs,
- &mut pci_root,
- cdi_seal,
+ bcc_handover.cdi_seal(),
instance_hash,
)?;
trace!("Got salt for instance: {salt:x?}");
@@ -204,8 +188,14 @@
// Get the "salt" which is one of the input for DICE derivation.
// This provides differentiation of secrets for different VM instances with same payloads.
-fn salt_from_instance_id(fdt: &Fdt) -> Result<Hidden, RebootReason> {
- let id = instance_id(fdt)?;
+fn salt_from_instance_id(fdt: &Fdt) -> Result<Option<Hidden>, RebootReason> {
+ let Some(id) = read_instance_id(fdt).map_err(|e| {
+ error!("Failed to get instance-id in DT: {e}");
+ RebootReason::InvalidFdt
+ })?
+ else {
+ return Ok(None);
+ };
let salt = Digester::sha512()
.digest(&[&b"InstanceId:"[..], id].concat())
.map_err(|e| {
@@ -214,46 +204,5 @@
})?
.try_into()
.map_err(|_| RebootReason::InternalError)?;
- Ok(salt)
-}
-
-fn instance_id(fdt: &Fdt) -> Result<&[u8], RebootReason> {
- let node = avf_untrusted_node(fdt)?;
- let id = node.getprop(c"instance-id").map_err(|e| {
- error!("Failed to get instance-id in DT: {e}");
- RebootReason::InvalidFdt
- })?;
- id.ok_or_else(|| {
- error!("Missing instance-id");
- RebootReason::InvalidFdt
- })
-}
-
-fn avf_untrusted_node(fdt: &Fdt) -> Result<FdtNode, RebootReason> {
- let node = fdt.node(c"/avf/untrusted").map_err(|e| {
- error!("Failed to get /avf/untrusted node: {e}");
- RebootReason::InvalidFdt
- })?;
- node.ok_or_else(|| {
- error!("/avf/untrusted node is missing in DT");
- RebootReason::InvalidFdt
- })
-}
-
-/// Logs the given PCI error and returns the appropriate `RebootReason`.
-fn handle_pci_error(e: PciError) -> RebootReason {
- error!("{}", e);
- match e {
- PciError::FdtErrorPci(_)
- | PciError::FdtNoPci
- | PciError::FdtErrorReg(_)
- | PciError::FdtMissingReg
- | PciError::FdtRegEmpty
- | PciError::FdtRegMissingSize
- | PciError::CamWrongSize(_)
- | PciError::FdtErrorRanges(_)
- | PciError::FdtMissingRanges
- | PciError::RangeAddressMismatch { .. }
- | PciError::NoSuitableRange => RebootReason::InvalidFdt,
- }
+ Ok(Some(salt))
}
diff --git a/guest/pvmfw/src/rollback.rs b/guest/pvmfw/src/rollback.rs
index f7723d7..74b2cd8 100644
--- a/guest/pvmfw/src/rollback.rs
+++ b/guest/pvmfw/src/rollback.rs
@@ -16,16 +16,20 @@
use crate::dice::PartialInputs;
use crate::entry::RebootReason;
+use crate::fdt::read_defer_rollback_protection;
use crate::instance::EntryBody;
use crate::instance::Error as InstanceError;
use crate::instance::{get_recorded_entry, record_instance_entry};
use diced_open_dice::Hidden;
-use libfdt::{Fdt, FdtNode};
+use libfdt::Fdt;
use log::{error, info};
use pvmfw_avb::Capability;
use pvmfw_avb::VerifiedBootData;
use virtio_drivers::transport::pci::bus::PciRoot;
+use vmbase::fdt::{pci::PciInfo, SwiotlbInfo};
+use vmbase::memory::init_shared_pool;
use vmbase::rand;
+use vmbase::virtio::pci;
/// Performs RBP based on the input payload, current DICE chain, and host-controlled platform.
///
@@ -37,7 +41,6 @@
fdt: &Fdt,
verified_boot_data: &VerifiedBootData,
dice_inputs: &PartialInputs,
- pci_root: &mut PciRoot,
cdi_seal: &[u8],
instance_hash: Option<Hidden>,
) -> Result<(bool, Hidden, bool), RebootReason> {
@@ -53,7 +56,7 @@
skip_rollback_protection()?;
Ok((false, instance_hash.unwrap(), false))
} else {
- perform_legacy_rollback_protection(dice_inputs, pci_root, cdi_seal, instance_hash)
+ perform_legacy_rollback_protection(fdt, dice_inputs, cdi_seal, instance_hash)
}
}
@@ -92,17 +95,18 @@
/// Performs RBP using instance.img where updates require clearing old entries, causing new CDIs.
fn perform_legacy_rollback_protection(
+ fdt: &Fdt,
dice_inputs: &PartialInputs,
- pci_root: &mut PciRoot,
cdi_seal: &[u8],
instance_hash: Option<Hidden>,
) -> Result<(bool, Hidden, bool), RebootReason> {
info!("Fallback to instance.img based rollback checks");
- let (recorded_entry, mut instance_img, header_index) = get_recorded_entry(pci_root, cdi_seal)
- .map_err(|e| {
- error!("Failed to get entry from instance.img: {e}");
- RebootReason::InternalError
- })?;
+ let mut pci_root = initialize_instance_img_device(fdt)?;
+ let (recorded_entry, mut instance_img, header_index) =
+ get_recorded_entry(&mut pci_root, cdi_seal).map_err(|e| {
+ error!("Failed to get entry from instance.img: {e}");
+ RebootReason::InternalError
+ })?;
let (new_instance, salt) = if let Some(entry) = recorded_entry {
check_dice_measurements_match_entry(dice_inputs, &entry)?;
let salt = instance_hash.unwrap_or(entry.salt);
@@ -155,24 +159,34 @@
}
fn should_defer_rollback_protection(fdt: &Fdt) -> Result<bool, RebootReason> {
- let node = avf_untrusted_node(fdt)?;
- let defer_rbp = node
- .getprop(c"defer-rollback-protection")
- .map_err(|e| {
- error!("Failed to get defer-rollback-protection property in DT: {e}");
- RebootReason::InvalidFdt
- })?
- .is_some();
- Ok(defer_rbp)
-}
-
-fn avf_untrusted_node(fdt: &Fdt) -> Result<FdtNode, RebootReason> {
- let node = fdt.node(c"/avf/untrusted").map_err(|e| {
- error!("Failed to get /avf/untrusted node: {e}");
+ let defer_rbp = read_defer_rollback_protection(fdt).map_err(|e| {
+ error!("Failed to get defer-rollback-protection property in DT: {e}");
RebootReason::InvalidFdt
})?;
- node.ok_or_else(|| {
- error!("/avf/untrusted node is missing in DT");
+ Ok(defer_rbp.is_some())
+}
+
+/// Set up PCI bus and VirtIO-blk device containing the instance.img partition.
+fn initialize_instance_img_device(fdt: &Fdt) -> Result<PciRoot, RebootReason> {
+ let pci_info = PciInfo::from_fdt(fdt).map_err(|e| {
+ error!("Failed to detect PCI from DT: {e}");
RebootReason::InvalidFdt
- })
+ })?;
+ let swiotlb_range = SwiotlbInfo::new_from_fdt(fdt)
+ .map_err(|e| {
+ error!("Failed to detect swiotlb from DT: {e}");
+ RebootReason::InvalidFdt
+ })?
+ .and_then(|info| info.fixed_range());
+
+ let pci_root = pci::initialize(pci_info).map_err(|e| {
+ error!("Failed to initialize PCI: {e}");
+ RebootReason::InternalError
+ })?;
+ init_shared_pool(swiotlb_range).map_err(|e| {
+ error!("Failed to initialize shared pool: {e}");
+ RebootReason::InternalError
+ })?;
+
+ Ok(pci_root)
}
diff --git a/guest/rialto/tests/test.rs b/guest/rialto/tests/test.rs
index c94a0e3..d68c568 100644
--- a/guest/rialto/tests/test.rs
+++ b/guest/rialto/tests/test.rs
@@ -54,13 +54,9 @@
const INSTANCE_IMG_PATH: &str = "/data/local/tmp/rialto_test/arm64/instance.img";
const TEST_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
-#[cfg(dice_changes)]
#[test]
fn process_requests_in_protected_vm() -> Result<()> {
if hypervisor_props::is_protected_vm_supported()? {
- // The test is skipped if the feature flag |dice_changes| is not enabled, because when
- // the flag is off, the DICE chain is truncated in the pvmfw, and the service VM cannot
- // verify the chain due to the missing entries in the chain.
check_processing_requests(VmType::ProtectedVm, None)
} else {
warn!("pVMs are not supported on device, skipping test");
diff --git a/guest/trusty/security_vm/launcher/Android.bp b/guest/trusty/security_vm/launcher/Android.bp
index fea8873..ff628fd 100644
--- a/guest/trusty/security_vm/launcher/Android.bp
+++ b/guest/trusty/security_vm/launcher/Android.bp
@@ -18,56 +18,3 @@
false: false,
}),
}
-
-prebuilt_etc {
- name: "lk_trusty.elf",
- system_ext_specific: true,
- relative_install_path: "vm/trusty_vm",
- filename: "lk_trusty.elf",
- arch: {
- x86_64: {
- src: ":trusty_security_vm_signed",
- },
- arm64: {
- src: ":trusty_security_vm_signed",
- },
- },
- src: ":empty_file",
-}
-
-filegroup {
- name: "trusty_vm_sign_key",
- srcs: [":avb_testkey_rsa4096"],
-}
-
-// python -c "import hashlib; print(hashlib.sha256(b'trusty_security_vm_salt').hexdigest())"
-trusty_security_vm_salt = "75a71e967c1a1e0f805cca20465e7acf83e6a04e567a67c426d8b5a94f8d61c5"
-
-TRUSTY_SECURITY_VM_VERSION = 1
-
-avb_add_hash_footer {
- name: "trusty_security_vm_signed",
- filename: "trusty_security_vm_signed",
- partition_name: "boot",
- private_key: ":trusty_vm_sign_key",
- salt: trusty_security_vm_salt,
- rollback_index: TRUSTY_SECURITY_VM_VERSION,
- props: [
- {
- name: "com.android.virt.cap",
- value: "trusty_security_vm",
- },
- ],
- src: ":empty_file",
- enabled: false,
- arch: {
- x86_64: {
- src: ":trusty-lk.elf",
- enabled: true,
- },
- arm64: {
- src: ":trusty-test-lk.elf",
- enabled: true,
- },
- },
-}
diff --git a/guest/trusty/security_vm/vm/Android.bp b/guest/trusty/security_vm/vm/Android.bp
new file mode 100644
index 0000000..f23385b
--- /dev/null
+++ b/guest/trusty/security_vm/vm/Android.bp
@@ -0,0 +1,56 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+prebuilt_etc {
+ name: "lk_trusty.elf",
+ system_ext_specific: true,
+ relative_install_path: "vm/trusty_vm",
+ filename: "lk_trusty.elf",
+ arch: {
+ x86_64: {
+ src: ":trusty_security_vm_signed",
+ },
+ arm64: {
+ src: ":trusty_security_vm_signed",
+ },
+ },
+ src: ":empty_file",
+}
+
+filegroup {
+ name: "trusty_vm_sign_key",
+ srcs: [":avb_testkey_rsa4096"],
+}
+
+// python -c "import hashlib; print(hashlib.sha256(b'trusty_security_vm_salt').hexdigest())"
+trusty_security_vm_salt = "75a71e967c1a1e0f805cca20465e7acf83e6a04e567a67c426d8b5a94f8d61c5"
+
+TRUSTY_SECURITY_VM_VERSION = 1
+
+avb_add_hash_footer {
+ name: "trusty_security_vm_signed",
+ filename: "trusty_security_vm_signed",
+ partition_name: "boot",
+ private_key: ":trusty_vm_sign_key",
+ salt: trusty_security_vm_salt,
+ rollback_index: TRUSTY_SECURITY_VM_VERSION,
+ props: [
+ {
+ name: "com.android.virt.cap",
+ value: "trusty_security_vm",
+ },
+ ],
+ src: ":empty_file",
+ enabled: false,
+ arch: {
+ x86_64: {
+ src: ":trusty-lk.elf",
+ enabled: true,
+ },
+ arm64: {
+ src: ":trusty-test-lk.elf",
+ enabled: true,
+ },
+ },
+}
diff --git a/guest/vmbase_example/Android.bp b/guest/vmbase_example/Android.bp
index ab21191..e5dfc2a 100644
--- a/guest/vmbase_example/Android.bp
+++ b/guest/vmbase_example/Android.bp
@@ -12,6 +12,7 @@
"libdiced_open_dice_nostd",
"liblibfdt_nostd",
"liblog_rust_nostd",
+ "libspin_nostd",
"libvirtio_drivers",
"libvmbase",
],
diff --git a/guest/vmbase_example/src/main.rs b/guest/vmbase_example/src/main.rs
index f5b41bd..b7d2f95 100644
--- a/guest/vmbase_example/src/main.rs
+++ b/guest/vmbase_example/src/main.rs
@@ -26,9 +26,9 @@
use crate::layout::print_addresses;
use crate::pci::check_pci;
use alloc::{vec, vec::Vec};
-use core::ptr::addr_of_mut;
use libfdt::Fdt;
use log::{debug, error, info, trace, warn, LevelFilter};
+use spin::mutex::SpinMutex;
use vmbase::{
bionic, configure_heap,
fdt::pci::PciInfo,
@@ -39,8 +39,8 @@
};
static INITIALISED_DATA: [u32; 4] = [1, 2, 3, 4];
-static mut ZEROED_DATA: [u32; 10] = [0; 10];
-static mut MUTABLE_DATA: [u32; 4] = [1, 2, 3, 4];
+static ZEROED_DATA: SpinMutex<[u32; 10]> = SpinMutex::new([0; 10]);
+static MUTABLE_DATA: SpinMutex<[u32; 4]> = SpinMutex::new([1, 2, 3, 4]);
generate_image_header!();
main!(main);
@@ -103,22 +103,16 @@
fn check_data() {
info!("INITIALISED_DATA: {:?}", INITIALISED_DATA.as_ptr());
- // SAFETY: We only print the addresses of the static mutable variable, not actually access it.
- info!("ZEROED_DATA: {:?}", unsafe { ZEROED_DATA.as_ptr() });
- // SAFETY: We only print the addresses of the static mutable variable, not actually access it.
- info!("MUTABLE_DATA: {:?}", unsafe { MUTABLE_DATA.as_ptr() });
assert_eq!(INITIALISED_DATA[0], 1);
assert_eq!(INITIALISED_DATA[1], 2);
assert_eq!(INITIALISED_DATA[2], 3);
assert_eq!(INITIALISED_DATA[3], 4);
- // SAFETY: Nowhere else in the program accesses this static mutable variable, so there is no
- // chance of concurrent access.
- let zeroed_data = unsafe { &mut *addr_of_mut!(ZEROED_DATA) };
- // SAFETY: Nowhere else in the program accesses this static mutable variable, so there is no
- // chance of concurrent access.
- let mutable_data = unsafe { &mut *addr_of_mut!(MUTABLE_DATA) };
+ let zeroed_data = &mut *ZEROED_DATA.lock();
+ let mutable_data = &mut *MUTABLE_DATA.lock();
+ info!("ZEROED_DATA: {:?}", zeroed_data.as_ptr());
+ info!("MUTABLE_DATA: {:?}", mutable_data.as_ptr());
for element in zeroed_data.iter() {
assert_eq!(*element, 0);
diff --git a/libs/apkverify/src/hashtree.rs b/libs/apkverify/src/hashtree.rs
index 00d8292..54e879b 100644
--- a/libs/apkverify/src/hashtree.rs
+++ b/libs/apkverify/src/hashtree.rs
@@ -84,7 +84,7 @@
let mut level0 = Cursor::new(&mut hash_tree[cur.start..cur.end]);
let mut a_block = vec![0; block_size];
- let mut num_blocks = (input_size + block_size - 1) / block_size;
+ let mut num_blocks = input_size.div_ceil(block_size);
while num_blocks > 0 {
input.read_exact(&mut a_block)?;
let h = hash_one_block(&a_block, salt, block_size, algorithm)?;
@@ -138,7 +138,7 @@
if input_size <= block_size {
break;
}
- let num_blocks = (input_size + block_size - 1) / block_size;
+ let num_blocks = input_size.div_ceil(block_size);
let hashes_size = round_to_multiple(num_blocks * digest_size, block_size);
level_sizes.push(hashes_size);
}
diff --git a/libs/bssl/src/cbb.rs b/libs/bssl/src/cbb.rs
index a48c714..282a77d 100644
--- a/libs/bssl/src/cbb.rs
+++ b/libs/bssl/src/cbb.rs
@@ -40,13 +40,13 @@
}
}
-impl<'a> AsRef<CBB> for CbbFixed<'a> {
+impl AsRef<CBB> for CbbFixed<'_> {
fn as_ref(&self) -> &CBB {
&self.cbb
}
}
-impl<'a> AsMut<CBB> for CbbFixed<'a> {
+impl AsMut<CBB> for CbbFixed<'_> {
fn as_mut(&mut self) -> &mut CBB {
&mut self.cbb
}
diff --git a/libs/bssl/src/cbs.rs b/libs/bssl/src/cbs.rs
index 12671cf..166484c 100644
--- a/libs/bssl/src/cbs.rs
+++ b/libs/bssl/src/cbs.rs
@@ -42,13 +42,13 @@
}
}
-impl<'a> AsRef<CBS> for Cbs<'a> {
+impl AsRef<CBS> for Cbs<'_> {
fn as_ref(&self) -> &CBS {
&self.cbs
}
}
-impl<'a> AsMut<CBS> for Cbs<'a> {
+impl AsMut<CBS> for Cbs<'_> {
fn as_mut(&mut self) -> &mut CBS {
&mut self.cbs
}
diff --git a/libs/bssl/src/ec_key.rs b/libs/bssl/src/ec_key.rs
index 3e2e382..da9eb77 100644
--- a/libs/bssl/src/ec_key.rs
+++ b/libs/bssl/src/ec_key.rs
@@ -471,7 +471,7 @@
/// Wrapper of an `EC_GROUP` reference.
struct EcGroup<'a>(&'a EC_GROUP);
-impl<'a> EcGroup<'a> {
+impl EcGroup<'_> {
/// Returns the NID that identifies the EC group of the key.
fn curve_nid(&self) -> i32 {
// SAFETY: It is safe since the inner pointer is valid and points to an initialized
@@ -518,7 +518,7 @@
}
}
-impl<'a> AsRef<EC_GROUP> for EcGroup<'a> {
+impl AsRef<EC_GROUP> for EcGroup<'_> {
fn as_ref(&self) -> &EC_GROUP {
self.0
}
diff --git a/libs/devicemapper/Android.bp b/libs/devicemapper/Android.bp
index 5332469..6b7f680 100644
--- a/libs/devicemapper/Android.bp
+++ b/libs/devicemapper/Android.bp
@@ -8,7 +8,6 @@
defaults: ["avf_build_flags_rust"],
srcs: ["src/lib.rs"],
edition: "2021",
- prefer_rlib: true,
rustlibs: [
"libanyhow",
"libbitflags",
@@ -18,16 +17,12 @@
"libuuid",
"libzerocopy",
],
- multilib: {
- lib32: {
- enabled: false,
- },
- },
}
rust_library {
name: "libdm_rust",
defaults: ["libdm_rust.defaults"],
+ host_supported: true,
}
rust_test {
diff --git a/libs/devicemapper/src/crypt.rs b/libs/devicemapper/src/crypt.rs
index 75417ed..1326caf 100644
--- a/libs/devicemapper/src/crypt.rs
+++ b/libs/devicemapper/src/crypt.rs
@@ -87,7 +87,7 @@
opt_params: Vec<&'a str>,
}
-impl<'a> Default for DmCryptTargetBuilder<'a> {
+impl Default for DmCryptTargetBuilder<'_> {
fn default() -> Self {
DmCryptTargetBuilder {
cipher: CipherType::AES256HCTR2,
diff --git a/libs/devicemapper/src/lib.rs b/libs/devicemapper/src/lib.rs
index a8f3049..a8c2833 100644
--- a/libs/devicemapper/src/lib.rs
+++ b/libs/devicemapper/src/lib.rs
@@ -235,6 +235,7 @@
#[cfg(test)]
mod tests {
use super::*;
+ use crate::loopdevice::LoopConfigOptions;
use crypt::{CipherType, DmCryptTargetBuilder};
use rdroidtest::{ignore_if, rdroidtest};
use rustutils::system_properties;
@@ -328,10 +329,10 @@
backing_file,
0,
sz,
- /* direct_io */ true,
- /* writable */ true,
+ &LoopConfigOptions { direct_io: true, writable: true, ..Default::default() },
)
- .unwrap();
+ .unwrap()
+ .path;
let device_diff = device.to_owned() + "_diff";
scopeguard::defer! {
@@ -372,10 +373,10 @@
backing_file,
0,
sz,
- /* direct_io */ true,
- /* writable */ true,
+ &LoopConfigOptions { direct_io: true, writable: true, ..Default::default() },
)
- .unwrap();
+ .unwrap()
+ .path;
let device_diff = device.to_owned() + "_diff";
scopeguard::defer! {
loopdevice::detach(&data_device).unwrap();
diff --git a/libs/devicemapper/src/loopdevice.rs b/libs/devicemapper/src/loopdevice.rs
index 130c1c4..b830eda 100644
--- a/libs/devicemapper/src/loopdevice.rs
+++ b/libs/devicemapper/src/loopdevice.rs
@@ -59,14 +59,31 @@
Ok(unsafe { _loop_clr_fd(device_file.as_raw_fd()) }?)
}
+/// LOOP_CONFIGURE ioctl operation flags.
+#[derive(Default)]
+pub struct LoopConfigOptions {
+ /// Whether to use direct I/O
+ pub direct_io: bool,
+ /// Whether the device is writable
+ pub writable: bool,
+ /// Whether to autodestruct the device on last close
+ pub autoclear: bool,
+}
+
+pub struct LoopDevice {
+ /// The loop device file
+ pub file: File,
+ /// Path to the loop device
+ pub path: PathBuf,
+}
+
/// Creates a loop device and attach the given file at `path` as the backing store.
pub fn attach<P: AsRef<Path>>(
path: P,
offset: u64,
size_limit: u64,
- direct_io: bool,
- writable: bool,
-) -> Result<PathBuf> {
+ options: &LoopConfigOptions,
+) -> Result<LoopDevice> {
// Attaching a file to a loop device can make a race condition; a loop device number obtained
// from LOOP_CTL_GET_FREE might have been used by another thread or process. In that case the
// subsequent LOOP_CONFIGURE ioctl returns with EBUSY. Try until it succeeds.
@@ -80,8 +97,8 @@
let begin = Instant::now();
loop {
- match try_attach(&path, offset, size_limit, direct_io, writable) {
- Ok(loop_dev) => return Ok(loop_dev),
+ match try_attach(&path, offset, size_limit, options) {
+ Ok(loop_device) => return Ok(loop_device),
Err(e) => {
if begin.elapsed() > TIMEOUT {
return Err(e);
@@ -102,9 +119,8 @@
path: P,
offset: u64,
size_limit: u64,
- direct_io: bool,
- writable: bool,
-) -> Result<PathBuf> {
+ options: &LoopConfigOptions,
+) -> Result<LoopDevice> {
// Get a free loop device
wait_for_path(LOOP_CONTROL)?;
let ctrl_file = OpenOptions::new()
@@ -117,8 +133,8 @@
// Construct the loop_info64 struct
let backing_file = OpenOptions::new()
.read(true)
- .write(writable)
- .custom_flags(if direct_io { O_DIRECT } else { 0 })
+ .write(options.writable)
+ .custom_flags(if options.direct_io { O_DIRECT } else { 0 })
.open(&path)
.context(format!("failed to open {:?}", path.as_ref()))?;
let mut config = loop_config::new_zeroed();
@@ -127,14 +143,18 @@
config.info.lo_offset = offset;
config.info.lo_sizelimit = size_limit;
- if !writable {
+ if !options.writable {
config.info.lo_flags = Flag::LO_FLAGS_READ_ONLY;
}
- if direct_io {
+ if options.direct_io {
config.info.lo_flags.insert(Flag::LO_FLAGS_DIRECT_IO);
}
+ if options.autoclear {
+ config.info.lo_flags.insert(Flag::LO_FLAGS_AUTOCLEAR);
+ }
+
// Configure the loop device to attach the backing file
let device_path = format!("{}{}", LOOP_DEV_PREFIX, num);
wait_for_path(&device_path)?;
@@ -146,7 +166,7 @@
loop_configure(&device_file, &config)
.context(format!("Failed to configure {:?}", &device_path))?;
- Ok(PathBuf::from(device_path))
+ Ok(LoopDevice { file: device_file, path: PathBuf::from(device_path) })
}
/// Detaches backing file from the loop device `path`.
@@ -185,7 +205,10 @@
let a_file = a_dir.path().join("test");
let a_size = 4096u64;
create_empty_file(&a_file, a_size);
- let dev = attach(a_file, 0, a_size, /* direct_io */ true, /* writable */ false).unwrap();
+ let dev =
+ attach(a_file, 0, a_size, &LoopConfigOptions { direct_io: true, ..Default::default() })
+ .unwrap()
+ .path;
scopeguard::defer! {
detach(&dev).unwrap();
}
@@ -198,7 +221,7 @@
let a_file = a_dir.path().join("test");
let a_size = 4096u64;
create_empty_file(&a_file, a_size);
- let dev = attach(a_file, 0, a_size, /* direct_io */ false, /* writable */ false).unwrap();
+ let dev = attach(a_file, 0, a_size, &LoopConfigOptions::default()).unwrap().path;
scopeguard::defer! {
detach(&dev).unwrap();
}
@@ -211,11 +234,34 @@
let a_file = a_dir.path().join("test");
let a_size = 4096u64;
create_empty_file(&a_file, a_size);
- let dev = attach(a_file, 0, a_size, /* direct_io */ true, /* writable */ true).unwrap();
+ let dev = attach(
+ a_file,
+ 0,
+ a_size,
+ &LoopConfigOptions { direct_io: true, writable: true, ..Default::default() },
+ )
+ .unwrap()
+ .path;
scopeguard::defer! {
detach(&dev).unwrap();
}
assert!(is_direct_io(&dev));
assert!(is_direct_io_writable(&dev));
}
+
+ #[rdroidtest]
+ fn attach_loop_device_autoclear() {
+ let a_dir = tempfile::TempDir::new().unwrap();
+ let a_file = a_dir.path().join("test");
+ let a_size = 4096u64;
+ create_empty_file(&a_file, a_size);
+ let dev =
+ attach(a_file, 0, a_size, &LoopConfigOptions { autoclear: true, ..Default::default() })
+ .unwrap();
+ drop(dev.file);
+
+ let dev_size_path =
+ Path::new("/sys/block").join(dev.path.file_name().unwrap()).join("size");
+ assert_eq!("0", fs::read_to_string(dev_size_path).unwrap().trim());
+ }
}
diff --git a/libs/devicemapper/src/verity.rs b/libs/devicemapper/src/verity.rs
index 09087da..100320b 100644
--- a/libs/devicemapper/src/verity.rs
+++ b/libs/devicemapper/src/verity.rs
@@ -66,7 +66,7 @@
}
}
-impl<'a> Default for DmVerityTargetBuilder<'a> {
+impl Default for DmVerityTargetBuilder<'_> {
fn default() -> Self {
DmVerityTargetBuilder {
version: DmVerityVersion::V1,
diff --git a/libs/dice/driver/src/lib.rs b/libs/dice/driver/src/lib.rs
index b5c1f12..245bf11 100644
--- a/libs/dice/driver/src/lib.rs
+++ b/libs/dice/driver/src/lib.rs
@@ -185,7 +185,6 @@
#[cfg(test)]
mod tests {
use super::*;
- use core::ffi::CStr;
use diced_open_dice::{
hash, retry_bcc_format_config_descriptor, DiceConfigValues, HIDDEN_SIZE,
};
@@ -233,10 +232,7 @@
let dice = DiceDriver::from_file(&file_path)?;
- let values = DiceConfigValues {
- component_name: Some(CStr::from_bytes_with_nul(b"test\0")?),
- ..Default::default()
- };
+ let values = DiceConfigValues { component_name: Some(c"test"), ..Default::default() };
let desc = retry_bcc_format_config_descriptor(&values)?;
let code_hash = hash(&String::from("test code hash").into_bytes())?;
let authority_hash = hash(&String::from("test authority hash").into_bytes())?;
diff --git a/libs/dice/open_dice/src/bcc.rs b/libs/dice/open_dice/src/bcc.rs
index a3ddd76..1d9039d 100644
--- a/libs/dice/open_dice/src/bcc.rs
+++ b/libs/dice/open_dice/src/bcc.rs
@@ -172,7 +172,7 @@
bcc: Option<&'a [u8]>,
}
-impl<'a> DiceArtifacts for BccHandover<'a> {
+impl DiceArtifacts for BccHandover<'_> {
fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
self.cdi_attest
}
diff --git a/libs/dice/open_dice/src/error.rs b/libs/dice/open_dice/src/error.rs
index 9089432..c9eb5cc 100644
--- a/libs/dice/open_dice/src/error.rs
+++ b/libs/dice/open_dice/src/error.rs
@@ -31,6 +31,8 @@
PlatformError,
/// Unsupported key algorithm.
UnsupportedKeyAlgorithm(coset::iana::Algorithm),
+ /// A failed fallible allocation. Used in no_std environments.
+ MemoryAllocationError,
}
/// This makes `DiceError` accepted by anyhow.
@@ -48,6 +50,7 @@
Self::UnsupportedKeyAlgorithm(algorithm) => {
write!(f, "Unsupported key algorithm: {algorithm:?}")
}
+ Self::MemoryAllocationError => write!(f, "Memory allocation failed"),
}
}
}
diff --git a/libs/dice/open_dice/src/retry.rs b/libs/dice/open_dice/src/retry.rs
index 6e75e91..803673d 100644
--- a/libs/dice/open_dice/src/retry.rs
+++ b/libs/dice/open_dice/src/retry.rs
@@ -13,9 +13,9 @@
// limitations under the License.
//! This module implements a retry version for multiple DICE functions that
-//! require preallocated output buffer. As the retry functions require
-//! memory allocation on heap, currently we only expose these functions in
-//! std environment.
+//! require preallocated output buffer. When running without std the allocation
+//! of this buffer may fail and callers will see Error::MemoryAllocationError.
+//! When running with std, allocation may fail.
use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow, DiceConfigValues};
use crate::dice::{
@@ -62,6 +62,9 @@
let mut buffer = Vec::new();
match f(&mut buffer) {
Err(DiceError::BufferTooSmall(actual_size)) => {
+ #[cfg(not(feature = "std"))]
+ buffer.try_reserve_exact(actual_size).map_err(|_| DiceError::MemoryAllocationError)?;
+
buffer.resize(actual_size, 0);
f(&mut buffer)?;
}
diff --git a/libs/dice/sample_inputs/src/sample_inputs.rs b/libs/dice/sample_inputs/src/sample_inputs.rs
index c323bc4..adca46b 100644
--- a/libs/dice/sample_inputs/src/sample_inputs.rs
+++ b/libs/dice/sample_inputs/src/sample_inputs.rs
@@ -18,7 +18,6 @@
use alloc::vec;
use alloc::vec::Vec;
use ciborium::{de, ser, value::Value};
-use core::ffi::CStr;
use coset::{iana, Algorithm, AsCborValue, CoseKey, KeyOperation, KeyType, Label};
use diced_open_dice::{
derive_cdi_private_key_seed, keypair_from_seed, retry_bcc_format_config_descriptor,
@@ -115,7 +114,7 @@
// Gets the ABL certificate to as the root certificate of DICE chain.
let config_values = DiceConfigValues {
- component_name: Some(CStr::from_bytes_with_nul(b"ABL\0").unwrap()),
+ component_name: Some(c"ABL"),
component_version: Some(1),
resettable: true,
security_version: Some(10),
@@ -148,7 +147,7 @@
// Appends AVB certificate to DICE chain.
let config_values = DiceConfigValues {
- component_name: Some(CStr::from_bytes_with_nul(b"AVB\0").unwrap()),
+ component_name: Some(c"AVB"),
component_version: Some(1),
resettable: true,
security_version: Some(11),
@@ -173,7 +172,7 @@
// Appends Android certificate to DICE chain.
let config_values = DiceConfigValues {
- component_name: Some(CStr::from_bytes_with_nul(b"Android\0").unwrap()),
+ component_name: Some(c"Android"),
component_version: Some(12),
resettable: true,
security_version: Some(12),
diff --git a/libs/libfdt/src/iterators.rs b/libs/libfdt/src/iterators.rs
index 743c52b..1c66e4d 100644
--- a/libs/libfdt/src/iterators.rs
+++ b/libs/libfdt/src/iterators.rs
@@ -66,7 +66,7 @@
}
}
-impl<'a> Iterator for CellIterator<'a> {
+impl Iterator for CellIterator<'_> {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
@@ -118,7 +118,7 @@
}
}
-impl<'a> Iterator for RegIterator<'a> {
+impl Iterator for RegIterator<'_> {
type Item = Reg<u64>;
fn next(&mut self) -> Option<Self::Item> {
@@ -161,7 +161,7 @@
}
}
-impl<'a> Iterator for MemRegIterator<'a> {
+impl Iterator for MemRegIterator<'_> {
type Item = Range<usize>;
fn next(&mut self) -> Option<Self::Item> {
@@ -215,8 +215,8 @@
}
}
-impl<'a, A: FromAddrCells, P: FromAddrCells, S: FromSizeCells> Iterator
- for RangesIterator<'a, A, P, S>
+impl<A: FromAddrCells, P: FromAddrCells, S: FromSizeCells> Iterator
+ for RangesIterator<'_, A, P, S>
{
type Item = AddressRange<A, P, S>;
diff --git a/libs/libfdt/src/lib.rs b/libs/libfdt/src/lib.rs
index 0dcd31a..47f4817 100644
--- a/libs/libfdt/src/lib.rs
+++ b/libs/libfdt/src/lib.rs
@@ -344,7 +344,7 @@
}
}
-impl<'a> PartialEq for FdtNode<'a> {
+impl PartialEq for FdtNode<'_> {
fn eq(&self, other: &Self) -> bool {
self.fdt.as_ptr() == other.fdt.as_ptr() && self.offset == other.offset
}
diff --git a/libs/libservice_vm_requests/src/cert.rs b/libs/libservice_vm_requests/src/cert.rs
index e31d870..de5ae1a 100644
--- a/libs/libservice_vm_requests/src/cert.rs
+++ b/libs/libservice_vm_requests/src/cert.rs
@@ -58,7 +58,7 @@
vm_components: Vec<VmComponent<'a>>,
}
-impl<'a> AssociatedOid for AttestationExtension<'a> {
+impl AssociatedOid for AttestationExtension<'_> {
const OID: ObjectIdentifier = AVF_ATTESTATION_EXTENSION_V1;
}
diff --git a/libs/libvm_payload/src/lib.rs b/libs/libvm_payload/src/lib.rs
index cbadec2..14aff99 100644
--- a/libs/libvm_payload/src/lib.rs
+++ b/libs/libvm_payload/src/lib.rs
@@ -27,7 +27,7 @@
use rpcbinder::{RpcServer, RpcSession};
use openssl::{ec::EcKey, sha::sha256, ecdsa::EcdsaSig};
use std::convert::Infallible;
-use std::ffi::{CString, CStr};
+use std::ffi::CString;
use std::fmt::Debug;
use std::os::raw::{c_char, c_void};
use std::path::Path;
@@ -376,20 +376,16 @@
#[no_mangle]
pub extern "C" fn AVmAttestationStatus_toString(status: AVmAttestationStatus) -> *const c_char {
let message = match status {
- AVmAttestationStatus::ATTESTATION_OK => {
- CStr::from_bytes_with_nul(b"The remote attestation completes successfully.\0").unwrap()
- }
+ AVmAttestationStatus::ATTESTATION_OK => c"The remote attestation completes successfully.",
AVmAttestationStatus::ATTESTATION_ERROR_INVALID_CHALLENGE => {
- CStr::from_bytes_with_nul(b"The challenge size is not between 0 and 64.\0").unwrap()
+ c"The challenge size is not between 0 and 64."
}
AVmAttestationStatus::ATTESTATION_ERROR_ATTESTATION_FAILED => {
- CStr::from_bytes_with_nul(b"Failed to attest the VM. Please retry at a later time.\0")
- .unwrap()
+ c"Failed to attest the VM. Please retry at a later time."
}
- AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => CStr::from_bytes_with_nul(
- b"Remote attestation is not supported in the current environment.\0",
- )
- .unwrap(),
+ AVmAttestationStatus::ATTESTATION_ERROR_UNSUPPORTED => {
+ c"Remote attestation is not supported in the current environment."
+ }
};
message.as_ptr()
}
diff --git a/libs/libvm_payload/wrapper/attestation.rs b/libs/libvm_payload/wrapper/attestation.rs
index e0055d5..69fef4f 100644
--- a/libs/libvm_payload/wrapper/attestation.rs
+++ b/libs/libvm_payload/wrapper/attestation.rs
@@ -265,7 +265,7 @@
current: usize, // Invariant: current <= count
}
-impl<'a> Iterator for CertIterator<'a> {
+impl Iterator for CertIterator<'_> {
type Item = Vec<u8>;
fn next(&mut self) -> Option<Self::Item> {
@@ -284,5 +284,5 @@
}
}
-impl<'a> ExactSizeIterator for CertIterator<'a> {}
-impl<'a> FusedIterator for CertIterator<'a> {}
+impl ExactSizeIterator for CertIterator<'_> {}
+impl FusedIterator for CertIterator<'_> {}
diff --git a/libs/libvmbase/src/bionic.rs b/libs/libvmbase/src/bionic.rs
index 37b6e45..2b59493 100644
--- a/libs/libvmbase/src/bionic.rs
+++ b/libs/libvmbase/src/bionic.rs
@@ -20,7 +20,6 @@
use core::ffi::c_int;
use core::ffi::c_void;
use core::ffi::CStr;
-use core::ptr::addr_of_mut;
use core::slice;
use core::str;
@@ -71,11 +70,10 @@
pub static mut ERRNO: c_int = 0;
#[no_mangle]
-#[allow(unused_unsafe)]
+// SAFETY: C functions which call this are only called from the main thread, not from exception
+// handlers.
unsafe extern "C" fn __errno() -> *mut c_int {
- // SAFETY: C functions which call this are only called from the main thread, not from exception
- // handlers.
- unsafe { addr_of_mut!(ERRNO) as *mut _ }
+ (&raw mut ERRNO).cast()
}
fn set_errno(value: c_int) {
@@ -88,15 +86,20 @@
unsafe { ERRNO }
}
+/// # Safety
+///
+/// `buffer` must point to an allocation of at least `length` bytes which is valid to write to and
+/// has no concurrent access while this function is running.
#[no_mangle]
-extern "C" fn getentropy(buffer: *mut c_void, length: usize) -> c_int {
+unsafe extern "C" fn getentropy(buffer: *mut c_void, length: usize) -> c_int {
if length > 256 {
// The maximum permitted value for the length argument is 256.
set_errno(EIO);
return -1;
}
- // SAFETY: Just like libc, we need to assume that `ptr` is valid.
+ // SAFETY: The caller promised that `buffer` is a valid pointer to at least `length` bytes with
+ // no concurrent access.
let buffer = unsafe { slice::from_raw_parts_mut(buffer.cast::<u8>(), length) };
fill_with_entropy(buffer).unwrap();
@@ -169,9 +172,13 @@
#[no_mangle]
static stderr: CFilePtr = CFilePtr::Stderr;
+/// # Safety
+///
+/// `c_str` must be a valid pointer to a NUL-terminated string which is not modified before this
+/// function returns.
#[no_mangle]
-extern "C" fn fputs(c_str: *const c_char, stream: usize) -> c_int {
- // SAFETY: Just like libc, we need to assume that `s` is a valid NULL-terminated string.
+unsafe extern "C" fn fputs(c_str: *const c_char, stream: usize) -> c_int {
+ // SAFETY: The caller promised that `c_str` is a valid NUL-terminated string.
let c_str = unsafe { CStr::from_ptr(c_str) };
if let (Ok(s), Ok(f)) = (c_str.to_str(), CFilePtr::try_from(stream)) {
@@ -183,11 +190,16 @@
}
}
+/// # Safety
+///
+/// `ptr` must be a valid pointer to an array of at least `size * nmemb` initialised bytes, which
+/// are not modified before this function returns.
#[no_mangle]
-extern "C" fn fwrite(ptr: *const c_void, size: usize, nmemb: usize, stream: usize) -> usize {
+unsafe extern "C" fn fwrite(ptr: *const c_void, size: usize, nmemb: usize, stream: usize) -> usize {
let length = size.saturating_mul(nmemb);
- // SAFETY: Just like libc, we need to assume that `ptr` is valid.
+ // SAFETY: The caller promised that `ptr` is a valid pointer to at least `size * nmemb`
+ // initialised bytes, and `length` is no more than that.
let bytes = unsafe { slice::from_raw_parts(ptr as *const u8, length) };
if let (Ok(s), Ok(f)) = (str::from_utf8(bytes), CFilePtr::try_from(stream)) {
@@ -203,12 +215,16 @@
cstr_error(n).as_ptr().cast_mut().cast()
}
+/// # Safety
+///
+/// `s` must be a valid pointer to a NUL-terminated string which is not modified before this
+/// function returns.
#[no_mangle]
-extern "C" fn perror(s: *const c_char) {
+unsafe extern "C" fn perror(s: *const c_char) {
let prefix = if s.is_null() {
None
} else {
- // SAFETY: Just like libc, we need to assume that `s` is a valid NULL-terminated string.
+ // SAFETY: The caller promised that `s` is a valid NUL-terminated string.
let c_str = unsafe { CStr::from_ptr(s) };
if c_str.is_empty() {
None
diff --git a/libs/libvmbase/src/layout.rs b/libs/libvmbase/src/layout.rs
index cf3a8fc..4c45eb2 100644
--- a/libs/libvmbase/src/layout.rs
+++ b/libs/libvmbase/src/layout.rs
@@ -22,7 +22,6 @@
use crate::memory::{max_stack_size, page_4kb_of, PAGE_SIZE};
use aarch64_paging::paging::VirtualAddress;
use core::ops::Range;
-use core::ptr::addr_of;
use static_assertions::const_assert_eq;
/// First address that can't be translated by a level 1 TTBR0_EL1.
@@ -44,9 +43,7 @@
#[macro_export]
macro_rules! linker_addr {
($symbol:ident) => {{
- // SAFETY: We're just getting the address of an extern static symbol provided by the linker,
- // not dereferencing it.
- let addr = unsafe { addr_of!($crate::linker::$symbol) as usize };
+ let addr = (&raw const $crate::linker::$symbol) as usize;
VirtualAddress(addr)
}};
}
@@ -132,5 +129,5 @@
// SAFETY: __stack_chk_guard shouldn't have any mutable aliases unless the stack overflows. If
// it does, then there could be undefined behaviour all over the program, but we want to at
// least have a chance at catching it.
- unsafe { addr_of!(__stack_chk_guard).read_volatile() }
+ unsafe { (&raw const __stack_chk_guard).read_volatile() }
}
diff --git a/libs/statslog_virtualization/Android.bp b/libs/statslog_virtualization/Android.bp
index 2860e6c..f33a147 100644
--- a/libs/statslog_virtualization/Android.bp
+++ b/libs/statslog_virtualization/Android.bp
@@ -72,4 +72,7 @@
rustlibs: [
"libstatslog_virtualization_rust_header",
],
+ flags: [
+ "-A clippy::needless-lifetimes",
+ ],
}
diff --git a/tests/aidl/Android.bp b/tests/aidl/Android.bp
index ed4e8ff..63db488 100644
--- a/tests/aidl/Android.bp
+++ b/tests/aidl/Android.bp
@@ -17,5 +17,8 @@
cpp: {
enabled: true,
},
+ ndk: {
+ min_sdk_version: "UpsideDownCake",
+ },
},
}
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 109c5e0..1d827b9 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -27,6 +27,8 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assume.assumeTrue;
+
import android.app.Application;
import android.app.Instrumentation;
import android.content.ComponentCallbacks2;
@@ -69,13 +71,16 @@
import java.io.Writer;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.OptionalLong;
+import java.util.Random;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
@@ -927,4 +932,169 @@
}
reportMetrics(requestAttestationTime, "request_attestation_time", "microsecond");
}
+
+ List<Double> rpDataAccessWithExistingSession(boolean measureWrite) throws Exception {
+ assumeTrue(
+ "Rollback protected secrets are only available in Updatable VMs",
+ isUpdatableVmSupported());
+ final int NUM_WARMUPS = 10;
+ final int NUM_REQUESTS = 10_000;
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_NONE)
+ .build();
+
+ byte[] data = new byte[32];
+ Arrays.fill(data, (byte) 0xcc);
+
+ List<Double> requestLatencies = new ArrayList<>(NUM_REQUESTS);
+ VirtualMachine vm = forceCreateNewVirtualMachine("rp_data_access", config);
+ TestResults testResult =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ tr.mTimings = new long[NUM_REQUESTS];
+ for (int i = 0; i < NUM_WARMUPS; i++) {
+ ts.insecurelyWritePayloadRpData(data);
+ ts.insecurelyReadPayloadRpData();
+ }
+ for (int i = 0; i < NUM_REQUESTS; i++) {
+ long start = System.nanoTime();
+ if (measureWrite) {
+ ts.insecurelyWritePayloadRpData(data);
+ tr.mTimings[i] = System.nanoTime() - start;
+ } else {
+ tr.mPayloadRpData = ts.insecurelyReadPayloadRpData();
+ tr.mTimings[i] = System.nanoTime() - start;
+ assertThat(tr.mPayloadRpData).isEqualTo(data);
+ }
+ }
+ });
+ // Correctness check.
+ testResult.assertNoException();
+ for (long timings : testResult.mTimings) {
+ requestLatencies.add((double) timings / NANO_TO_MICRO);
+ }
+ return requestLatencies;
+ }
+
+ @Test
+ public void rpDataReadWithExistingSession() throws Exception {
+ reportMetrics(
+ rpDataAccessWithExistingSession(false),
+ "latency/readRollbackProtectedSecretWithExistingSession",
+ "us");
+ }
+
+ @Test
+ public void rpDataWriteWithExistingSession() throws Exception {
+ reportMetrics(
+ rpDataAccessWithExistingSession(true),
+ "latency/writeRollbackProtectedSecretWithExistingSession",
+ "us");
+ }
+
+ List<Double> rpDataAccessWithRefreshingSession(boolean measureWrite) throws Exception {
+ assumeTrue(
+ "Rollback protected secrets are only available in Updatable VMs",
+ isUpdatableVmSupported());
+ final long vmSize = minMemoryRequired();
+ final int numVMs = 8;
+ final int NUM_REQUESTS = 10;
+ final long availableMem = getAvailableMemory();
+
+ // Let's not use more than half of the available memory
+ assume().withMessage("Available memory (" + availableMem + " bytes) too small")
+ .that((numVMs * vmSize) <= (availableMem / 2))
+ .isTrue();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .setMemoryBytes(vmSize)
+ .build();
+
+ byte[] data = new byte[32];
+ Arrays.fill(data, (byte) 0xcc);
+
+ List<Double> requestLatencies = new ArrayList<>(numVMs * NUM_REQUESTS);
+ CompletableFuture<TestResults>[] resultFutureList = new CompletableFuture[numVMs];
+ RunTestsAgainstTestService testToRun =
+ (ts, tr) -> {
+ tr.mTimings = new long[NUM_REQUESTS];
+ // Warm up request!
+ ts.insecurelyWritePayloadRpData(data);
+ for (int j = 0; j < NUM_REQUESTS; j++) {
+ // Sleep time between 2 requests.
+ // Randomized
+ // between 200ms-300ms.
+ long rnd_sleep_time = (long) (200.0 + new Random().nextDouble() * 100);
+ Thread.sleep(rnd_sleep_time); // Sleep
+ long start = System.nanoTime();
+ if (measureWrite) {
+ // Write
+ ts.insecurelyWritePayloadRpData(data);
+ tr.mTimings[j] = System.nanoTime() - start;
+
+ } else {
+ tr.mPayloadRpData = ts.insecurelyReadPayloadRpData();
+ tr.mTimings[j] = System.nanoTime() - start;
+ assertThat(tr.mPayloadRpData).isEqualTo(data);
+ }
+ }
+ };
+ for (int i = 0; i < numVMs; i++) {
+ final VirtualMachine vm =
+ forceCreateNewVirtualMachine("rp_data_access_refresh" + i, config);
+ resultFutureList[i] =
+ CompletableFuture.supplyAsync(
+ () -> {
+ try {
+ TestResults testResult = runVmTestService(TAG, vm, testToRun);
+ // Correctness check.
+ testResult.assertNoException();
+ return testResult;
+ } catch (Exception e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ for (int i = 0; i < numVMs; i++) {
+ TestResults tr = resultFutureList[i].get();
+ tr.assertNoException();
+ for (long timings : tr.mTimings) {
+ requestLatencies.add((double) timings / NANO_TO_MICRO);
+ }
+ }
+ return requestLatencies;
+ }
+
+ // The following benchmark corresponds to cases when payload access rollback protected secret,
+ // but there is no existing session with Secretkeeper - which could be the case when several VMs
+ // are attempting to establish a connection.
+ //
+ // Implementation detail of the API in such scenario: Microdroid attempts to access the secret
+ // from Secretkeeper -> gets an error ("UnknownKeyId") -> Refreshes the session (this includes
+ // several call to AuthGraphKey Exchange HAL) -> retries access.
+ //
+ // Essentially this latency is (Failed Secretkeeper access from pVM + AuthGraphKeyExchange
+ // protocol between pVM & Secretkeeper + Successful Secretkeeper access from pVM)
+ @Test
+ public void rpDataReadWithRefreshingSession() throws Exception {
+ reportMetrics(
+ rpDataAccessWithRefreshingSession(false),
+ "latency/readRollbackProtectedSecretWithRefreshSession",
+ "us");
+ }
+
+ @Test
+ public void rpDataWriteWithRefreshingSession() throws Exception {
+ reportMetrics(
+ rpDataAccessWithRefreshingSession(true),
+ "latency/writeRollbackProtectedSecretWithRefreshSession",
+ "us");
+ }
}
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index c05fb0b..94f7ced 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -27,9 +27,11 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
+import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.Context;
+import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
import android.system.Os;
@@ -79,6 +81,10 @@
"microdroid_16k",
"microdroid_gki-android15-6.6")));
+ private static final long ONE_MEBI = 1024 * 1024;
+ private static final long MIN_MEM_ARM64 = 170 * ONE_MEBI;
+ private static final long MIN_MEM_X86_64 = 196 * ONE_MEBI;
+
public static boolean isCuttlefish() {
return getDeviceProperties().isCuttlefish();
}
@@ -393,6 +399,10 @@
return mProcessedBootTimeMetrics;
}
+ // Stopping a virtual machine is like pulling the plug on a real computer. VM may be left in
+ // an inconsistent state.
+ // For a graceful shutdown, request the payload to call {@code exit()} and wait for
+ // VirtualMachineCallback#onPayloadFinished} to be called.
protected void forceStop(VirtualMachine vm) {
try {
vm.stop();
@@ -722,7 +732,6 @@
public void onPayloadFinished(VirtualMachine vm, int exitCode) {
Log.i(logTag, "onPayloadFinished: " + exitCode);
payloadFinished.complete(true);
- forceStop(vm);
}
};
@@ -733,6 +742,26 @@
return testResults;
}
+ protected long getAvailableMemory() {
+ ActivityManager am = getContext().getSystemService(ActivityManager.class);
+ ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+ am.getMemoryInfo(memoryInfo);
+ return memoryInfo.availMem;
+ }
+
+ protected long minMemoryRequired() {
+ assertThat(Build.SUPPORTED_ABIS).isNotEmpty();
+ String primaryAbi = Build.SUPPORTED_ABIS[0];
+ switch (primaryAbi) {
+ case "x86_64":
+ return MIN_MEM_X86_64;
+ case "arm64-v8a":
+ case "arm64-v8a-hwasan":
+ return MIN_MEM_ARM64;
+ }
+ throw new AssertionError("Unsupported ABI: " + primaryAbi);
+ }
+
@FunctionalInterface
protected interface RunTestsAgainstTestService {
void runTests(ITestService testService, TestResults testResults) throws Exception;
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 4f9806a..e8673ce 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -101,6 +101,9 @@
private static final String INSTANCE_IMG = TEST_ROOT + "instance.img";
private static final String INSTANCE_ID_FILE = TEST_ROOT + "instance_id";
+ private static final String DEBUG_LEVEL_FULL = "full --enable-earlycon";
+ private static final String DEBUG_LEVEL_NONE = "none";
+
private static final int MIN_MEM_ARM64 = 170;
private static final int MIN_MEM_X86_64 = 196;
@@ -465,7 +468,7 @@
try {
microdroid =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(true)
@@ -495,7 +498,7 @@
// Act
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(true)
@@ -644,7 +647,7 @@
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(protectedVm)
@@ -751,7 +754,7 @@
VIRT_APEX + "bin/vm",
"run-app",
"--debug",
- debuggable ? "full" : "none",
+ debuggable ? DEBUG_LEVEL_FULL : DEBUG_LEVEL_NONE,
apkPath,
idsigPath,
instanceImgPath));
@@ -871,7 +874,7 @@
final String configPath = "assets/vm_config_apex.json"; // path inside the APK
ITestDevice microdroid =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(protectedVm)
@@ -1023,7 +1026,7 @@
final String configPath = "assets/vm_config.json"; // path inside the APK
testMicrodroidBootsWithBuilder(
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(protectedVm)
@@ -1061,7 +1064,7 @@
final String configPath = "assets/vm_config.json";
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(protectedVm)
@@ -1175,7 +1178,7 @@
"shell",
VIRT_APEX + "bin/vm",
"run-app",
- "--debug full",
+ "--debug " + DEBUG_LEVEL_FULL,
"--console " + CONSOLE_PATH,
"--payload-binary-name",
"MicrodroidEmptyPayloadJniLib.so",
@@ -1357,7 +1360,7 @@
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(protectedVm)
@@ -1403,7 +1406,7 @@
final String configPath = "assets/vm_config.json";
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(minMemorySize())
.cpuTopology("match_host")
.protectedVm(protectedVm)
@@ -1434,7 +1437,7 @@
// Start the VM with the dump DT option.
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(mem_size)
.cpuTopology("one_cpu")
.protectedVm(false)
@@ -1464,7 +1467,7 @@
// Start the VM with the dump DT option.
mMicrodroidDevice =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
+ .debugLevel(DEBUG_LEVEL_FULL)
.memoryMib(mem_size)
.cpuTopology("one_cpu")
.protectedVm(true)
diff --git a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
index 095eb54..de9f7c5 100644
--- a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
+++ b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_golden.dts
@@ -49,7 +49,7 @@
};
chosen {
- bootargs = "panic=-1 crashkernel=17M";
+ bootargs = "panic=-1 crashkernel=17M earlycon=uart8250,mmio,0x3f8 keep_bootcon";
kaslr-seed = <>;
linux,initrd-end = <0x81200360>;
linux,initrd-start = <0x81000000>;
diff --git a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
index f2ebdf9..f09e4ff 100644
--- a/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
+++ b/tests/hostside/java/com/android/microdroid/test/goldens/dt_dump_protected_golden.dts
@@ -49,7 +49,7 @@
};
chosen {
- bootargs = "panic=-1 crashkernel=31M";
+ bootargs = "panic=-1 crashkernel=31M earlycon=uart8250,mmio,0x3f8 keep_bootcon";
kaslr-seed = <>;
linux,initrd-end = <0x81202104>;
linux,initrd-start = <0x81000000>;
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index d0e045b..99300e2 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -173,6 +173,8 @@
"liblog",
"libprotobuf-cpp-lite-ndk",
],
+ // We've added support for updatable payloads in Android U.
+ min_sdk_version: "UpsideDownCake",
}
cc_library_shared {
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 8502ec3..7431a72 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -32,14 +32,12 @@
import static com.google.common.truth.TruthJUnit.assume;
import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.stream.Collectors.toList;
-import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.ComponentName;
@@ -47,7 +45,6 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
@@ -119,6 +116,7 @@
import java.util.OptionalLong;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@@ -171,11 +169,6 @@
public void tearDown() {
revokePermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
}
-
- private static final long ONE_MEBI = 1024 * 1024;
-
- private static final long MIN_MEM_ARM64 = 170 * ONE_MEBI;
- private static final long MIN_MEM_X86_64 = 196 * ONE_MEBI;
private static final String EXAMPLE_STRING = "Literally any string!! :)";
private static final String VM_SHARE_APP_PACKAGE_NAME = "com.android.microdroid.vmshare_app";
@@ -1874,22 +1867,14 @@
return false;
}
- private void ensureUpdatableVmSupported() throws Exception {
- if (getVendorApiLevel() >= 202504 && deviceCapableOfProtectedVm()) {
- assertTrue(
- "Missing Updatable VM support, have you declared Secretkeeper interface?",
- isUpdatableVmSupported());
- } else {
- assumeTrue("Device does not support Updatable VM", isUpdatableVmSupported());
- }
- }
-
@Test
+ @CddTest
public void rollbackProtectedDataOfPayload() throws Exception {
assumeSupportedDevice();
// Rollback protected data is only possible if Updatable VMs is supported -
// which implies Secretkeeper support.
- ensureUpdatableVmSupported();
+ assumeTrue("Missing Updatable VM support", isUpdatableVmSupported());
+
byte[] value1 = new byte[32];
Arrays.fill(value1, (byte) 0xcc);
byte[] value2 = new byte[32];
@@ -1909,7 +1894,7 @@
(ts, tr) -> {
tr.mPayloadRpData = ts.insecurelyReadPayloadRpData();
});
- // ainsecurelyReadPayloadRpData()` must've failed since no data was ever written!
+ // `insecurelyReadPayloadRpData()` must've failed since no data was ever written!
assertWithMessage("The read (unexpectedly) succeeded!")
.that(testResults.mException)
.isNotNull();
@@ -1940,6 +1925,62 @@
}
@Test
+ public void rollbackProtectedDataCanBeAccessedPostConnectionExpiration() throws Exception {
+ final long vmSize = minMemoryRequired();
+ // The reference implementation of Secretkeeper maintains 4 live session keys,
+ // dropping the oldest one when new connections are requested. Therefore we spin 8 VMs
+ // asynchronously.
+ // Within a VM, wait for 5 sec (> Microdroid boot time) and trigger rp data access
+ // hoping at least some of the connection between VM <-> Secretkeeper are expired.
+ final int numVMs = 8;
+ final long availableMem = getAvailableMemory();
+
+ // Let's not use more than half of the available memory
+ assume().withMessage("Available memory (" + availableMem + " bytes) too small")
+ .that((numVMs * vmSize) <= (availableMem / 2))
+ .isTrue();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .setMemoryBytes(vmSize)
+ .build();
+ byte[] data = new byte[32];
+ Arrays.fill(data, (byte) 0xcc);
+
+ CompletableFuture<TestResults>[] resultFutureList = new CompletableFuture[numVMs];
+ for (int i = 0; i < numVMs; i++) {
+ final VirtualMachine vm =
+ forceCreateNewVirtualMachine("test_sk_session_expiration_vm_" + i, config);
+ resultFutureList[i] =
+ CompletableFuture.supplyAsync(
+ () -> {
+ try {
+ TestResults testResults =
+ runVmTestService(
+ TAG,
+ vm,
+ (ts, tr) -> {
+ ts.insecurelyWritePayloadRpData(data);
+ Thread.sleep(5 * 1000); // 5 seconds of wait
+ tr.mPayloadRpData =
+ ts.insecurelyReadPayloadRpData();
+ });
+ return testResults;
+ } catch (Exception e) {
+ throw new CompletionException(e);
+ }
+ });
+ }
+
+ for (int i = 0; i < numVMs; i++) {
+ TestResults testResult = resultFutureList[i].get();
+ testResult.assertNoException();
+ assertThat(testResult.mPayloadRpData).isEqualTo(data);
+ }
+ }
+
+ @Test
@CddTest
public void isNewInstanceTest() throws Exception {
assumeSupportedDevice();
@@ -2473,25 +2514,7 @@
}
@Test
- public void kernelVersionRequirement() throws Exception {
- assumeVsrCompliant();
- int firstApiLevel = SystemProperties.getInt("ro.product.first_api_level", 0);
- assume().withMessage("Skip on devices launched before Android 14 (API level 34)")
- .that(firstApiLevel)
- .isAtLeast(34);
-
- String[] tokens = KERNEL_VERSION.split("\\.");
- int major = Integer.parseInt(tokens[0]);
- int minor = Integer.parseInt(tokens[1]);
-
- // Check kernel version >= 5.15
- assertTrue(major >= 5);
- if (major == 5) {
- assertTrue(minor >= 15);
- }
- }
-
- @Test
+ @CddTest
public void createAndRunRustVm() throws Exception {
// This test is here mostly to exercise the Rust wrapper around the VM Payload API.
// We're testing the same functionality as in other tests, the only difference is
@@ -2695,6 +2718,7 @@
}
@Test
+ @GmsTest(requirements = {"GMS-3-7.1-001.002"})
public void pageSize() throws Exception {
assumeSupportedDevice();
@@ -2780,13 +2804,6 @@
}
}
- private long getAvailableMemory() {
- ActivityManager am = getContext().getSystemService(ActivityManager.class);
- ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
- am.getMemoryInfo(memoryInfo);
- return memoryInfo.availMem;
- }
-
private VirtualMachineDescriptor toParcelFromParcel(VirtualMachineDescriptor descriptor) {
Parcel parcel = Parcel.obtain();
descriptor.writeToParcel(parcel, 0);
@@ -2819,17 +2836,4 @@
Exception e = assertThrows(VirtualMachineException.class, runnable);
assertThat(e).hasMessageThat().contains(expectedContents);
}
-
- private long minMemoryRequired() {
- assertThat(Build.SUPPORTED_ABIS).isNotEmpty();
- String primaryAbi = Build.SUPPORTED_ABIS[0];
- switch (primaryAbi) {
- case "x86_64":
- return MIN_MEM_X86_64;
- case "arm64-v8a":
- case "arm64-v8a-hwasan":
- return MIN_MEM_ARM64;
- }
- throw new AssertionError("Unsupported ABI: " + primaryAbi);
- }
}
diff --git a/tests/testapk/src/native/testbinary.cpp b/tests/testapk/src/native/testbinary.cpp
index 06c7e9d..2ab73a4 100644
--- a/tests/testapk/src/native/testbinary.cpp
+++ b/tests/testapk/src/native/testbinary.cpp
@@ -348,25 +348,40 @@
}
ScopedAStatus insecurelyReadPayloadRpData(std::array<uint8_t, 32>* out) override {
- int32_t ret = AVmPayload_readRollbackProtectedSecret(out->data(), 32);
- if (ret != 32) {
- return ScopedAStatus::fromServiceSpecificError(ret);
+ if (__builtin_available(android 36, *)) {
+ int32_t ret = AVmPayload_readRollbackProtectedSecret(out->data(), 32);
+ if (ret != 32) {
+ return ScopedAStatus::fromServiceSpecificError(ret);
+ }
+ return ScopedAStatus::ok();
+ } else {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "not available before SDK 36");
}
- return ScopedAStatus::ok();
}
ScopedAStatus insecurelyWritePayloadRpData(
const std::array<uint8_t, 32>& inputData) override {
- int32_t ret = AVmPayload_writeRollbackProtectedSecret(inputData.data(), 32);
- if (ret != 32) {
- return ScopedAStatus::fromServiceSpecificError(ret);
+ if (__builtin_available(android 36, *)) {
+ int32_t ret = AVmPayload_writeRollbackProtectedSecret(inputData.data(), 32);
+ if (ret != 32) {
+ return ScopedAStatus::fromServiceSpecificError(ret);
+ }
+ return ScopedAStatus::ok();
+ } else {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "not available before SDK 36");
}
- return ScopedAStatus::ok();
}
ScopedAStatus isNewInstance(bool* is_new_instance_out) override {
- *is_new_instance_out = AVmPayload_isNewInstance();
- return ScopedAStatus::ok();
+ if (__builtin_available(android 36, *)) {
+ *is_new_instance_out = AVmPayload_isNewInstance();
+ return ScopedAStatus::ok();
+ } else {
+ return ScopedAStatus::fromExceptionCodeWithMessage(EX_SERVICE_SPECIFIC,
+ "not available before SDK 36");
+ }
}
ScopedAStatus quit() override { exit(0); }
diff --git a/tests/vmshareapp/Android.bp b/tests/vmshareapp/Android.bp
index 5f6dc57..86c48f6 100644
--- a/tests/vmshareapp/Android.bp
+++ b/tests/vmshareapp/Android.bp
@@ -13,4 +13,8 @@
"MicrodroidPayloadInOtherAppNativeLib",
],
min_sdk_version: "34",
+ sdk_version: "system_current",
+ // Ideally we should set something "latest finalized sdk version" here.
+ // However, soong doesn't seem to provide such functionality.
+ target_sdk_version: "VanillaIceCream",
}
diff --git a/tests/vts/src/vts_libavf_test.rs b/tests/vts/src/vts_libavf_test.rs
index e30c175..dc37aad 100644
--- a/tests/vts/src/vts_libavf_test.rs
+++ b/tests/vts/src/vts_libavf_test.rs
@@ -16,7 +16,6 @@
use anyhow::{bail, ensure, Context, Result};
use log::info;
-use std::ffi::CStr;
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::os::fd::IntoRawFd;
@@ -87,10 +86,7 @@
// SAFETY: config is the only reference to a valid object
unsafe {
- AVirtualMachineRawConfig_setName(
- config,
- CStr::from_bytes_with_nul(b"vts_libavf_test_rialto\0").unwrap().as_ptr(),
- );
+ AVirtualMachineRawConfig_setName(config, c"vts_libavf_test_rialto".as_ptr());
AVirtualMachineRawConfig_setKernel(config, kernel_fd);
AVirtualMachineRawConfig_setProtectedVm(config, protected_vm);
AVirtualMachineRawConfig_setMemoryMiB(config, VM_MEMORY_MB);