Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue causing the Android editor to crash when creating a new AudioStreamMicrophone #77686

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion platform/android/audio_driver_opensl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,8 @@ Error AudioDriverOpenSL::input_start() {
return init_input_device();
}

return OK;
WARN_PRINT("Unable to start audio capture - No RECORD_AUDIO permission");
return ERR_UNAUTHORIZED;
}

Error AudioDriverOpenSL::input_stop() {
Expand Down
2 changes: 2 additions & 0 deletions platform/android/java/editor/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="29"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />

<application
android:allowBackup="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ open class GodotEditor : FullScreenGodotApp() {
private val commandLineParams = ArrayList<String>()

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)
updateCommandLineParams(params)
Expand All @@ -98,6 +100,8 @@ open class GodotEditor : FullScreenGodotApp() {
val longPressEnabled = enableLongPressGestures()
val panScaleEnabled = enablePanAndScaleGestures()

checkForProjectPermissionsToEnable()

runOnUiThread {
// Enable long press, panning and scaling gestures
godotFragment?.renderView?.inputHandler?.apply {
Expand All @@ -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>?) {
// Update the list of command line params with the new args
commandLineParams.clear()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ class GodotGame : GodotEditor() {
override fun enableLongPressGestures() = 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.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,9 @@ package org.godotengine.editor
* Upon selection of a project, this activity (via its parent logic) starts the
* [GodotEditor] activity.
*/
class GodotProjectManager : GodotEditor()
class GodotProjectManager : GodotEditor() {
override fun checkForProjectPermissionsToEnable() {
// Nothing to do here.. we have yet to select a project to load.
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,12 @@
import android.provider.Settings;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* This class includes utility functions for Android permissions related operations.
Expand All @@ -58,38 +60,65 @@ public final class PermissionsUtil {
static final int REQUEST_CAMERA_PERMISSION = 2;
static final int REQUEST_VIBRATE_PERMISSION = 3;
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;

private 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>
* @param name the name of the requested permission.
* @param permissionName the name of the requested permission.
* @param activity the caller activity for this method.
* @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) {
// Not necessary, asked on install already
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;
}

/**
Expand All @@ -98,6 +127,16 @@ public static boolean requestPermission(String name, Activity activity) {
* @return true/false. "true" if all permissions were granted otherwise returns "false".
*/
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) {
return true;
}
Expand All @@ -115,6 +154,9 @@ public static boolean requestManifestPermissions(Activity activity) {

List<String> requestedPermissions = new ArrayList<>();
for (String manifestPermission : manifestPermissions) {
if (excludes != null && excludes.contains(manifestPermission)) {
continue;
}
try {
if (manifestPermission.equals(Manifest.permission.MANAGE_EXTERNAL_STORAGE)) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
Expand Down