Repository: go
Issue: proposal: x/mobile: add permission request on android
Labels: Proposal,NeedsInvestigation,FeatureRequest,mobile

## What version of Go are you using (go version)?
```
go version go1.17.3 linux/amd64
```
## Does this issue reproduce with the latest release?
yes

## What operating system and processor architecture are you using (go env)?
<details><summary>go env Output</summary>

```
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/matejmag305/.cache/go-build"
GOENV="/home/matejmag305/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/matejmag305/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/matejmag305/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/home/matejmag305/go_sdk/go1.17.3"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/matejmag305/go_sdk/go1.17.3/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17.3"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build16419202=/tmp/go-build -gno-record-gcc-switches"
```

</details>

## What did you do?
I was found problem: is no easy way how request permission by runtime.......... 
<details><summary>More story </summary>

I m interesting in programing android apps with go, I have created small library for my (experimental) project, the library is trying implement bluetooth socket ....., but I have problem with manifest (androids ignore Bluetooth permission........), I was trying to fix(by runtime), then I realize that there is not easy way how request permission by runtime.........., I was writing this problem to  [fyne-io/fyne](https://github.com/fyne-io/fyne/issues/2533), which forms the graphics library of my experimental project, but now I think it is issue to gomobile.

</details>

##  Solution Proposal
The Solution would be use existing way how from ndk request permission. It would add two functions to programmer (user) interface. It is support with ndk solution:
<details><summary>More story </summary>

Add to code to `mobile/app/android.c` or somewhere where it pass best a few functions(or one, it depends on ordering of code) to background (in the fact I suggest the implementation solution, but it will be on implementation team how it will works) and two functions to user interface (the reason of this topic), pattern of these functions is the first answer in [ndk request camera](https://stackoverflow.com/questions/55062730/how-to-request-android-ndk-camera-permission-from-c-code)

</details>

### Exported GO side (main Proposal)

<details>
<summary> func RequestPermision(permission string) </summary>
Next code would use RunOnJVM from "mobileinit" package, it would have argument like "permission string" but in final version it could be "permission int", the code call `C.android_permissions(env, n)`, which could be void or return something, in further code is void ...

```
func RequestPermision(permission string){
	n := C.CString(permission)
	mobileinit.RunOnJVM(func(vm, jniEnv, ctx uintptr) error {
		env := (*C.JNIEnv)(unsafe.Pointer(jniEnv))
		C.android_permissions(env, n)// or result := ...
		return nil // or return result
	})
	C.free(unsafe.Pointer(n))
}
```

Other OS (IOS) should have: `func RequestPermision(permission string){} // dummy function`.
Of course probably IOS has similar system of permission, but I haven't experience for suggest any example........... <br>
</details>
<details>
<summary> func IsPermision(permission string) bool </summary>

The next exported function would have similar situation with argument like previous one - I do not know whether solution of type functions is the best..... and in the fact it: it would  unnecessary function, could be skipped because it could be execute at the beginning `android_permissions`, but sometime you want only find out whether permission is given and nothing more, therefore I mention this option .....    

```
func IsPermision(permission string) bool{
	n := C.CString(permission)
        var result bool
	mobileinit.RunOnJVM(func(vm, jniEnv, ctx uintptr) error {
		env := (*C.JNIEnv)(unsafe.Pointer(jniEnv))
		result = bool(android_has_permission( env, perm_name))
		return nil // or return result
	})
	C.free(unsafe.Pointer(n))
        return result
}
```

</details>

### Unexported - Background C side (of course only example how it could be done)
<details>
<summary> void android_permissions(JNIEnv* env,  char* perm_name) </summary>
In this example it is void, but it could be string (char*) or bool and of course argument could be int, at the beginning function control whether permission would be granted and after that if it was not granted it would be call function to "real" request.:

```
void android_permissions(JNIEnv* env,  char* perm_name) {// or bool or string to indentify error
    bool OK = android_has_permission(
       env, perm_name
    ) ;
    if(!OK) {
       android_request_permission(env,perm_name ); // or return  android_request_file_permissions(env,perm_name ); due to error
    }
}
```

</details>

<details>
<summary> void android_request_permission(JNIEnv* env, char* permissions)</summary>
It would be  call ndk java api to granted permission.

```
void android_request_permission(JNIEnv* env, char* permissions) { // or bool or string to reference error
    jobjectArray perm_array = (*env)->NewObjectArray(env,
      2,
      find_class(env, "java/lang/String"),
      (*env)->NewStringUTF(env, "")
    );
    (*env)->SetObjectArrayElement(env,
          perm_array, 1,
          android_permission_name(env, permissions)
        );
    (*env)->CallVoidMethod(
       current_class, request_permissions, perm_array, 0
    );
}
``` 

</details>

<details>
<summary> bool android_has_permission(JNIEnv* env, const char* perm_name) </summary>
The function to check whether permission is granted: 

```
bool android_has_permission(JNIEnv* env, const char* perm_name) {  
    bool result = false;
    jstring ls_PERM = android_permission_name(env, perm_name);
    int PERMISSION_GRANTED = -1;
    {
       jclass ClassPackageManager = find_class(env, "android/content/pm/PackageManager" );
       jfieldID lid_PERMISSION_GRANTED = (*env)->GetStaticFieldID(env,
          ClassPackageManager, "PERMISSION_GRANTED", "I"
       );
       PERMISSION_GRANTED = (*env)->GetStaticIntField(env,
          ClassPackageManager, lid_PERMISSION_GRANTED
       );
    }
    {
       jclass ClassContext = find_class(env,  "android/content/Context"   );
       jmethodID MethodcheckSelfPermission = find_method(env, ClassContext,
       "checkSelfPermission", "(Ljava/lang/String;)I" );
       int int_result =(int) (*env)->CallIntMethod(env,
           current_class, MethodcheckSelfPermission, ls_PERM
       );
       result = (int_result == PERMISSION_GRANTED);
    }
    return result;
}
``` 

</details>

<details>
<summary> jclass android_permission_name(JNIEnv* env, const char* perm_name) </summary>
The function to get permission class from (char*) string: 

```
jclass android_permission_name(JNIEnv* env, const char* perm_name) {
    // nested class permission in class android.Manifest,
    // hence android 'slash' Manifest 'dollar' permission
    jclass ClassManifestpermission = find_class(env, "android/Manifest$permission");
    jfieldID lid_PERM = (*env)->GetStaticFieldID(env,
       ClassManifestpermission, perm_name, "Ljava/lang/String;"
    );
    jclass ls_PERM = (jclass)((*env)->GetStaticObjectField(env,
        ClassManifestpermission, lid_PERM
    ));
    return ls_PERM;
}
```

</details>

<details>
<summary> static jmethodID request_permissions </summary>
And of course some functions could be "extracted" at init: 

```
static jmethodID request_permissions;  
//which init in function ANativeActivity_onCreate
```

Like show:  https://github.com/golang/mobile/blob/4e6c2922fdeed32d3596616518aaee7b0d79ce55/app/android.c#L71
and https://github.com/golang/mobile/blob/4e6c2922fdeed32d3596616518aaee7b0d79ce55/app/android.c#L78 
</details>
<br>
I m sorry for my English, which is not best....... 

### EDITED: 
Of course there is further reason, why implement this request: dangerous permission:  https://developer.android.com/training. Yes I know that golang isn't primary for android and majority of request will never need (because missing API), but  there are some permissions, which gomobile could use ..............., for example above mentioned Bluetooth. 
### ADDED LATER: 
I have made some prototype of this (it is working on my android simulator): https://github.com/MatejMagat305/golang-prototype-permision (without adding to current code, only use function RunOnJVM) - it is only prototype which probably contains memory leak and maybe it should be good added or change functions to request multiple strings ............... And yes, something similar is on IOS, but I haven't suitable device, experience and internet claim that work a little bit diferent