Browse Source

Merge pull request #77686 from m4gr3d/fix_editor_audio_stream_microphone_crash_main

Fix issue causing the Android editor to crash when creating a new AudioStreamMicrophone
Rémi Verschelde 2 years ago
parent
commit
574f917442

+ 2 - 1
platform/android/audio_driver_opensl.cpp

@@ -272,7 +272,8 @@ Error AudioDriverOpenSL::input_start() {
 		return init_input_device();
 		return init_input_device();
 	}
 	}
 
 
-	return OK;
+	WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
+	return ERR_UNAUTHORIZED;
 }
 }
 
 
 Error AudioDriverOpenSL::input_stop() {
 Error AudioDriverOpenSL::input_stop() {

+ 2 - 0
platform/android/java/editor/src/main/AndroidManifest.xml

@@ -21,6 +21,8 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
         android:maxSdkVersion="29"/>
         android:maxSdkVersion="29"/>
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.VIBRATE" />
 
 
     <application
     <application
         android:allowBackup="false"
         android:allowBackup="false"

+ 16 - 1
platform/android/java/editor/src/main/java/org/godotengine/editor/GodotEditor.kt

@@ -81,7 +81,9 @@ open class GodotEditor : FullScreenGodotApp() {
 	private val commandLineParams = ArrayList<String>()
 	private val commandLineParams = ArrayList<String>()
 
 
 	override fun onCreate(savedInstanceState: Bundle?) {
 	override fun onCreate(savedInstanceState: Bundle?) {
-		PermissionsUtil.requestManifestPermissions(this)
+		// We exclude certain permissions from the set we request at startup, as they'll be
+		// requested on demand based on use-cases.
+		PermissionsUtil.requestManifestPermissions(this, setOf(Manifest.permission.RECORD_AUDIO))
 
 
 		val params = intent.getStringArrayExtra(COMMAND_LINE_PARAMS)
 		val params = intent.getStringArrayExtra(COMMAND_LINE_PARAMS)
 		updateCommandLineParams(params)
 		updateCommandLineParams(params)
@@ -98,6 +100,8 @@ open class GodotEditor : FullScreenGodotApp() {
 		val longPressEnabled = enableLongPressGestures()
 		val longPressEnabled = enableLongPressGestures()
 		val panScaleEnabled = enablePanAndScaleGestures()
 		val panScaleEnabled = enablePanAndScaleGestures()
 
 
+		checkForProjectPermissionsToEnable()
+
 		runOnUiThread {
 		runOnUiThread {
 			// Enable long press, panning and scaling gestures
 			// Enable long press, panning and scaling gestures
 			godotFragment?.renderView?.inputHandler?.apply {
 			godotFragment?.renderView?.inputHandler?.apply {
@@ -107,6 +111,17 @@ open class GodotEditor : FullScreenGodotApp() {
 		}
 		}
 	}
 	}
 
 
+	/**
+	 * Check for project permissions to enable
+	 */
+	protected open fun checkForProjectPermissionsToEnable() {
+		// Check for RECORD_AUDIO permission
+		val audioInputEnabled = java.lang.Boolean.parseBoolean(GodotLib.getGlobal("audio/driver/enable_input"));
+		if (audioInputEnabled) {
+			PermissionsUtil.requestPermission(Manifest.permission.RECORD_AUDIO, this)
+		}
+	}
+
 	private fun updateCommandLineParams(args: Array<String>?) {
 	private fun updateCommandLineParams(args: Array<String>?) {
 		// Update the list of command line params with the new args
 		// Update the list of command line params with the new args
 		commandLineParams.clear()
 		commandLineParams.clear()

+ 5 - 0
platform/android/java/editor/src/main/java/org/godotengine/editor/GodotGame.kt

@@ -39,4 +39,9 @@ class GodotGame : GodotEditor() {
 	override fun enableLongPressGestures() = false
 	override fun enableLongPressGestures() = false
 
 
 	override fun enablePanAndScaleGestures() = false
 	override fun enablePanAndScaleGestures() = false
+
+	override fun checkForProjectPermissionsToEnable() {
+		// Nothing to do.. by the time we get here, the project permissions will have already
+		// been requested by the Editor window.
+	}
 }
 }

+ 6 - 1
platform/android/java/editor/src/main/java/org/godotengine/editor/GodotProjectManager.kt

@@ -37,4 +37,9 @@ package org.godotengine.editor
  * Upon selection of a project, this activity (via its parent logic) starts the
  * Upon selection of a project, this activity (via its parent logic) starts the
  * [GodotEditor] activity.
  * [GodotEditor] activity.
  */
  */
-class GodotProjectManager : GodotEditor()
+class GodotProjectManager : GodotEditor() {
+	override fun checkForProjectPermissionsToEnable() {
+		// Nothing to do here.. we have yet to select a project to load.
+	}
+}
+

+ 56 - 14
platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java

@@ -42,10 +42,12 @@ import android.os.Environment;
 import android.provider.Settings;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Log;
 
 
+import androidx.annotation.Nullable;
 import androidx.core.content.ContextCompat;
 import androidx.core.content.ContextCompat;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
+import java.util.Set;
 
 
 /**
 /**
  * This class includes utility functions for Android permissions related operations.
  * This class includes utility functions for Android permissions related operations.
@@ -58,6 +60,7 @@ public final class PermissionsUtil {
 	static final int REQUEST_CAMERA_PERMISSION = 2;
 	static final int REQUEST_CAMERA_PERMISSION = 2;
 	static final int REQUEST_VIBRATE_PERMISSION = 3;
 	static final int REQUEST_VIBRATE_PERMISSION = 3;
 	public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
 	public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
+	public static final int REQUEST_SINGLE_PERMISSION_REQ_CODE = 1002;
 	public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;
 	public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;
 
 
 	private PermissionsUtil() {
 	private PermissionsUtil() {
@@ -65,31 +68,57 @@ public final class PermissionsUtil {
 
 
 	/**
 	/**
 	 * Request a dangerous permission. name must be specified in <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/AndroidManifest.xml">this</a>
 	 * Request a dangerous permission. name must be specified in <a href="https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/res/AndroidManifest.xml">this</a>
-	 * @param name the name of the requested permission.
+	 * @param permissionName the name of the requested permission.
 	 * @param activity the caller activity for this method.
 	 * @param activity the caller activity for this method.
 	 * @return true/false. "true" if permission was granted otherwise returns "false".
 	 * @return true/false. "true" if permission was granted otherwise returns "false".
 	 */
 	 */
-	public static boolean requestPermission(String name, Activity activity) {
+	public static boolean requestPermission(String permissionName, Activity activity) {
 		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
 		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
 			// Not necessary, asked on install already
 			// Not necessary, asked on install already
 			return true;
 			return true;
 		}
 		}
 
 
-		if (name.equals("RECORD_AUDIO") && ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
-			activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
-			return false;
-		}
+		switch (permissionName) {
+			case "RECORD_AUDIO":
+			case Manifest.permission.RECORD_AUDIO:
+				if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
+					activity.requestPermissions(new String[] { Manifest.permission.RECORD_AUDIO }, REQUEST_RECORD_AUDIO_PERMISSION);
+					return false;
+				}
+				return true;
 
 
-		if (name.equals("CAMERA") && ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
-			activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
-			return false;
-		}
+			case "CAMERA":
+			case Manifest.permission.CAMERA:
+				if (ContextCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
+					activity.requestPermissions(new String[] { Manifest.permission.CAMERA }, REQUEST_CAMERA_PERMISSION);
+					return false;
+				}
+				return true;
 
 
-		if (name.equals("VIBRATE") && ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
-			activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
-			return false;
+			case "VIBRATE":
+			case Manifest.permission.VIBRATE:
+				if (ContextCompat.checkSelfPermission(activity, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
+					activity.requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
+					return false;
+				}
+				return true;
+
+			default:
+				// Check if the given permission is a dangerous permission
+				try {
+					PermissionInfo permissionInfo = getPermissionInfo(activity, permissionName);
+					int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
+					if (protectionLevel == PermissionInfo.PROTECTION_DANGEROUS && ContextCompat.checkSelfPermission(activity, permissionName) != PackageManager.PERMISSION_GRANTED) {
+						activity.requestPermissions(new String[] { permissionName }, REQUEST_SINGLE_PERMISSION_REQ_CODE);
+						return false;
+					}
+				} catch (PackageManager.NameNotFoundException e) {
+					// Unknown permission - return false as it can't be granted.
+					Log.w(TAG, "Unable to identify permission " + permissionName, e);
+					return false;
+				}
+				return true;
 		}
 		}
-		return true;
 	}
 	}
 
 
 	/**
 	/**
@@ -98,6 +127,16 @@ public final class PermissionsUtil {
 	 * @return true/false. "true" if all permissions were granted otherwise returns "false".
 	 * @return true/false. "true" if all permissions were granted otherwise returns "false".
 	 */
 	 */
 	public static boolean requestManifestPermissions(Activity activity) {
 	public static boolean requestManifestPermissions(Activity activity) {
+		return requestManifestPermissions(activity, null);
+	}
+
+	/**
+	 * Request dangerous permissions which are defined in the Android manifest file from the user.
+	 * @param activity the caller activity for this method.
+	 * @param excludes Set of permissions to exclude from the request
+	 * @return true/false. "true" if all permissions were granted otherwise returns "false".
+	 */
+	public static boolean requestManifestPermissions(Activity activity, @Nullable Set<String> excludes) {
 		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
 		if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
 			return true;
 			return true;
 		}
 		}
@@ -115,6 +154,9 @@ public final class PermissionsUtil {
 
 
 		List<String> requestedPermissions = new ArrayList<>();
 		List<String> requestedPermissions = new ArrayList<>();
 		for (String manifestPermission : manifestPermissions) {
 		for (String manifestPermission : manifestPermissions) {
+			if (excludes != null && excludes.contains(manifestPermission)) {
+				continue;
+			}
 			try {
 			try {
 				if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
 				if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
 					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
 					if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {