Remove VmLauncherApp
Bug: 379800648
Test: TH
Change-Id: I805580bdb684d77cddb6803695487edc6c8b57d0
diff --git a/OWNERS b/OWNERS
index 81217f3..5597f6b 100644
--- a/OWNERS
+++ b/OWNERS
@@ -31,6 +31,5 @@
# Ferrochrome
per-file android/TerminalApp/**=jiyong@google.com,jeongik@google.com
-per-file android/VmLauncherApp/**=jiyong@google.com,jeongik@google.com
per-file libs/vm_launcher_lib/**=jiyong@google.com,jeongik@google.com
per-file build/debian/**=jiyong@google.com,jeongik@google.com
diff --git a/android/VmLauncherApp/Android.bp b/android/VmLauncherApp/Android.bp
deleted file mode 100644
index 2e8cc93..0000000
--- a/android/VmLauncherApp/Android.bp
+++ /dev/null
@@ -1,29 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-android_app {
- name: "VmLauncherApp",
- srcs: ["java/**/*.java"],
- resource_dirs: ["res"],
- static_libs: [
- // TODO(b/330257000): will be removed when binder RPC is used
- "android.system.virtualizationservice_internal-java",
- // TODO(b/331708504): will be removed when AVF framework handles surface
- "libcrosvm_android_display_service-java",
- "vm_launcher_lib",
- ],
- libs: [
- "framework-virtualization.impl",
- "framework-annotations-lib",
- ],
- platform_apis: true,
- privileged: true,
- apex_available: [
- "com.android.virt",
- ],
- optimize: {
- proguard_flags_files: ["proguard.flags"],
- shrink_resources: true,
- },
-}
diff --git a/android/VmLauncherApp/AndroidManifest.xml b/android/VmLauncherApp/AndroidManifest.xml
deleted file mode 100644
index 4fb4b5c..0000000
--- a/android/VmLauncherApp/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.virtualization.vmlauncher" >
-
- <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
- <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.RECORD_AUDIO" />
- <uses-feature android:name="android.software.virtualization_framework" android:required="true" />
-
- <permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
- android:protectionLevel="signature|preinstalled"/>
-
- <application
- android:label="VmLauncherApp">
- <activity android:name=".MainActivity"
- android:screenOrientation="landscape"
- android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode"
- android:theme="@style/MyTheme"
- android:resizeableActivity="false"
- android:permission="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER"
- android:exported="true">
- <intent-filter>
- <action android:name="android.virtualization.VM_LAUNCHER" />
- <action android:name="android.virtualization.VM_OPEN_URL" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </activity>
-
- </application>
-
-</manifest>
diff --git a/android/VmLauncherApp/README.md b/android/VmLauncherApp/README.md
deleted file mode 100644
index 0109f37..0000000
--- a/android/VmLauncherApp/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# VM launcher app
-
-## Building
-
-This app is now part of the virt APEX.
-
-## Enabling
-
-This app is disabled by default. To re-enable it, execute the following command.
-
-```
-adb root
-adb shell pm enable com.android.virtualization.vmlauncher/.MainActivity
-```
-
-## Running
-
-Copy vm config json file to /data/local/tmp/vm_config.json.
-And then, run the app, check log meesage.
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ClipboardHandler.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ClipboardHandler.java
deleted file mode 100644
index def464e..0000000
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/ClipboardHandler.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.vmlauncher;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.util.Log;
-
-import java.nio.charset.StandardCharsets;
-
-/** Provide methods to synchronize clipboard across Android and VM. */
-class ClipboardHandler {
- private static final String TAG = MainActivity.TAG;
- private final ClipboardManager mClipboardManager;
- private final VmAgent mVmAgent;
-
- ClipboardHandler(Context context, VmAgent vmAgent) {
- mClipboardManager = context.getSystemService(ClipboardManager.class);
- mVmAgent = vmAgent;
- }
-
- private VmAgent.Connection getConnection() throws InterruptedException {
- return mVmAgent.connect();
- }
-
- /** Read a text clip from Android's clipboard and send it to VM. */
- void writeClipboardToVm() {
- if (!mClipboardManager.hasPrimaryClip()) {
- return;
- }
-
- ClipData clip = mClipboardManager.getPrimaryClip();
- String text = clip.getItemAt(0).getText().toString();
- // TODO: remove this trailing null character. The size is already encoded in the header.
- text = text + '\0';
- // TODO: use UTF-8 encoding
- byte[] data = text.getBytes();
-
- try {
- getConnection().sendData(VmAgent.WRITE_CLIPBOARD_TYPE_TEXT_PLAIN, data);
- } catch (InterruptedException | RuntimeException e) {
- Log.e(TAG, "Failed to write clipboard data to VM", e);
- }
- }
-
- /** Read a text clip from VM and paste it to Android's clipboard. */
- void readClipboardFromVm() {
- VmAgent.Data data;
- try {
- data = getConnection().sendAndReceive(VmAgent.READ_CLIPBOARD_FROM_VM, null);
- } catch (InterruptedException | RuntimeException e) {
- Log.e(TAG, "Failed to read clipboard data from VM", e);
- return;
- }
-
- switch (data.type) {
- case VmAgent.WRITE_CLIPBOARD_TYPE_EMPTY:
- Log.d(TAG, "clipboard data from VM is empty");
- break;
- case VmAgent.WRITE_CLIPBOARD_TYPE_TEXT_PLAIN:
- String text = new String(data.data, StandardCharsets.UTF_8);
- ClipData clip = ClipData.newPlainText(null, text);
- mClipboardManager.setPrimaryClip(clip);
- break;
- default:
- Log.e(TAG, "Unknown clipboard response type: " + data.type);
- }
- }
-}
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/DisplayProvider.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/DisplayProvider.java
deleted file mode 100644
index 6eba709..0000000
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/DisplayProvider.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * 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.vmlauncher;
-
-import android.crosvm.ICrosvmAndroidDisplayService;
-import android.graphics.PixelFormat;
-import android.os.IBinder;
-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 libcore.io.IoBridge;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/** Presents Android-side surface where VM can use as a display */
-class DisplayProvider {
- private static final String TAG = MainActivity.TAG;
- private final SurfaceView mMainView;
- private final SurfaceView mCursorView;
- private final IVirtualizationServiceInternal mVirtService;
- private CursorHandler mCursorHandler;
-
- DisplayProvider(SurfaceView mainView, SurfaceView cursorView) {
- mMainView = mainView;
- mCursorView = cursorView;
-
- mMainView.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT);
- mMainView.getHolder().addCallback(new Callback(Callback.SurfaceKind.MAIN));
-
- mCursorView.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT);
- mCursorView.getHolder().addCallback(new Callback(Callback.SurfaceKind.CURSOR));
- mCursorView.getHolder().setFormat(PixelFormat.RGBA_8888);
- // TODO: do we need this z-order?
- mCursorView.setZOrderMediaOverlay(true);
-
- IBinder b = ServiceManager.waitForService("android.system.virtualizationservice");
- mVirtService = IVirtualizationServiceInternal.Stub.asInterface(b);
- try {
- // To ensure that the previous display service is removed.
- mVirtService.clearDisplayService();
- } catch (RemoteException e) {
- throw new RuntimeException("Failed to clear prior display service", e);
- }
- }
-
- void 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 (RemoteException e) {
- throw new RuntimeException("Failed to save frame for the main surface", e);
- }
- }
-
- private synchronized ICrosvmAndroidDisplayService getDisplayService() {
- try {
- IBinder b = mVirtService.waitDisplayService();
- return ICrosvmAndroidDisplayService.Stub.asInterface(b);
- } catch (Exception e) {
- throw new RuntimeException("Error while getting display service", e);
- }
- }
-
- private class Callback implements SurfaceHolder.Callback {
- enum SurfaceKind {
- MAIN,
- CURSOR
- }
-
- private final SurfaceKind mSurfaceKind;
-
- Callback(SurfaceKind kind) {
- mSurfaceKind = kind;
- }
-
- private boolean isForCursor() {
- return mSurfaceKind == SurfaceKind.CURSOR;
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- try {
- getDisplayService().setSurface(holder.getSurface(), isForCursor());
- } catch (Exception e) {
- // 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 " + mSurfaceKind + " to VM", e);
- }
-
- try {
- switch (mSurfaceKind) {
- case MAIN:
- getDisplayService().drawSavedFrameForSurface(isForCursor());
- break;
- case CURSOR:
- ParcelFileDescriptor stream = createNewCursorStream();
- getDisplayService().setCursorStream(stream);
- break;
- }
- } catch (Exception e) {
- // TODO: don't consume exceptions here too
- Log.e(TAG, "Failed to configure surface " + mSurfaceKind, e);
- }
- }
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- // 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
- public void surfaceDestroyed(SurfaceHolder holder) {
- try {
- getDisplayService().removeSurface(isForCursor());
- } catch (RemoteException e) {
- throw new RuntimeException("Error while destroying surface for " + mSurfaceKind, e);
- }
- }
- }
-
- private ParcelFileDescriptor createNewCursorStream() {
- if (mCursorHandler != null) {
- mCursorHandler.interrupt();
- }
- ParcelFileDescriptor[] pfds;
- try {
- pfds = ParcelFileDescriptor.createSocketPair();
- } catch (IOException e) {
- throw new RuntimeException("Failed to create socketpair for cursor stream", e);
- }
- mCursorHandler = new CursorHandler(pfds[0]);
- mCursorHandler.start();
- return pfds[1];
- }
-
- /**
- * Thread reading cursor coordinate from a stream, and updating the position of the cursor
- * surface accordingly.
- */
- private class CursorHandler extends Thread {
- private final ParcelFileDescriptor mStream;
- private final SurfaceControl mCursor;
- private final SurfaceControl.Transaction mTransaction;
-
- CursorHandler(ParcelFileDescriptor stream) {
- mStream = stream;
- mCursor = DisplayProvider.this.mCursorView.getSurfaceControl();
- mTransaction = new SurfaceControl.Transaction();
-
- SurfaceControl main = DisplayProvider.this.mMainView.getSurfaceControl();
- mTransaction.reparent(mCursor, main).apply();
- }
-
- @Override
- public void run() {
- try {
- ByteBuffer byteBuffer = ByteBuffer.allocate(8 /* (x: u32, y: u32) */);
- byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
- while (true) {
- if (Thread.interrupted()) {
- Log.d(TAG, "CursorHandler thread interrupted!");
- return;
- }
- byteBuffer.clear();
- int bytes =
- IoBridge.read(
- mStream.getFileDescriptor(),
- byteBuffer.array(),
- 0,
- byteBuffer.array().length);
- if (bytes == -1) {
- Log.e(TAG, "cannot read from cursor stream, stop the handler");
- return;
- }
- float x = (float) (byteBuffer.getInt() & 0xFFFFFFFF);
- float y = (float) (byteBuffer.getInt() & 0xFFFFFFFF);
- mTransaction.setPosition(mCursor, x, y).apply();
- }
- } catch (IOException e) {
- Log.e(TAG, "failed to run CursorHandler", e);
- }
- }
- }
-}
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/InputForwarder.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/InputForwarder.java
deleted file mode 100644
index 1be362b..0000000
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/InputForwarder.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.vmlauncher;
-
-import android.content.Context;
-import android.hardware.input.InputManager;
-import android.os.Handler;
-import android.system.virtualmachine.VirtualMachine;
-import android.system.virtualmachine.VirtualMachineCustomImageConfig;
-import android.util.Log;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.View;
-
-/** Forwards input events (touch, mouse, ...) from Android to VM */
-class InputForwarder {
- private static final String TAG = MainActivity.TAG;
- private final Context mContext;
- private final VirtualMachine mVirtualMachine;
- private InputManager.InputDeviceListener mInputDeviceListener;
-
- private boolean isTabletMode = false;
-
- InputForwarder(
- Context context,
- VirtualMachine vm,
- View touchReceiver,
- View mouseReceiver,
- View keyReceiver) {
- mContext = context;
- mVirtualMachine = vm;
-
- VirtualMachineCustomImageConfig config = vm.getConfig().getCustomImageConfig();
- if (config.useTouch()) {
- 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());
- }
- }
-
- void cleanUp() {
- if (mInputDeviceListener != null) {
- InputManager im = mContext.getSystemService(InputManager.class);
- im.unregisterInputDeviceListener(mInputDeviceListener);
- mInputDeviceListener = null;
- }
- }
-
- private void setupTouchReceiver(View receiver) {
- receiver.setOnTouchListener(
- (v, event) -> {
- return mVirtualMachine.sendMultiTouchEvent(event);
- });
- }
-
- private void setupMouseReceiver(View receiver) {
- receiver.requestUnbufferedDispatch(InputDevice.SOURCE_ANY);
- receiver.setOnCapturedPointerListener(
- (v, event) -> {
- int eventSource = event.getSource();
- if ((eventSource & InputDevice.SOURCE_CLASS_POSITION) != 0) {
- return mVirtualMachine.sendTrackpadEvent(event);
- }
- return mVirtualMachine.sendMouseEvent(event);
- });
- }
-
- private void setupKeyReceiver(View receiver) {
- receiver.setOnKeyListener(
- (v, code, event) -> {
- // TODO: this is guest-os specific. It shouldn't be handled here.
- if (isVolumeKey(code)) {
- return false;
- }
- return mVirtualMachine.sendKeyEvent(event);
- });
- }
-
- private static boolean isVolumeKey(int keyCode) {
- return keyCode == KeyEvent.KEYCODE_VOLUME_UP
- || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
- || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE;
- }
-
- private void setupTabletModeHandler(Handler handler) {
- InputManager im = mContext.getSystemService(InputManager.class);
- mInputDeviceListener =
- new InputManager.InputDeviceListener() {
- @Override
- public void onInputDeviceAdded(int deviceId) {
- setTabletModeConditionally();
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId) {
- setTabletModeConditionally();
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId) {
- setTabletModeConditionally();
- }
- };
- im.registerInputDeviceListener(mInputDeviceListener, handler);
- }
-
- private static boolean hasPhysicalKeyboard() {
- for (int id : InputDevice.getDeviceIds()) {
- InputDevice d = InputDevice.getDevice(id);
- if (!d.isVirtual() && d.isEnabled() && d.isFullKeyboard()) {
- return true;
- }
- }
- return false;
- }
-
- void setTabletModeConditionally() {
- boolean tabletModeNeeded = !hasPhysicalKeyboard();
- if (tabletModeNeeded != isTabletMode) {
- String mode = tabletModeNeeded ? "tablet mode" : "desktop mode";
- Log.d(TAG, "switching to " + mode);
- isTabletMode = tabletModeNeeded;
- mVirtualMachine.sendTabletModeEvent(tabletModeNeeded);
- }
- }
-}
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/MainActivity.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/MainActivity.java
deleted file mode 100644
index e18b0d1..0000000
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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.vmlauncher;
-
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.Manifest.permission;
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.system.virtualmachine.VirtualMachine;
-import android.system.virtualmachine.VirtualMachineConfig;
-import android.system.virtualmachine.VirtualMachineException;
-import android.util.Log;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowInsets;
-import android.view.WindowInsetsController;
-
-import java.nio.file.Path;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-public class MainActivity extends Activity {
- static final String TAG = "VmLauncherApp";
- // TODO: this path should be from outside of this activity
- private static final String VM_CONFIG_PATH = "/data/local/tmp/vm_config.json";
-
- private static final int RECORD_AUDIO_PERMISSION_REQUEST_CODE = 101;
-
- private static final String ACTION_VM_LAUNCHER = "android.virtualization.VM_LAUNCHER";
- private static final String ACTION_VM_OPEN_URL = "android.virtualization.VM_OPEN_URL";
-
- private ExecutorService mExecutorService;
- private VirtualMachine mVirtualMachine;
- private InputForwarder mInputForwarder;
- private DisplayProvider mDisplayProvider;
- private VmAgent mVmAgent;
- private ClipboardHandler mClipboardHandler;
- private OpenUrlHandler mOpenUrlHandler;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Log.d(TAG, "onCreate intent: " + getIntent());
- checkAndRequestRecordAudioPermission();
- mExecutorService = Executors.newCachedThreadPool();
-
- ConfigJson json = ConfigJson.from(VM_CONFIG_PATH);
- VirtualMachineConfig config = json.toConfigBuilder(this).build();
-
- Runner runner;
- try {
- runner = Runner.create(this, config);
- } catch (VirtualMachineException e) {
- throw new RuntimeException(e);
- }
- mVirtualMachine = runner.getVm();
- runner.getExitStatus()
- .thenAcceptAsync(
- success -> {
- setResult(success ? RESULT_OK : RESULT_CANCELED);
- finish();
- });
-
- // Setup UI
- setContentView(R.layout.activity_main);
- SurfaceView mainView = findViewById(R.id.surface_view);
- SurfaceView cursorView = findViewById(R.id.cursor_surface_view);
- View touchView = findViewById(R.id.background_touch_view);
- makeFullscreen();
-
- // Connect the views to the VM
- mInputForwarder = new InputForwarder(this, mVirtualMachine, touchView, mainView, mainView);
- mDisplayProvider = new DisplayProvider(mainView, cursorView);
-
- Path logPath = getFileStreamPath(mVirtualMachine.getName() + ".log").toPath();
- Logger.setup(mVirtualMachine, logPath, mExecutorService);
-
- mVmAgent = new VmAgent(mVirtualMachine);
- mClipboardHandler = new ClipboardHandler(this, mVmAgent);
- mOpenUrlHandler = new OpenUrlHandler(mVmAgent);
- handleIntent(getIntent());
- }
-
- private void makeFullscreen() {
- Window w = getWindow();
- w.setDecorFitsSystemWindows(false);
- WindowInsetsController insetsCtrl = w.getInsetsController();
- insetsCtrl.hide(WindowInsets.Type.systemBars());
- insetsCtrl.setSystemBarsBehavior(
- WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mInputForwarder.setTabletModeConditionally();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- mDisplayProvider.notifyDisplayIsGoingToInvisible();
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- try {
- mVirtualMachine.suspend();
- } catch (VirtualMachineException e) {
- Log.e(TAG, "Failed to suspend VM" + e);
- }
- }
-
- @Override
- protected void onRestart() {
- super.onRestart();
- try {
- mVirtualMachine.resume();
- } catch (VirtualMachineException e) {
- Log.e(TAG, "Failed to resume VM" + e);
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mExecutorService.shutdownNow();
- mInputForwarder.cleanUp();
- mOpenUrlHandler.shutdown();
- Log.d(TAG, "destroyed");
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
-
- // TODO: explain why we have to do this on every focus change
- if (hasFocus) {
- SurfaceView mainView = findViewById(R.id.surface_view);
- mainView.requestPointerCapture();
- }
-
- // TODO: remove executor here. Let clipboard handler handle this.
- mExecutorService.execute(
- () -> {
- if (hasFocus) {
- mClipboardHandler.writeClipboardToVm();
- } else {
- mClipboardHandler.readClipboardFromVm();
- }
- });
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- Log.d(TAG, "onNewIntent intent: " + intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- if (ACTION_VM_OPEN_URL.equals(intent.getAction())) {
- String url = intent.getStringExtra(Intent.EXTRA_TEXT);
- if (url != null) {
- mOpenUrlHandler.sendUrlToVm(url);
- }
- }
- }
-
- private void checkAndRequestRecordAudioPermission() {
- if (getApplicationContext().checkSelfPermission(permission.RECORD_AUDIO)
- != PERMISSION_GRANTED) {
- requestPermissions(
- new String[] {permission.RECORD_AUDIO}, RECORD_AUDIO_PERMISSION_REQUEST_CODE);
- }
- }
-}
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/OpenUrlHandler.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/OpenUrlHandler.java
deleted file mode 100644
index fb0c6bf..0000000
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/OpenUrlHandler.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.vmlauncher;
-
-import android.util.Log;
-
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-class OpenUrlHandler {
- private static final String TAG = MainActivity.TAG;
-
- private final VmAgent mVmAgent;
- private final ExecutorService mExecutorService;
-
- OpenUrlHandler(VmAgent vmAgent) {
- mVmAgent = vmAgent;
- mExecutorService = Executors.newSingleThreadExecutor();
- }
-
- void shutdown() {
- mExecutorService.shutdownNow();
- }
-
- void sendUrlToVm(String url) {
- mExecutorService.execute(
- () -> {
- try {
- mVmAgent.connect().sendData(VmAgent.OPEN_URL, url.getBytes());
- Log.d(TAG, "Successfully sent URL to the VM");
- } catch (InterruptedException | RuntimeException e) {
- Log.e(TAG, "Failed to send URL to the VM", e);
- }
- });
- }
-}
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmAgent.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmAgent.java
deleted file mode 100644
index af1d298..0000000
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmAgent.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.vmlauncher;
-
-import android.os.ParcelFileDescriptor;
-import android.os.SystemClock;
-import android.system.virtualmachine.VirtualMachine;
-import android.system.virtualmachine.VirtualMachineException;
-import android.util.Log;
-
-import libcore.io.Streams;
-
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * Agent running in the VM. This class provides connection to the agent and ways to communicate with
- * it.
- */
-class VmAgent {
- private static final String TAG = MainActivity.TAG;
- private static final int DATA_SHARING_SERVICE_PORT = 3580;
- private static final int HEADER_SIZE = 8; // size of the header
- private static final int SIZE_OFFSET = 4; // offset of the size field in the header
- private static final long RETRY_INTERVAL_MS = 1_000;
-
- static final byte READ_CLIPBOARD_FROM_VM = 0;
- static final byte WRITE_CLIPBOARD_TYPE_EMPTY = 1;
- static final byte WRITE_CLIPBOARD_TYPE_TEXT_PLAIN = 2;
- static final byte OPEN_URL = 3;
-
- private final VirtualMachine mVirtualMachine;
-
- VmAgent(VirtualMachine vm) {
- mVirtualMachine = vm;
- }
-
- /**
- * Connects to the agent and returns the established communication channel. This can block.
- *
- * @throws InterruptedException If the current thread was interrupted
- */
- Connection connect() throws InterruptedException {
- boolean shouldLog = true;
- while (true) {
- if (Thread.interrupted()) {
- throw new InterruptedException();
- }
- try {
- return new Connection(mVirtualMachine.connectVsock(DATA_SHARING_SERVICE_PORT));
- } catch (VirtualMachineException e) {
- if (shouldLog) {
- shouldLog = false;
- Log.d(TAG, "Still waiting for VM agent to start", e);
- }
- }
- SystemClock.sleep(RETRY_INTERVAL_MS);
- }
- }
-
- static class Data {
- final int type;
- final byte[] data;
-
- Data(int type, byte[] data) {
- this.type = type;
- this.data = data;
- }
- }
-
- /** Represents a connection to the agent */
- class Connection {
- private final ParcelFileDescriptor mConn;
-
- private Connection(ParcelFileDescriptor conn) {
- mConn = conn;
- }
-
- /** Send data of a given type. This can block. */
- void sendData(byte type, byte[] data) {
- // Byte 0: Data type
- // Byte 1-3: Padding alignment & Reserved for other use cases in the future
- // Byte 4-7: Data size of the payload
- ByteBuffer header = ByteBuffer.allocate(HEADER_SIZE);
- header.clear();
- header.order(ByteOrder.LITTLE_ENDIAN);
- header.put(0, type);
- int dataSize = data == null ? 0 : data.length;
- header.putInt(SIZE_OFFSET, dataSize);
-
- try (OutputStream out = new FileOutputStream(mConn.getFileDescriptor())) {
- out.write(header.array());
- if (data != null) {
- out.write(data);
- }
- } catch (IOException e) {
- throw new RuntimeException("Failed to send message of type: " + type, e);
- }
- }
-
- /** Read data from agent. This can block. */
- Data readData() {
- ByteBuffer header = ByteBuffer.allocate(HEADER_SIZE);
- header.clear();
- header.order(ByteOrder.LITTLE_ENDIAN);
- byte[] data;
-
- try (InputStream in = new FileInputStream(mConn.getFileDescriptor())) {
- Streams.readFully(in, header.array());
- byte type = header.get(0);
- int dataSize = header.getInt(SIZE_OFFSET);
- data = new byte[dataSize];
- Streams.readFully(in, data);
- return new Data(type, data);
- } catch (IOException e) {
- throw new RuntimeException("Failed to read data", e);
- }
- }
-
- /** Convenient method for sending data and then reading response for it. This can block. */
- Data sendAndReceive(byte type, byte[] data) {
- sendData(type, data);
- return readData();
- }
- }
-}
diff --git a/android/VmLauncherApp/proguard.flags b/android/VmLauncherApp/proguard.flags
deleted file mode 100644
index b93240c..0000000
--- a/android/VmLauncherApp/proguard.flags
+++ /dev/null
@@ -1,33 +0,0 @@
-##---------------Begin: proguard configuration for Gson ----------
-# Gson uses generic type information stored in a class file when working with fields. Proguard
-# removes such information by default, so configure it to keep all of it.
--keepattributes Signature
-
-# For using GSON @Expose annotation
--keepattributes *Annotation*
-
-# Gson specific classes
--dontwarn sun.misc.**
-#-keep class com.google.gson.stream.** { *; }
-
-# Application classes that will be serialized/deserialized over Gson
--keep class com.android.virtualization.vmlauncher.ConfigJson { <fields>; }
--keep class com.android.virtualization.vmlauncher.ConfigJson$* { <fields>; }
-
-# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
-# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
--keep class * extends com.google.gson.TypeAdapter
--keep class * implements com.google.gson.TypeAdapterFactory
--keep class * implements com.google.gson.JsonSerializer
--keep class * implements com.google.gson.JsonDeserializer
-
-# Prevent R8 from leaving Data object members always null
--keepclassmembers,allowobfuscation class * {
- @com.google.gson.annotations.SerializedName <fields>;
-}
-
-# Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher.
--keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken
--keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken
-
-##---------------End: proguard configuration for Gson ----------
\ No newline at end of file
diff --git a/android/VmLauncherApp/res/layout/activity_main.xml b/android/VmLauncherApp/res/layout/activity_main.xml
deleted file mode 100644
index a80ece0..0000000
--- a/android/VmLauncherApp/res/layout/activity_main.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<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=".MainActivity">
- <View
- android:id="@+id/background_touch_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
- <SurfaceView
- android:id="@+id/surface_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- 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>
diff --git a/android/VmLauncherApp/res/values/themes.xml b/android/VmLauncherApp/res/values/themes.xml
deleted file mode 100644
index c10b6d9..0000000
--- a/android/VmLauncherApp/res/values/themes.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-<resources xmlns:tools="http://schemas.android.com/tools">
- <style name="MyTheme" parent="@android:style/Theme.DeviceDefault.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/docs/custom_vm.md b/docs/custom_vm.md
index 2863eb6..9a9ede4 100644
--- a/docs/custom_vm.md
+++ b/docs/custom_vm.md
@@ -24,85 +24,3 @@
The `vm` command also has other subcommands for debugging; run
`/apex/com.android.virt/bin/vm help` for details.
-
-### Running Debian
-1. Download an ARM64 image from https://cloud.debian.org/images/cloud/ (We tested nocloud image)
-
-2. Resize the image
-```shell
-truncate -s 20G debian.img
-virt-resize --expand /dev/sda1 <download_image_file> debian.img
-```
-
-3. Copy the image file
-```shell
-tar cfS debian.img.tar debian.img
-adb push debian.img.tar /data/local/tmp/
-adb shell tar xf /data/local/tmp/debian.img.tar -C /data/local/tmp/
-adb shell rm /data/local/tmp/debian.img.tar
-adb shell chmod a+w /data/local/tmp/debian.img
-rm debian.img.tar
-```
-
-Note: we tar and untar to keep the image file sparse.
-
-4. Make the VM config file
-```shell
-cat > vm_config.json <<EOF
-{
- "name": "debian",
- "disks": [
- {
- "image": "/data/local/tmp/debian.img",
- "partitions": [],
- "writable": true
- }
- ],
- "protected": false,
- "cpu_topology": "match_host",
- "platform_version": "~1.0",
- "memory_mib": 8096,
- "debuggable": true,
- "console_out": true,
- "connect_console": true,
- "console_input_device": "ttyS0",
- "network": true,
- "input": {
- "touchscreen": true,
- "keyboard": true,
- "mouse": true,
- "trackpad": true,
- "switches": true
- },
- "audio": {
- "speaker": true,
- "microphone": true
- },
- "gpu": {
- "backend": "virglrenderer",
- "context_types": ["virgl2"]
- },
- "display": {
- "refresh_rate": "30"
- }
-}
-EOF
-adb push vm_config.json /data/local/tmp/
-```
-
-5. Launch VmLauncherApp(the detail will be explain below)
-
-6. For console, we can refer to `Debugging` section below. (id: root)
-
-7. For graphical shell, you need to install xfce(for now, only xfce is tested)
-```
-apt install task-xfce-desktop
-dpkg --configure -a (if necessary)
-systemctl set-default graphical.target
-
-# need non-root user for graphical shell
-adduser linux
-# optional
-adduser linux sudo
-reboot
-```