mux: mp4: fix broken p_obj test
[vlc.git] / src / android / specific.c
blobc75b5e239d9bbc520e1812c357ec03a205d1b690
1 /*****************************************************************************
2 * specific.c: stubs for Android OS-specific initialization
3 *****************************************************************************
4 * Copyright © 2016 VLC authors and VideoLAN
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include <assert.h>
27 #include <vlc_common.h>
28 #include <vlc_network.h>
29 #include <vlc_fs.h>
30 #include "../libvlc.h"
31 #include "config/configuration.h"
33 #include <string.h>
34 #include <jni.h>
36 static JavaVM *s_jvm = NULL;
37 #define GENERIC_DIR_COUNT (VLC_VIDEOS_DIR - VLC_DESKTOP_DIR + 1)
38 static char *ppsz_generic_names[GENERIC_DIR_COUNT] = {};
39 static struct {
40 struct {
41 jclass clazz;
42 jmethodID getExternalStoragePublicDirectory;
43 } Environment;
44 struct {
45 jmethodID getAbsolutePath;
46 } File;
47 struct {
48 jclass clazz;
49 jmethodID getProperty;
50 } System;
51 } fields = { .Environment.clazz = NULL };
53 static char *
54 get_java_string(JNIEnv *env, jclass clazz, const char *psz_name)
56 jfieldID id = (*env)->GetStaticFieldID(env, clazz, psz_name,
57 "Ljava/lang/String;");
58 if ((*env)->ExceptionCheck(env))
60 (*env)->ExceptionClear(env);
61 return NULL;
64 jstring jstr = (*env)->GetStaticObjectField(env, clazz, id);
66 const char *psz_str = (*env)->GetStringUTFChars(env, jstr, 0);
67 if (psz_str == NULL)
68 return NULL;
70 char *psz_strdup = strdup(psz_str);
72 (*env)->ReleaseStringUTFChars(env, jstr, psz_str);
73 (*env)->DeleteLocalRef(env, jstr);
75 return psz_strdup;
78 void
79 JNI_OnUnload(JavaVM* vm, void* reserved)
81 (void) reserved;
83 for (size_t i = 0; i < GENERIC_DIR_COUNT; ++i)
84 free(ppsz_generic_names[i]);
86 JNIEnv* env = NULL;
87 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK)
88 return;
90 if (fields.Environment.clazz)
91 (*env)->DeleteGlobalRef(env, fields.Environment.clazz);
93 if (fields.System.clazz)
94 (*env)->DeleteGlobalRef(env, fields.System.clazz);
97 /* This function is called when the libvlcore dynamic library is loaded via the
98 * java.lang.System.loadLibrary method. Therefore, s_jvm will be already set
99 * when libvlc_InternalInit is called. */
100 jint
101 JNI_OnLoad(JavaVM *vm, void *reserved)
103 s_jvm = vm;
104 JNIEnv* env = NULL;
106 if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_2) != JNI_OK)
107 return -1;
109 jclass clazz = (*env)->FindClass(env, "android/os/Environment");
110 if ((*env)->ExceptionCheck(env))
111 return -1;
113 static const char *ppsz_env_names[GENERIC_DIR_COUNT] = {
114 NULL, /* VLC_DESKTOP_DIR */
115 "DIRECTORY_DOWNLOADS", /* VLC_DOWNLOAD_DIR */
116 NULL, /* VLC_TEMPLATES_DIR */
117 NULL, /* VLC_PUBLICSHARE_DIR */
118 "DIRECTORY_DOCUMENTS", /* VLC_DOCUMENTS_DIR */
119 "DIRECTORY_MUSIC", /* VLC_MUSIC_DIR */
120 "DIRECTORY_PICTURES", /* VLC_PICTURES_DIR */
121 "DIRECTORY_MOVIES", /* VLC_VIDEOS_DIR */
123 for (size_t i = 0; i < GENERIC_DIR_COUNT; ++i)
125 if (ppsz_env_names[i] != NULL)
126 ppsz_generic_names[i] = get_java_string(env, clazz,
127 ppsz_env_names[i]);
130 fields.Environment.clazz = (*env)->NewGlobalRef(env, clazz);
131 fields.Environment.getExternalStoragePublicDirectory =
132 (*env)->GetStaticMethodID(env, clazz,
133 "getExternalStoragePublicDirectory",
134 "(Ljava/lang/String;)Ljava/io/File;");
135 if ((*env)->ExceptionCheck(env))
136 goto error;
137 (*env)->DeleteLocalRef(env, clazz);
139 clazz = (*env)->FindClass(env, "java/io/File");
140 fields.File.getAbsolutePath =
141 (*env)->GetMethodID(env, clazz, "getAbsolutePath",
142 "()Ljava/lang/String;");
143 if ((*env)->ExceptionCheck(env))
144 goto error;
145 (*env)->DeleteLocalRef(env, clazz);
147 clazz = (*env)->FindClass(env, "java/lang/System");
148 if ((*env)->ExceptionCheck(env))
149 goto error;
150 fields.System.clazz = (*env)->NewGlobalRef(env, clazz);
151 fields.System.getProperty =
152 (*env)->GetStaticMethodID(env, clazz, "getProperty",
153 "(Ljava/lang/String;)Ljava/lang/String;");
154 (*env)->DeleteLocalRef(env, clazz);
156 return JNI_VERSION_1_2;
158 error:
159 if (clazz)
160 (*env)->DeleteLocalRef(env, clazz);
161 JNI_OnUnload(vm, reserved);
162 return -1;
165 void
166 system_Init(void)
170 void
171 system_Configure(libvlc_int_t *p_libvlc, int i_argc, const char *const pp_argv[])
173 (void)i_argc; (void)pp_argv;
174 assert(s_jvm != NULL);
175 var_Create(p_libvlc, "android-jvm", VLC_VAR_ADDRESS);
176 var_SetAddress(p_libvlc, "android-jvm", s_jvm);
179 static char *config_GetHomeDir(const char *psz_dir, const char *psz_default_dir)
181 char *psz_home = getenv("HOME");
182 if (psz_home == NULL)
183 goto fallback;
185 if (psz_dir == NULL)
186 return strdup(psz_home);
188 char *psz_fullpath;
189 if (asprintf(&psz_fullpath, "%s/%s", psz_home, psz_dir) == -1)
190 goto fallback;
191 if (vlc_mkdir(psz_fullpath, 0700) == -1 && errno != EEXIST)
193 free(psz_fullpath);
194 goto fallback;
196 return psz_fullpath;
198 fallback:
199 return psz_default_dir != NULL ? strdup(psz_default_dir) : NULL;
202 static JNIEnv *get_env(bool *p_detach)
204 JNIEnv *env;
205 if ((*s_jvm)->GetEnv(s_jvm, (void **)&env, JNI_VERSION_1_2) != JNI_OK)
207 /* attach the thread to the Java VM */
208 JavaVMAttachArgs args;
210 args.version = JNI_VERSION_1_2;
211 args.name = "config_GetGenericDir";
212 args.group = NULL;
214 if ((*s_jvm)->AttachCurrentThread(s_jvm, &env, &args) != JNI_OK)
215 return NULL;
216 *p_detach = true;
218 else
219 *p_detach = false;
220 return env;
223 static void release_env(bool b_detach)
225 if (b_detach)
226 (*s_jvm)->DetachCurrentThread(s_jvm);
229 static char *config_GetGenericDir(const char *psz_name)
231 JNIEnv *env;
232 bool b_detach;
233 char *psz_ret = NULL;
235 env = get_env(&b_detach);
236 if (env == NULL)
237 return NULL;
239 jstring jname= (*env)->NewStringUTF(env, psz_name);
240 if ((*env)->ExceptionCheck(env))
242 (*env)->ExceptionClear(env);
243 jname = NULL;
245 if (jname == NULL)
246 goto error;
248 jobject jfile = (*env)->CallStaticObjectMethod(env,
249 fields.Environment.clazz,
250 fields.Environment.getExternalStoragePublicDirectory,
251 jname);
252 (*env)->DeleteLocalRef(env, jname);
253 if (jfile == NULL)
254 goto error;
256 jstring jpath = (*env)->CallObjectMethod(env, jfile,
257 fields.File.getAbsolutePath);
258 (*env)->DeleteLocalRef(env, jfile);
260 const char *psz_path = (*env)->GetStringUTFChars(env, jpath, 0);
261 if (psz_path == NULL)
262 goto error;
263 psz_ret = strdup(psz_path);
264 (*env)->ReleaseStringUTFChars(env, jpath, psz_path);
265 (*env)->DeleteLocalRef(env, jpath);
267 error:
268 release_env(b_detach);
269 return psz_ret;
272 char *config_GetUserDir (vlc_userdir_t type)
274 switch (type)
276 case VLC_USERDATA_DIR:
277 return config_GetHomeDir(".share",
278 "/sdcard/Android/data/org.videolan.vlc");
279 case VLC_CACHE_DIR:
280 return config_GetHomeDir(".cache",
281 "/sdcard/Android/data/org.videolan.vlc/cache");
282 case VLC_HOME_DIR:
283 return config_GetHomeDir(NULL, NULL);
284 case VLC_CONFIG_DIR:
285 return config_GetHomeDir(".config", NULL);
287 case VLC_DESKTOP_DIR:
288 case VLC_DOWNLOAD_DIR:
289 case VLC_TEMPLATES_DIR:
290 case VLC_PUBLICSHARE_DIR:
291 case VLC_DOCUMENTS_DIR:
292 case VLC_MUSIC_DIR:
293 case VLC_PICTURES_DIR:
294 case VLC_VIDEOS_DIR:
296 assert(type >= VLC_DESKTOP_DIR && type <= VLC_VIDEOS_DIR);
297 const char *psz_name = ppsz_generic_names[type - VLC_DESKTOP_DIR];
298 if (psz_name != NULL)
299 return config_GetGenericDir(psz_name);
302 return NULL;
305 char *config_GetSysPath(vlc_sysdir_t type, const char *filename)
307 char *dir = NULL;
309 switch (type)
311 case VLC_SYSDATA_DIR:
312 dir = strdup("/system/usr/share");
313 break;
314 case VLC_LIB_DIR:
315 dir = config_GetLibDir();
316 break;
317 default:
318 break;
321 if (filename == NULL || dir == NULL)
322 return dir;
324 char *path;
325 if (unlikely(asprintf(&path, "%s/%s", dir, filename) == -1))
326 path = NULL;
327 free(dir);
328 return path;
332 * Determines the network proxy server to use (if any).
334 * This function fetch the Android proxy using the System.getProperty() method
335 * with "http.proxyHost" and "http.proxyPort" keys. This is working only for
336 * Android 4.0 and after.
338 * @param url absolute URL for which to get the proxy server (unused)
339 * @return proxy URL, NULL if no proxy or error
341 char *vlc_getProxyUrl(const char *url)
343 VLC_UNUSED(url);
344 JNIEnv *env;
345 bool b_detach;
346 char *psz_ret = NULL;
347 const char *psz_host = NULL, *psz_port = NULL;
348 jstring jhost = NULL, jport = NULL;
350 env = get_env(&b_detach);
351 if (env == NULL)
352 return NULL;
354 /* Fetch "http.proxyHost" property */
355 jstring jkey = (*env)->NewStringUTF(env, "http.proxyHost");
356 if ((*env)->ExceptionCheck(env))
358 (*env)->ExceptionClear(env);
359 jkey = NULL;
361 if (jkey == NULL)
362 goto end;
364 jhost = (*env)->CallStaticObjectMethod(env, fields.System.clazz,
365 fields.System.getProperty, jkey);
366 (*env)->DeleteLocalRef(env, jkey);
367 if (jhost == NULL)
368 goto end;
370 psz_host = (*env)->GetStringUTFChars(env, jhost, 0);
371 /* Ensure the property is valid */
372 if (psz_host == NULL || psz_host[0] == '\0')
373 goto end;
375 /* Fetch "http.proxyPort" property (only if "http.proxyHost" is valid) */
376 jkey = (*env)->NewStringUTF(env, "http.proxyPort");
377 if ((*env)->ExceptionCheck(env))
379 (*env)->ExceptionClear(env);
380 jkey = NULL;
382 if (jkey == NULL)
383 goto end;
385 jport = (*env)->CallStaticObjectMethod(env, fields.System.clazz,
386 fields.System.getProperty, jkey);
387 (*env)->DeleteLocalRef(env, jkey);
389 /* Ensure the property is valid */
390 if (jport != NULL)
392 psz_port = (*env)->GetStringUTFChars(env, jport, 0);
393 if (psz_port != NULL && (psz_port[0] == '\0' || psz_port[0] == '0'))
395 (*env)->ReleaseStringUTFChars(env, jport, psz_port);
396 psz_port = NULL;
400 /* Concat "http://" "http.proxyHost" and "http.proxyPort" */
401 if (asprintf(&psz_ret, "http://%s%s%s",
402 psz_host,
403 psz_port != NULL ? ":" : "",
404 psz_port != NULL ? psz_port : "") == -1)
405 psz_ret = NULL;
407 end:
408 if (psz_host != NULL)
409 (*env)->ReleaseStringUTFChars(env, jhost, psz_host);
410 if (jhost != NULL)
411 (*env)->DeleteLocalRef(env, jhost);
412 if (psz_port != NULL)
413 (*env)->ReleaseStringUTFChars(env, jport, psz_port);
414 if (jport != NULL)
415 (*env)->DeleteLocalRef(env, jport);
416 release_env(b_detach);
417 return psz_ret;