crossbow_permissions/
lib.rs

1// TODO: Add enum with all android permissions
2// TODO: Add JNI calls to request permissions
3
4#[cfg(target_os = "android")]
5pub fn request_permission() {}
6
7/*
8/**
9 * \brief Gets the internal name for an android permission.
10 * \param[in] lJNIEnv a pointer to the JNI environment
11 * \param[in] perm_name the name of the permission, e.g.,
12 *   "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
13 * \return a jstring with the internal name of the permission,
14 *   to be used with android Java functions
15 *   Context.checkSelfPermission() or Activity.requestPermissions()
16 */
17 jstring android_permission_name(JNIEnv* lJNIEnv, const char* perm_name) {
18    // nested class permission in class android.Manifest,
19    // hence android 'slash' Manifest 'dollar' permission
20    jclass ClassManifestpermission = lJNIEnv->FindClass(
21       "android/Manifest$permission"
22    );
23    jfieldID lid_PERM = lJNIEnv->GetStaticFieldID(
24       ClassManifestpermission, perm_name, "Ljava/lang/String;"
25    );
26    jstring ls_PERM = (jstring)(lJNIEnv->GetStaticObjectField(
27        ClassManifestpermission, lid_PERM
28    ));
29    return ls_PERM;
30}
31
32/**
33 * \brief Tests whether a permission is granted.
34 * \param[in] app a pointer to the android app.
35 * \param[in] perm_name the name of the permission, e.g.,
36 *   "READ_EXTERNAL_STORAGE", "WRITE_EXTERNAL_STORAGE".
37 * \retval true if the permission is granted.
38 * \retval false otherwise.
39 * \note Requires Android API level 23 (Marshmallow, May 2015)
40 */
41bool android_has_permission(struct android_app* app, const char* perm_name) {
42    JavaVM* lJavaVM = app->activity->vm;
43    JNIEnv* lJNIEnv = nullptr;
44    bool lThreadAttached = false;
45
46    // Get JNIEnv from lJavaVM using GetEnv to test whether
47    // thread is attached or not to the VM. If not, attach it
48    // (and note that it will need to be detached at the end
49    //  of the function).
50    switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) {
51      case JNI_OK:
52        break;
53      case JNI_EDETACHED: {
54        jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr);
55        if(lResult == JNI_ERR) {
56          throw std::runtime_error("Could not attach current thread");
57        }
58        lThreadAttached = true;
59      } break;
60      case JNI_EVERSION:
61        throw std::runtime_error("Invalid java version");
62    }
63
64    bool result = false;
65
66    jstring ls_PERM = android_permission_name(lJNIEnv, perm_name);
67
68    jint PERMISSION_GRANTED = jint(-1);
69    {
70       jclass ClassPackageManager = lJNIEnv->FindClass(
71          "android/content/pm/PackageManager"
72       );
73       jfieldID lid_PERMISSION_GRANTED = lJNIEnv->GetStaticFieldID(
74          ClassPackageManager, "PERMISSION_GRANTED", "I"
75       );
76       PERMISSION_GRANTED = lJNIEnv->GetStaticIntField(
77          ClassPackageManager, lid_PERMISSION_GRANTED
78       );
79    }
80    {
81       jobject activity = app->activity->clazz;
82       jclass ClassContext = lJNIEnv->FindClass(
83          "android/content/Context"
84       );
85       jmethodID MethodcheckSelfPermission = lJNIEnv->GetMethodID(
86          ClassContext, "checkSelfPermission", "(Ljava/lang/String;)I"
87       );
88       jint int_result = lJNIEnv->CallIntMethod(
89           activity, MethodcheckSelfPermission, ls_PERM
90       );
91       result = (int_result == PERMISSION_GRANTED);
92    }
93
94    if(lThreadAttached) {
95      lJavaVM->DetachCurrentThread();
96    }
97
98    return result;
99}
100
101/**
102 * \brief Query file permissions.
103 * \details This opens the system dialog that lets the user
104 *  grant (or deny) the permission.
105 * \param[in] app a pointer to the android app.
106 * \note Requires Android API level 23 (Marshmallow, May 2015)
107 */
108void android_request_file_permissions(struct android_app* app) {
109    JavaVM* lJavaVM = app->activity->vm;
110    JNIEnv* lJNIEnv = nullptr;
111    bool lThreadAttached = false;
112
113    // Get JNIEnv from lJavaVM using GetEnv to test whether
114    // thread is attached or not to the VM. If not, attach it
115    // (and note that it will need to be detached at the end
116    //  of the function).
117    switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) {
118      case JNI_OK:
119        break;
120      case JNI_EDETACHED: {
121        jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr);
122        if(lResult == JNI_ERR) {
123          throw std::runtime_error("Could not attach current thread");
124        }
125        lThreadAttached = true;
126      } break;
127      case JNI_EVERSION:
128        throw std::runtime_error("Invalid java version");
129      }
130
131    jobjectArray perm_array = lJNIEnv->NewObjectArray(
132      2,
133      lJNIEnv->FindClass("java/lang/String"),
134      lJNIEnv->NewStringUTF("")
135    );
136
137    lJNIEnv->SetObjectArrayElement(
138      perm_array, 0,
139      android_permission_name(lJNIEnv, "READ_EXTERNAL_STORAGE")
140    );
141
142    lJNIEnv->SetObjectArrayElement(
143      perm_array, 1,
144      android_permission_name(lJNIEnv, "WRITE_EXTERNAL_STORAGE")
145    );
146
147    jobject activity = app->activity->clazz;
148
149    jclass ClassActivity = lJNIEnv->FindClass(
150       "android/app/Activity"
151    );
152
153    jmethodID MethodrequestPermissions = lJNIEnv->GetMethodID(
154       ClassActivity, "requestPermissions", "([Ljava/lang/String;I)V"
155    );
156
157    // Last arg (0) is just for the callback (that I do not use)
158    lJNIEnv->CallVoidMethod(
159       activity, MethodrequestPermissions, perm_array, 0
160    );
161
162    if(lThreadAttached) {
163       lJavaVM->DetachCurrentThread();
164    }
165}
166
167void check_android_permissions(struct android_app* app) {
168    bool OK = android_has_permission(
169       app, "READ_EXTERNAL_STORAGE"
170    ) && android_has_permission(
171       app, "WRITE_EXTERNAL_STORAGE"
172    );
173    if(!OK) {
174       android_request_file_permissions(app);
175    }
176}
177*/