Add Display Activity
Separate activity with fixed-size surface
Bug: 389524419
Test: check if DisplayActivity shows something
Change-Id: I929e215439a0a3449c3f76a0b96f829261f00ee8
diff --git a/android/TerminalApp/Android.bp b/android/TerminalApp/Android.bp
index 79f0094..2bac412 100644
--- a/android/TerminalApp/Android.bp
+++ b/android/TerminalApp/Android.bp
@@ -16,6 +16,7 @@
"androidx-constraintlayout_constraintlayout",
"androidx.window_window",
"apache-commons-compress",
+ "avf_aconfig_flags_java",
"com.google.android.material_material",
"debian-service-grpclib-lite",
"gson",
diff --git a/android/TerminalApp/AndroidManifest.xml b/android/TerminalApp/AndroidManifest.xml
index 53fdafc..c11b1a0 100644
--- a/android/TerminalApp/AndroidManifest.xml
+++ b/android/TerminalApp/AndroidManifest.xml
@@ -47,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/DisplayActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/DisplayActivity.kt
new file mode 100644
index 0000000..03206e5
--- /dev/null
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/DisplayActivity.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.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)
+ }
+
+ 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
index fed8e5a..a04e056 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/DisplayProvider.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/DisplayProvider.kt
@@ -39,7 +39,10 @@
private val mainView: SurfaceView,
private val cursorView: SurfaceView,
) {
- private val virtService: IVirtualizationServiceInternal
+ private val virtService: IVirtualizationServiceInternal by lazy {
+ val b = ServiceManager.waitForService("android.system.virtualizationservice")
+ IVirtualizationServiceInternal.Stub.asInterface(b)
+ }
private var cursorHandler: CursorHandler? = null
init {
@@ -50,14 +53,6 @@
cursorView.holder.setFormat(PixelFormat.RGBA_8888)
// TODO: do we need this z-order?
cursorView.setZOrderMediaOverlay(true)
- val b = ServiceManager.waitForService("android.system.virtualizationservice")
- virtService = IVirtualizationServiceInternal.Stub.asInterface(b)
- try {
- // To ensure that the previous display service is removed.
- virtService.clearDisplayService()
- } catch (e: RemoteException) {
- throw RuntimeException("Failed to clear prior display service", e)
- }
}
fun notifyDisplayIsGoingToInvisible() {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
index 1ae6ec5..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)
@@ -256,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()
@@ -344,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
}
@@ -353,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)
}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index f8b1b45..52c8271 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
@@ -217,6 +218,17 @@
changed = true
}
+ // TODO(jeongik): let it configurable
+ if (terminalGuiSupport()) {
+ builder.setDisplayConfig(
+ VirtualMachineCustomImageConfig.DisplayConfig.Builder()
+ .setWidth(1920)
+ .setHeight(1080)
+ .build()
+ )
+ changed = true
+ }
+
val image = InstalledImage.getDefault(this)
if (image.hasBackup()) {
val backup = image.backupFile
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/strings.xml b/android/TerminalApp/res/values/strings.xml
index bdebb83..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] -->
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>