virtiofs: Request permission to access storage
start the VM after the permission is granted.
If the permission is denied, then do not share
emulated storage.
Bug: 372171883
Test: Launch app with configured shared paths.
1: Grant permission
Verify paths are mounted in guest
2: Reject permission
Verify paths are not shared by crosvm.
Change-Id: Idbd4bf2f8d1ea0f75987bf6cad00be718b061d74
Signed-off-by: Akilesh Kailash <akailash@google.com>
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index af41c85..e278165 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -26,8 +26,11 @@
import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.graphics.fonts.FontStyle;
+import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
+import android.os.Environment;
+import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
@@ -44,6 +47,10 @@
import android.webkit.WebViewClient;
import android.widget.Toast;
+import androidx.activity.result.ActivityResult;
+import androidx.activity.result.ActivityResultLauncher;
+import androidx.activity.result.contract.ActivityResultContracts;
+
import com.android.virtualization.vmlauncher.InstallUtils;
import com.android.virtualization.vmlauncher.VmLauncherService;
import com.android.virtualization.vmlauncher.VmLauncherServices;
@@ -81,6 +88,7 @@
private WebView mWebView;
private AccessibilityManager mAccessibilityManager;
private static final int POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE = 101;
+ private ActivityResultLauncher<Intent> manageExternalStorageActivityResultLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -111,12 +119,41 @@
connectToTerminalService();
readClientCertificate();
+ manageExternalStorageActivityResultLauncher =
+ registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ (ActivityResult result) -> {
+ if (Environment.isExternalStorageManager()) {
+ Toast.makeText(this, "Storage permission set!", Toast.LENGTH_SHORT)
+ .show();
+ } else {
+ Toast.makeText(
+ this,
+ "Storage permission not set",
+ Toast.LENGTH_SHORT)
+ .show();
+ }
+ startVm();
+ });
+
// if installer is launched, it will be handled in onActivityResult
if (!launchInstaller) {
- startVm();
+ if (!Environment.isExternalStorageManager()) {
+ requestStoragePermissions(this, manageExternalStorageActivityResultLauncher);
+ } else {
+ startVm();
+ }
}
}
+ private void requestStoragePermissions(
+ Context context, ActivityResultLauncher<Intent> activityResultLauncher) {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
+ Uri uri = Uri.fromParts("package", context.getPackageName(), null);
+ intent.setData(uri);
+ activityResultLauncher.launch(intent);
+ }
+
private URL getTerminalServiceUrl() {
Configuration config = getResources().getConfiguration();
@@ -406,7 +443,11 @@
Log.e(TAG, "Failed to start VM. Installer returned error.");
finish();
}
- startVm();
+ if (!Environment.isExternalStorageManager()) {
+ requestStoragePermissions(this, manageExternalStorageActivityResultLauncher);
+ } else {
+ startVm();
+ }
}
}
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
index 5d6b13f..a259fe2 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/ConfigJson.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
+import android.os.Environment;
import android.system.virtualmachine.VirtualMachineConfig;
import android.system.virtualmachine.VirtualMachineCustomImageConfig;
import android.system.virtualmachine.VirtualMachineCustomImageConfig.AudioConfig;
@@ -157,22 +158,40 @@
private static class SharedPathJson {
private SharedPathJson() {}
- // Package ID of Terminal app.
- private static final String TERMINAL_PACKAGE_ID =
- "com.google.android.virtualization.terminal";
private String sharedPath;
+ private static final int GUEST_UID = 1000;
+ private static final int GUEST_GID = 100;
private SharedPath toConfig(Context context) {
try {
- int uid =
- context.getPackageManager()
- .getPackageUidAsUser(TERMINAL_PACKAGE_ID, context.getUserId());
-
- return new SharedPath(sharedPath, uid, uid, 0, 0, 0007, "android", "android");
+ int terminalUid = getTerminalUid(context);
+ if (sharedPath.contains("emulated")) {
+ if (Environment.isExternalStorageManager()) {
+ int currentUserId = context.getUserId();
+ String path = sharedPath + "/" + currentUserId + "/Download";
+ return new SharedPath(
+ path,
+ terminalUid,
+ terminalUid,
+ GUEST_UID,
+ GUEST_GID,
+ 0007,
+ "android",
+ "android");
+ }
+ return null;
+ }
+ return new SharedPath(
+ sharedPath, terminalUid, terminalUid, 0, 0, 0007, "internal", "internal");
} catch (NameNotFoundException e) {
return null;
}
}
+
+ private int getTerminalUid(Context context) throws NameNotFoundException {
+ return context.getPackageManager()
+ .getPackageUidAsUser(context.getPackageName(), context.getUserId());
+ }
}
private static class InputJson {