Merge branch 'android-okhttp'
This commit is contained in:
@@ -28,6 +28,8 @@
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
JavaVM* theJVM;
|
||||
|
||||
std::string cachePath;
|
||||
std::string dataPath;
|
||||
std::string apkPath;
|
||||
@@ -90,7 +92,15 @@ jmethodID pointFConstructorId = nullptr;
|
||||
jfieldID pointFXId = nullptr;
|
||||
jfieldID pointFYId = nullptr;
|
||||
|
||||
bool throw_error(JNIEnv *env, const char *msg) {
|
||||
jclass httpContextClass = nullptr;
|
||||
jmethodID httpContextGetInstanceId = nullptr;
|
||||
jmethodID httpContextCreateRequestId = nullptr;
|
||||
|
||||
jclass httpRequestClass = nullptr;
|
||||
jmethodID httpRequestStartId = nullptr;
|
||||
jmethodID httpRequestCancelId = nullptr;
|
||||
|
||||
bool throw_jni_error(JNIEnv *env, const char *msg) {
|
||||
if (env->ThrowNew(runtimeExceptionClass, msg) < 0) {
|
||||
env->ExceptionDescribe();
|
||||
return false;
|
||||
@@ -99,6 +109,41 @@ bool throw_error(JNIEnv *env, const char *msg) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName) {
|
||||
JavaVMAttachArgs args = {JNI_VERSION_1_2, threadName.c_str(), NULL};
|
||||
|
||||
jint ret;
|
||||
*env = nullptr;
|
||||
bool detach = false;
|
||||
ret = vm->GetEnv(reinterpret_cast<void **>(env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
if (ret != JNI_EDETACHED) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
|
||||
throw new std::runtime_error("GetEnv() failed");
|
||||
} else {
|
||||
ret = vm->AttachCurrentThread(env, &args);
|
||||
if (ret != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("AttachCurrentThread() failed");
|
||||
}
|
||||
detach = true;
|
||||
}
|
||||
}
|
||||
|
||||
return detach;
|
||||
}
|
||||
|
||||
void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach) {
|
||||
if (detach) {
|
||||
jint ret;
|
||||
if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("DetachCurrentThread() failed");
|
||||
}
|
||||
}
|
||||
*env = nullptr;
|
||||
}
|
||||
|
||||
std::string std_string_from_jstring(JNIEnv *env, jstring jstr) {
|
||||
std::string str;
|
||||
|
||||
@@ -342,7 +387,7 @@ void JNICALL nativeInitializeDisplay(JNIEnv *env, jobject obj, jlong nativeMapVi
|
||||
{
|
||||
nativeMapView->initializeDisplay();
|
||||
} catch(const std::exception& e) {
|
||||
throw_error(env, "Unable to initialize GL display.");
|
||||
throw_jni_error(env, "Unable to initialize GL display.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,7 +406,7 @@ void JNICALL nativeInitializeContext(JNIEnv *env, jobject obj, jlong nativeMapVi
|
||||
try {
|
||||
nativeMapView->initializeContext();
|
||||
} catch(const std::exception& e) {
|
||||
throw_error(env, "Unable to initialize GL context.");
|
||||
throw_jni_error(env, "Unable to initialize GL context.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +425,7 @@ void JNICALL nativeCreateSurface(JNIEnv *env, jobject obj, jlong nativeMapViewPt
|
||||
try {
|
||||
nativeMapView->createSurface(ANativeWindow_fromSurface(env, surface));
|
||||
} catch(const std::exception& e) {
|
||||
throw_error(env, "Unable to create GL surface.");
|
||||
throw_jni_error(env, "Unable to create GL surface.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,6 +1176,8 @@ extern "C" {
|
||||
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnLoad");
|
||||
|
||||
theJVM = vm;
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
jint ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
@@ -1426,6 +1473,36 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
httpContextClass = env->FindClass("com/mapbox/mapboxgl/http/HTTPContext");
|
||||
if (httpContextClass == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
httpContextGetInstanceId = env->GetStaticMethodID(httpContextClass, "getInstance", "()Lcom/mapbox/mapboxgl/http/HTTPContext;");
|
||||
if (httpContextGetInstanceId == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
httpContextCreateRequestId = env->GetMethodID(httpContextClass, "createRequest", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lcom/mapbox/mapboxgl/http/HTTPContext$HTTPRequest;");
|
||||
if (httpContextCreateRequestId == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
httpRequestClass = env->FindClass("com/mapbox/mapboxgl/http/HTTPContext$HTTPRequest");
|
||||
if (httpRequestClass == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
httpRequestStartId = env->GetMethodID(httpRequestClass, "start", "()V");
|
||||
if (httpRequestStartId == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
httpRequestCancelId = env->GetMethodID(httpRequestClass, "cancel", "()V");
|
||||
if (httpRequestCancelId == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
const std::vector<JNINativeMethod> methods = {
|
||||
{"nativeCreate", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;FIJ)J",
|
||||
reinterpret_cast<void *>(&nativeCreate)},
|
||||
@@ -1524,10 +1601,16 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
//{"nativeGetWorldBoundsMeters", "(J)V", reinterpret_cast<void *>(&nativeGetWorldBoundsMeters)},
|
||||
//{"nativeGetWorldBoundsLatLng", "(J)V", reinterpret_cast<void *>(&nativeGetWorldBoundsLatLng)},
|
||||
{"nativeGetMetersPerPixelAtLatitude", "(JDD)D", reinterpret_cast<void *>(&nativeGetMetersPerPixelAtLatitude)},
|
||||
{"nativeProjectedMetersForLatLng", "(JLcom/mapbox/mapboxgl/geometry/LatLng;)Lcom/mapbox/mapboxgl/geometry/ProjectedMeters;", reinterpret_cast<void *>(&nativeProjectedMetersForLatLng)},
|
||||
{"nativeLatLngForProjectedMeters", "(JLcom/mapbox/mapboxgl/geometry/ProjectedMeters;)Lcom/mapbox/mapboxgl/geometry/LatLng;", reinterpret_cast<void *>(&nativeLatLngForProjectedMeters)},
|
||||
{"nativePixelForLatLng", "(JLcom/mapbox/mapboxgl/geometry/LatLng;)Landroid/graphics/PointF;", reinterpret_cast<void *>(&nativePixelForLatLng)},
|
||||
{"nativeLatLngForPixel", "(JLandroid/graphics/PointF;)Lcom/mapbox/mapboxgl/geometry/LatLng;", reinterpret_cast<void *>(&nativeLatLngForPixel)},
|
||||
{"nativeProjectedMetersForLatLng",
|
||||
"(JLcom/mapbox/mapboxgl/geometry/LatLng;)Lcom/mapbox/mapboxgl/geometry/ProjectedMeters;",
|
||||
reinterpret_cast<void *>(&nativeProjectedMetersForLatLng)},
|
||||
{"nativeLatLngForProjectedMeters",
|
||||
"(JLcom/mapbox/mapboxgl/geometry/ProjectedMeters;)Lcom/mapbox/mapboxgl/geometry/LatLng;",
|
||||
reinterpret_cast<void *>(&nativeLatLngForProjectedMeters)},
|
||||
{"nativePixelForLatLng", "(JLcom/mapbox/mapboxgl/geometry/LatLng;)Landroid/graphics/PointF;",
|
||||
reinterpret_cast<void *>(&nativePixelForLatLng)},
|
||||
{"nativeLatLngForPixel", "(JLandroid/graphics/PointF;)Lcom/mapbox/mapboxgl/geometry/LatLng;",
|
||||
reinterpret_cast<void *>(&nativeLatLngForPixel)},
|
||||
};
|
||||
|
||||
if (env->RegisterNatives(nativeMapViewClass, methods.data(), methods.size()) < 0) {
|
||||
@@ -1641,6 +1724,37 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
httpContextClass = reinterpret_cast<jclass>(env->NewGlobalRef(httpContextClass));
|
||||
if (httpContextClass == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
env->DeleteGlobalRef(latLngClass);
|
||||
env->DeleteGlobalRef(markerClass);
|
||||
env->DeleteGlobalRef(latLngZoomClass);
|
||||
env->DeleteGlobalRef(polylineClass);
|
||||
env->DeleteGlobalRef(polygonClass);
|
||||
env->DeleteGlobalRef(runtimeExceptionClass);
|
||||
env->DeleteGlobalRef(nullPointerExceptionClass);
|
||||
env->DeleteGlobalRef(arrayListClass);
|
||||
env->DeleteGlobalRef(projectedMetersClass);
|
||||
env->DeleteGlobalRef(pointFClass);
|
||||
}
|
||||
|
||||
httpRequestClass = reinterpret_cast<jclass>(env->NewGlobalRef(httpRequestClass));
|
||||
if (httpRequestClass == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
env->DeleteGlobalRef(latLngClass);
|
||||
env->DeleteGlobalRef(markerClass);
|
||||
env->DeleteGlobalRef(latLngZoomClass);
|
||||
env->DeleteGlobalRef(polylineClass);
|
||||
env->DeleteGlobalRef(polygonClass);
|
||||
env->DeleteGlobalRef(runtimeExceptionClass);
|
||||
env->DeleteGlobalRef(nullPointerExceptionClass);
|
||||
env->DeleteGlobalRef(arrayListClass);
|
||||
env->DeleteGlobalRef(projectedMetersClass);
|
||||
env->DeleteGlobalRef(pointFClass);
|
||||
env->DeleteGlobalRef(httpContextClass);
|
||||
}
|
||||
|
||||
char release[PROP_VALUE_MAX] = "";
|
||||
__system_property_get("ro.build.version.release", release);
|
||||
androidRelease = std::string(release);
|
||||
@@ -1651,6 +1765,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "JNI_OnUnload");
|
||||
|
||||
theJVM = vm;
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
jint ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
@@ -1725,5 +1841,15 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
|
||||
pointFConstructorId = nullptr;
|
||||
pointFXId = nullptr;
|
||||
pointFYId = nullptr;
|
||||
|
||||
env->DeleteGlobalRef(httpContextClass);
|
||||
httpContextGetInstanceId = nullptr;
|
||||
httpContextCreateRequestId = nullptr;
|
||||
|
||||
env->DeleteGlobalRef(httpRequestClass);
|
||||
httpRequestStartId = nullptr;
|
||||
httpRequestCancelId = nullptr;
|
||||
|
||||
theJVM = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,38 +148,15 @@ void NativeMapView::invalidate() {
|
||||
assert(vm != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
JavaVMAttachArgs args = {JNI_VERSION_1_2, "NativeMapView::invalidate()", NULL};
|
||||
|
||||
jint ret;
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = false;
|
||||
ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
if (ret != JNI_EDETACHED) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
|
||||
throw new std::runtime_error("GetEnv() failed");
|
||||
} else {
|
||||
ret = vm->AttachCurrentThread(&env, &args);
|
||||
if (ret != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("AttachCurrentThread() failed");
|
||||
}
|
||||
detach = true;
|
||||
}
|
||||
}
|
||||
bool detach = attach_jni_thread(vm, &env, "NativeMapView::invalidate()");
|
||||
|
||||
env->CallVoidMethod(obj, onInvalidateId);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
if (detach) {
|
||||
if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("DetachCurrentThread() failed");
|
||||
}
|
||||
}
|
||||
env = nullptr;
|
||||
detach_jni_thread(vm, &env, detach);
|
||||
}
|
||||
|
||||
void NativeMapView::swap() {
|
||||
@@ -662,38 +639,15 @@ void NativeMapView::notifyMapChange(mbgl::MapChange) {
|
||||
assert(vm != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
JavaVMAttachArgs args = {JNI_VERSION_1_2, "NativeMapView::notifyMapChange()", NULL};
|
||||
|
||||
jint ret;
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = false;
|
||||
ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
if (ret != JNI_EDETACHED) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
|
||||
throw new std::runtime_error("GetEnv() failed");
|
||||
} else {
|
||||
ret = vm->AttachCurrentThread(&env, &args);
|
||||
if (ret != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("AttachCurrentThread() failed");
|
||||
}
|
||||
detach = true;
|
||||
}
|
||||
}
|
||||
bool detach = attach_jni_thread(vm, &env, "NativeMapView::notifyMapChange()");
|
||||
|
||||
env->CallVoidMethod(obj, onMapChangedId);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
if (detach) {
|
||||
if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("DetachCurrentThread() failed");
|
||||
}
|
||||
}
|
||||
env = nullptr;
|
||||
detach_jni_thread(vm, &env, detach);
|
||||
}
|
||||
|
||||
void NativeMapView::enableFps(bool enable) {
|
||||
@@ -727,38 +681,15 @@ void NativeMapView::updateFps() {
|
||||
assert(vm != nullptr);
|
||||
assert(obj != nullptr);
|
||||
|
||||
JavaVMAttachArgs args = {JNI_VERSION_1_2, "NativeMapView::updateFps()", NULL};
|
||||
|
||||
jint ret;
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = false;
|
||||
ret = vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
|
||||
if (ret != JNI_OK) {
|
||||
if (ret != JNI_EDETACHED) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "GetEnv() failed with %i", ret);
|
||||
throw new std::runtime_error("GetEnv() failed");
|
||||
} else {
|
||||
ret = vm->AttachCurrentThread(&env, &args);
|
||||
if (ret != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "AttachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("AttachCurrentThread() failed");
|
||||
}
|
||||
detach = true;
|
||||
}
|
||||
}
|
||||
bool detach = attach_jni_thread(vm, &env, "NativeMapView::updateFps()");
|
||||
|
||||
env->CallVoidMethod(obj, onFpsChangedId, fps);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
if (detach) {
|
||||
if ((ret = vm->DetachCurrentThread()) != JNI_OK) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "DetachCurrentThread() failed with %i", ret);
|
||||
throw new std::runtime_error("DetachCurrentThread() failed");
|
||||
}
|
||||
}
|
||||
env = nullptr;
|
||||
detach_jni_thread(vm, &env, detach);
|
||||
}
|
||||
|
||||
void NativeMapView::onInvalidate() {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.mapbox.mapboxgl.http;
|
||||
|
||||
import com.mapbox.mapboxgl.constants.MapboxConstants;
|
||||
import com.squareup.okhttp.Call;
|
||||
import com.squareup.okhttp.Callback;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.SocketException;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
|
||||
class HTTPContext {
|
||||
|
||||
private static final int CONNECTION_ERROR = 0;
|
||||
private static final int TEMPORARY_ERROR = 1;
|
||||
private static final int PERMANENT_ERROR = 2;
|
||||
|
||||
private static HTTPContext mInstance = null;
|
||||
|
||||
private OkHttpClient mClient;
|
||||
|
||||
private HTTPContext() {
|
||||
super();
|
||||
mClient = new OkHttpClient();
|
||||
}
|
||||
|
||||
public static HTTPContext getInstance() {
|
||||
if (mInstance == null) {
|
||||
mInstance = new HTTPContext();
|
||||
}
|
||||
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public HTTPRequest createRequest(long nativePtr, String resourceUrl, String userAgent, String etag, String modified) {
|
||||
return new HTTPRequest(nativePtr, resourceUrl, userAgent, etag, modified);
|
||||
}
|
||||
|
||||
public class HTTPRequest implements Callback {
|
||||
private long mNativePtr = 0;
|
||||
|
||||
private Call mCall;
|
||||
private Request mRequest;
|
||||
|
||||
private native void nativeOnFailure(long nativePtr, int type, String message);
|
||||
private native void nativeOnResponse(long nativePtr, int code, String message, String etag, String modified, String cacheControl, String expires, byte[] body);
|
||||
|
||||
private HTTPRequest(long nativePtr, String resourceUrl, String userAgent, String etag, String modified) {
|
||||
mNativePtr = nativePtr;
|
||||
Request.Builder builder = new Request.Builder().url(resourceUrl).tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE)).addHeader("User-Agent", userAgent);
|
||||
if (etag.length() > 0) {
|
||||
builder = builder.addHeader("If-None-Match", etag);
|
||||
} else if (modified.length() > 0) {
|
||||
builder = builder.addHeader("If-Modified-Since", modified);
|
||||
}
|
||||
mRequest = builder.build();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
mCall = HTTPContext.getInstance().mClient.newCall(mRequest);
|
||||
mCall.enqueue(this);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
mCall.cancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Request request, IOException e) {
|
||||
int type = PERMANENT_ERROR;
|
||||
if ((e instanceof UnknownHostException) || (e instanceof SocketException) || (e instanceof ProtocolException) || (e instanceof SSLException)) {
|
||||
type = CONNECTION_ERROR;
|
||||
} else if ((e instanceof InterruptedIOException)) {
|
||||
type = TEMPORARY_ERROR;
|
||||
}
|
||||
nativeOnFailure(mNativePtr, type, e.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(Response response) throws IOException {
|
||||
byte[] body = response.body().bytes();
|
||||
response.body().close();
|
||||
|
||||
nativeOnResponse(mNativePtr, response.code(), response.message(), response.header("ETag"), response.header("Last-Modified"), response.header("Cache-Control"), response.header("Expires"), body);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
package com.mapbox.mapboxgl.util;
|
||||
|
||||
import com.mapbox.mapboxgl.constants.MapboxConstants;
|
||||
import com.squareup.okhttp.Callback;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
|
||||
public class JavaFileSource {
|
||||
|
||||
private static JavaFileSource instance = null;
|
||||
|
||||
// Single reference to OkHttp for performance gains
|
||||
private OkHttpClient client;
|
||||
|
||||
/**
|
||||
* Private Constructor to support Singleton pattern
|
||||
*/
|
||||
private JavaFileSource() {
|
||||
super();
|
||||
client = new OkHttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the singleton instance of JavaFileSource
|
||||
* @return Reference to the Singleton Instance of JavaFileSource
|
||||
*/
|
||||
public static JavaFileSource getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new JavaFileSource();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make an HTTP Request
|
||||
* @param resourceUrl URL to resource
|
||||
* @param callback Callback class
|
||||
*/
|
||||
public void request(final String resourceUrl, final Callback callback) {
|
||||
Request request = new Request.Builder().url(resourceUrl).tag(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE)).build();
|
||||
client.newCall(request).enqueue(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to cancel HTTP Request made
|
||||
* @param resourceUrl URL of request to cancel
|
||||
*/
|
||||
public void cancel(final String resourceUrl) {
|
||||
client.cancel(resourceUrl.toLowerCase(MapboxConstants.MAPBOX_LOCALE));
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,6 @@
|
||||
'<@(boost_cflags)',
|
||||
],
|
||||
'libraries': [
|
||||
'<@(openssl_static_libs)',
|
||||
'<@(libcurl_static_libs)',
|
||||
'<@(libpng_static_libs)',
|
||||
'<@(jpeg_static_libs)',
|
||||
'<@(sqlite_static_libs)',
|
||||
@@ -45,8 +43,6 @@
|
||||
'<@(libpng_ldflags)',
|
||||
'<@(jpeg_ldflags)',
|
||||
'<@(sqlite_ldflags)',
|
||||
'<@(openssl_ldflags)',
|
||||
'<@(libcurl_ldflags)',
|
||||
'<@(zlib_ldflags)',
|
||||
'<@(libzip_ldflags)',
|
||||
],
|
||||
@@ -74,7 +70,6 @@
|
||||
'copies': [
|
||||
{
|
||||
'files': [
|
||||
'../common/ca-bundle.crt',
|
||||
'../styles/styles'
|
||||
],
|
||||
'destination': '<(pwd)/../android/java/MapboxGLAndroidSDK/src/main/assets'
|
||||
|
||||
70
gyp/http-android.gypi
Normal file
70
gyp/http-android.gypi
Normal file
@@ -0,0 +1,70 @@
|
||||
{
|
||||
'targets': [
|
||||
{ 'target_name': 'http-android',
|
||||
'product_name': 'mbgl-http-android',
|
||||
'type': 'static_library',
|
||||
'standalone_static_library': 1,
|
||||
'hard_dependency': 1,
|
||||
|
||||
'sources': [
|
||||
'../platform/android/http_request_android.cpp',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
'../include',
|
||||
'../src',
|
||||
],
|
||||
|
||||
'variables': {
|
||||
'cflags_cc': [
|
||||
'<@(libuv_cflags)',
|
||||
'<@(boost_cflags)',
|
||||
],
|
||||
'ldflags': [
|
||||
'<@(libuv_ldflags)',
|
||||
],
|
||||
'libraries': [
|
||||
'<@(libuv_static_libs)',
|
||||
],
|
||||
'defines': [
|
||||
'-DMBGL_HTTP_ANDROID'
|
||||
],
|
||||
},
|
||||
|
||||
'conditions': [
|
||||
['OS == "mac"', {
|
||||
'xcode_settings': {
|
||||
'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
|
||||
},
|
||||
}, {
|
||||
'cflags_cc': [ '<@(cflags_cc)' ],
|
||||
}],
|
||||
],
|
||||
|
||||
'direct_dependent_settings': {
|
||||
'conditions': [
|
||||
['OS == "mac"', {
|
||||
'xcode_settings': {
|
||||
'OTHER_CFLAGS': [ '<@(defines)' ],
|
||||
'OTHER_CPLUSPLUSFLAGS': [ '<@(defines)' ],
|
||||
}
|
||||
}, {
|
||||
'cflags': [ '<@(defines)' ],
|
||||
'cflags_cc': [ '<@(defines)' ],
|
||||
}]
|
||||
],
|
||||
},
|
||||
|
||||
'link_settings': {
|
||||
'conditions': [
|
||||
['OS == "mac"', {
|
||||
'libraries': [ '<@(libraries)' ],
|
||||
'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
|
||||
}, {
|
||||
'libraries': [ '<@(libraries)', '<@(ldflags)' ],
|
||||
}]
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -2,15 +2,27 @@
|
||||
#define MBGL_ANDROID_JNI
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Forward definition of JNI types
|
||||
typedef class _jclass* jclass;
|
||||
typedef class _jstring* jstring;
|
||||
typedef class _jobject* jobject;
|
||||
typedef class _jlongArray* jlongArray;
|
||||
typedef struct _jmethodID* jmethodID;
|
||||
typedef struct _jfieldID* jfieldID;
|
||||
|
||||
struct _JavaVM;
|
||||
typedef _JavaVM JavaVM;
|
||||
|
||||
struct _JNIEnv;
|
||||
typedef _JNIEnv JNIEnv;
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
extern JavaVM* theJVM;
|
||||
|
||||
extern std::string cachePath;
|
||||
extern std::string dataPath;
|
||||
extern std::string apkPath;
|
||||
@@ -73,6 +85,23 @@ extern jmethodID pointFConstructorId;
|
||||
extern jfieldID pointFXId;
|
||||
extern jfieldID pointFYId;
|
||||
|
||||
extern jclass httpContextClass;
|
||||
extern jmethodID httpContextGetInstanceId;
|
||||
extern jmethodID httpContextCreateRequestId;
|
||||
|
||||
extern jclass httpRequestClass;
|
||||
extern jmethodID httpRequestStartId;
|
||||
extern jmethodID httpRequestCancelId;
|
||||
|
||||
extern bool throw_jni_error(JNIEnv *env, const char *msg);
|
||||
extern bool attach_jni_thread(JavaVM* vm, JNIEnv** env, std::string threadName);
|
||||
extern void detach_jni_thread(JavaVM* vm, JNIEnv** env, bool detach);
|
||||
extern std::string std_string_from_jstring(JNIEnv *env, jstring jstr);
|
||||
extern jstring std_string_to_jstring(JNIEnv *env, std::string str);
|
||||
extern std::vector<std::string> std_vector_string_from_jobject(JNIEnv *env, jobject jlist);
|
||||
extern jobject std_vector_string_to_jobject(JNIEnv *env, std::vector<std::string> vector);
|
||||
extern jlongArray std_vector_uint_to_jobject(JNIEnv *env, std::vector<uint32_t> vector);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
mbgl.gyp
1
mbgl.gyp
@@ -18,6 +18,7 @@
|
||||
['platform_lib == "android" and host == "android"', { 'includes': [ './gyp/platform-android.gypi' ] } ],
|
||||
['http_lib == "curl"', { 'includes': [ './gyp/http-curl.gypi' ] } ],
|
||||
['http_lib == "nsurl" and (host == "osx" or host == "ios")', { 'includes': [ './gyp/http-nsurl.gypi' ] } ],
|
||||
['http_lib == "android" and host == "android"', { 'includes': [ './gyp/http-android.gypi' ] } ],
|
||||
['asset_lib == "fs"', { 'includes': [ './gyp/asset-fs.gypi' ] } ],
|
||||
['asset_lib == "zip"', { 'includes': [ './gyp/asset-zip.gypi' ] } ],
|
||||
['cache_lib == "sqlite"', { 'includes': [ './gyp/cache-sqlite.gypi' ] } ],
|
||||
|
||||
399
platform/android/http_request_android.cpp
Normal file
399
platform/android/http_request_android.cpp
Normal file
@@ -0,0 +1,399 @@
|
||||
#include <mbgl/storage/http_context_base.hpp>
|
||||
#include <mbgl/storage/http_request_base.hpp>
|
||||
#include <mbgl/storage/resource.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/util/chrono.hpp>
|
||||
#include <mbgl/platform/log.hpp>
|
||||
#include <mbgl/android/jni.hpp>
|
||||
|
||||
#include <mbgl/util/time.hpp>
|
||||
#include <mbgl/util/util.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/parsedate.h>
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
void JNICALL nativeOnFailure(JNIEnv *env, jobject obj, jlong nativePtr, jint type, jstring message);
|
||||
void JNICALL nativeOnResponse(JNIEnv *env, jobject obj, jlong nativePtr, jint code, jstring message, jstring etag, jstring modified, jstring cacheControl, jstring expires, jbyteArray body);
|
||||
|
||||
class HTTPAndroidRequest;
|
||||
|
||||
class HTTPAndroidContext : public HTTPContextBase {
|
||||
public:
|
||||
explicit HTTPAndroidContext(uv_loop_t *loop);
|
||||
~HTTPAndroidContext();
|
||||
|
||||
HTTPRequestBase* createRequest(const Resource&,
|
||||
RequestBase::Callback,
|
||||
uv_loop_t*,
|
||||
std::shared_ptr<const Response>) final;
|
||||
|
||||
uv_loop_t *loop = nullptr;
|
||||
|
||||
JavaVM *vm = nullptr;
|
||||
jobject obj = nullptr;
|
||||
};
|
||||
|
||||
class HTTPAndroidRequest : public HTTPRequestBase {
|
||||
public:
|
||||
HTTPAndroidRequest(HTTPAndroidContext*,
|
||||
const Resource&,
|
||||
Callback,
|
||||
uv_loop_t*,
|
||||
std::shared_ptr<const Response>);
|
||||
~HTTPAndroidRequest();
|
||||
|
||||
void cancel() final;
|
||||
void retry() final;
|
||||
|
||||
void onFailure(int type, std::string message);
|
||||
void onResponse(int code, std::string message, std::string etag, std::string modified, std::string cacheControl, std::string expires, std::string body);
|
||||
|
||||
private:
|
||||
void retry(uint64_t timeout) final;
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
static void restart(uv_timer_t *timer, int);
|
||||
#else
|
||||
static void restart(uv_timer_t *timer);
|
||||
#endif
|
||||
void finish(ResponseStatus status);
|
||||
void start();
|
||||
|
||||
HTTPAndroidContext *context = nullptr;
|
||||
|
||||
bool cancelled = false;
|
||||
|
||||
std::unique_ptr<Response> response;
|
||||
const std::shared_ptr<const Response> existingResponse;
|
||||
|
||||
jobject obj = nullptr;
|
||||
|
||||
uv_timer_t *timer = nullptr;
|
||||
enum : bool { PreemptImmediately, ExponentialBackoff } strategy = PreemptImmediately;
|
||||
int attempts = 0;
|
||||
|
||||
static const int maxAttempts = 4;
|
||||
|
||||
static const int connectionError = 0;
|
||||
static const int temporaryError = 1;
|
||||
static const int permanentError = 1;
|
||||
};
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
HTTPAndroidContext::HTTPAndroidContext(uv_loop_t *loop_)
|
||||
: HTTPContextBase(loop_),
|
||||
loop(loop_),
|
||||
vm(mbgl::android::theJVM) {
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = mbgl::android::attach_jni_thread(vm, &env, "HTTPAndroidContext::HTTPAndroidContext()");
|
||||
|
||||
const std::vector<JNINativeMethod> methods = {
|
||||
{"nativeOnFailure", "(JILjava/lang/String;)V", reinterpret_cast<void *>(&nativeOnFailure)},
|
||||
{"nativeOnResponse",
|
||||
"(JILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[B)V",
|
||||
reinterpret_cast<void *>(&nativeOnResponse)}
|
||||
};
|
||||
|
||||
if (env->RegisterNatives(mbgl::android::httpRequestClass, methods.data(), methods.size()) < 0) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
obj = env->CallStaticObjectMethod(mbgl::android::httpContextClass, mbgl::android::httpContextGetInstanceId);
|
||||
if (env->ExceptionCheck() || (obj == nullptr)) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
obj = env->NewGlobalRef(obj);
|
||||
if (obj == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
mbgl::android::detach_jni_thread(vm, &env, detach);
|
||||
}
|
||||
|
||||
HTTPAndroidContext::~HTTPAndroidContext() {
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = mbgl::android::attach_jni_thread(vm, &env, "HTTPAndroidContext::~HTTPAndroidContext()");
|
||||
|
||||
env->DeleteGlobalRef(obj);
|
||||
obj = nullptr;
|
||||
|
||||
mbgl::android::detach_jni_thread(vm, &env, detach);
|
||||
|
||||
vm = nullptr;
|
||||
}
|
||||
|
||||
HTTPRequestBase* HTTPAndroidContext::createRequest(const Resource& resource,
|
||||
RequestBase::Callback callback,
|
||||
uv_loop_t* loop_,
|
||||
std::shared_ptr<const Response> response) {
|
||||
return new HTTPAndroidRequest(this, resource, callback, loop_, response);
|
||||
}
|
||||
|
||||
HTTPAndroidRequest::HTTPAndroidRequest(HTTPAndroidContext* context_, const Resource& resource_, Callback callback_, uv_loop_t*, std::shared_ptr<const Response> response_)
|
||||
: HTTPRequestBase(resource_, callback_),
|
||||
context(context_),
|
||||
existingResponse(response_) {
|
||||
|
||||
std::string etagStr;
|
||||
std::string modifiedStr;
|
||||
if (existingResponse) {
|
||||
if (!existingResponse->etag.empty()) {
|
||||
etagStr = existingResponse->etag;
|
||||
} else if (existingResponse->modified) {
|
||||
modifiedStr = util::rfc1123(existingResponse->modified);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = mbgl::android::attach_jni_thread(context->vm, &env, "HTTPAndroidContext::HTTPAndroidRequest()");
|
||||
|
||||
jstring resourceUrl = mbgl::android::std_string_to_jstring(env, resource.url);
|
||||
jstring userAgent = mbgl::android::std_string_to_jstring(env, "MapboxGL/1.0");
|
||||
jstring etag = mbgl::android::std_string_to_jstring(env, etagStr);
|
||||
jstring modified = mbgl::android::std_string_to_jstring(env, modifiedStr);
|
||||
obj = env->CallObjectMethod(context->obj, mbgl::android::httpContextCreateRequestId, reinterpret_cast<jlong>(this), resourceUrl, userAgent, etag, modified);
|
||||
if (env->ExceptionCheck() || (obj == nullptr)) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
obj = env->NewGlobalRef(obj);
|
||||
if (obj == nullptr) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
mbgl::android::detach_jni_thread(context->vm, &env, detach);
|
||||
|
||||
context->addRequest(this);
|
||||
start();
|
||||
}
|
||||
|
||||
HTTPAndroidRequest::~HTTPAndroidRequest() {
|
||||
context->removeRequest(this);
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = mbgl::android::attach_jni_thread(context->vm, &env, "HTTPAndroidContext::~HTTPAndroidRequest()");
|
||||
|
||||
env->DeleteGlobalRef(obj);
|
||||
obj = nullptr;
|
||||
|
||||
mbgl::android::detach_jni_thread(context->vm, &env, detach);
|
||||
|
||||
if (timer) {
|
||||
uv_timer_stop(timer);
|
||||
uv::close(timer);
|
||||
timer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPAndroidRequest::cancel() {
|
||||
cancelled = true;
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = mbgl::android::attach_jni_thread(context->vm, &env, "HTTPAndroidContext::cancel()");
|
||||
|
||||
env->CallVoidMethod(obj, mbgl::android::httpRequestCancelId);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
mbgl::android::detach_jni_thread(context->vm, &env, detach);
|
||||
}
|
||||
|
||||
void HTTPAndroidRequest::start() {
|
||||
attempts++;
|
||||
|
||||
JNIEnv *env = nullptr;
|
||||
bool detach = mbgl::android::attach_jni_thread(context->vm, &env, "HTTPAndroidContext::start()");
|
||||
|
||||
env->CallVoidMethod(obj, mbgl::android::httpRequestStartId);
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
|
||||
mbgl::android::detach_jni_thread(context->vm, &env, detach);
|
||||
}
|
||||
|
||||
void HTTPAndroidRequest::retry(uint64_t timeout) {
|
||||
response.reset();
|
||||
|
||||
assert(!timer);
|
||||
timer = new uv_timer_t;
|
||||
timer->data = this;
|
||||
uv_timer_init(context->loop, timer);
|
||||
uv_timer_start(timer, restart, timeout, 0);
|
||||
}
|
||||
|
||||
void HTTPAndroidRequest::retry() {
|
||||
if (timer && strategy == PreemptImmediately) {
|
||||
uv_timer_stop(timer);
|
||||
uv_timer_start(timer, restart, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
#if UV_VERSION_MAJOR == 0 && UV_VERSION_MINOR <= 10
|
||||
void HTTPAndroidRequest::restart(uv_timer_t *timer, int) {
|
||||
#else
|
||||
void HTTPAndroidRequest::restart(uv_timer_t *timer) {
|
||||
#endif
|
||||
auto baton = reinterpret_cast<HTTPAndroidRequest *>(timer->data);
|
||||
|
||||
baton->timer = nullptr;
|
||||
uv::close(timer);
|
||||
|
||||
baton->start();
|
||||
}
|
||||
|
||||
void HTTPAndroidRequest::finish(ResponseStatus status) {
|
||||
if (status == ResponseStatus::TemporaryError && attempts < maxAttempts) {
|
||||
strategy = ExponentialBackoff;
|
||||
return retry((1 << (attempts - 1)) * 1000);
|
||||
} else if (status == ResponseStatus::ConnectionError && attempts < maxAttempts) {
|
||||
strategy = PreemptImmediately;
|
||||
return retry(30000);
|
||||
}
|
||||
|
||||
if (status == ResponseStatus::NotModified) {
|
||||
notify(std::move(response), FileCache::Hint::Refresh);
|
||||
} else {
|
||||
notify(std::move(response), FileCache::Hint::Full);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
int64_t parseCacheControl(const char *value) {
|
||||
if (value) {
|
||||
unsigned long long seconds = 0;
|
||||
// TODO: cache-control may contain other information as well:
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||
if (std::sscanf(value, "max-age=%llu", &seconds) == 1) {
|
||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count() +
|
||||
seconds;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HTTPAndroidRequest::onResponse(int code, std::string message, std::string etag, std::string modified, std::string cacheControl, std::string expires, std::string body) {
|
||||
if (cancelled) {
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
response = std::make_unique<Response>();
|
||||
}
|
||||
|
||||
response->message = message;
|
||||
response->modified = parse_date(modified.c_str());
|
||||
response->etag = etag;
|
||||
response->expires = parseCacheControl(cacheControl.c_str());
|
||||
if (!expires.empty()) {
|
||||
response->expires = parse_date(expires.c_str());
|
||||
}
|
||||
response->data = body;
|
||||
|
||||
if (code == 304) {
|
||||
if (existingResponse) {
|
||||
response->status = existingResponse->status;
|
||||
response->message = existingResponse->message;
|
||||
response->modified = existingResponse->modified;
|
||||
response->etag = existingResponse->etag;
|
||||
response->data = existingResponse->data;
|
||||
return finish(ResponseStatus::NotModified);
|
||||
} else {
|
||||
response->status = Response::Successful;
|
||||
return finish(ResponseStatus::Successful);
|
||||
}
|
||||
} else if (code == 200) {
|
||||
response->status = Response::Successful;
|
||||
return finish(ResponseStatus::Successful);
|
||||
} else if (code >= 500 && code < 600) {
|
||||
response->status = Response::Error;
|
||||
response->message = "HTTP status code " + util::toString(code);
|
||||
return finish(ResponseStatus::TemporaryError);
|
||||
} else {
|
||||
response->status = Response::Error;
|
||||
response->message = "HTTP status code " + util::toString(code);
|
||||
return finish(ResponseStatus::PermanentError);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Response hasn't been handled");
|
||||
}
|
||||
|
||||
void HTTPAndroidRequest::onFailure(int type, std::string message) {
|
||||
if (cancelled) {
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!response) {
|
||||
response = std::make_unique<Response>();
|
||||
}
|
||||
|
||||
response->status = Response::Error;
|
||||
response->message = message;
|
||||
|
||||
switch (type) {
|
||||
case connectionError:
|
||||
return finish(ResponseStatus::ConnectionError);
|
||||
|
||||
case temporaryError:
|
||||
return finish(ResponseStatus::TemporaryError);
|
||||
|
||||
default:
|
||||
return finish(ResponseStatus::PermanentError);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Response hasn't been handled");
|
||||
}
|
||||
|
||||
std::unique_ptr<HTTPContextBase> HTTPContextBase::createContext(uv_loop_t* loop) {
|
||||
return std::make_unique<HTTPAndroidContext>(loop);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic ignored "-Wunused-parameter"
|
||||
|
||||
void JNICALL nativeOnFailure(JNIEnv *env, jobject obj, jlong nativePtr, jint type, jstring message) {
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "nativeOnFailure");
|
||||
assert(nativePtr != 0);
|
||||
HTTPAndroidRequest *request = reinterpret_cast<HTTPAndroidRequest *>(nativePtr);
|
||||
std::string messageStr = mbgl::android::std_string_from_jstring(env, message);
|
||||
return request->onFailure(type, messageStr);
|
||||
}
|
||||
|
||||
void JNICALL nativeOnResponse(JNIEnv *env, jobject obj, jlong nativePtr, jint code, jstring message, jstring etag, jstring modified, jstring cacheControl, jstring expires, jbyteArray body) {
|
||||
mbgl::Log::Debug(mbgl::Event::JNI, "nativeOnResponse");
|
||||
assert(nativePtr != 0);
|
||||
HTTPAndroidRequest *request = reinterpret_cast<HTTPAndroidRequest *>(nativePtr);
|
||||
std::string messageStr = mbgl::android::std_string_from_jstring(env, message);
|
||||
std::string etagStr;
|
||||
if (etag != nullptr) {
|
||||
etagStr = mbgl::android::std_string_from_jstring(env, etag);
|
||||
}
|
||||
std::string modifiedStr;
|
||||
if (modified != nullptr) {
|
||||
modifiedStr = mbgl::android::std_string_from_jstring(env, modified);
|
||||
}
|
||||
std::string cacheControlStr;
|
||||
if (cacheControl != nullptr) {
|
||||
cacheControlStr = mbgl::android::std_string_from_jstring(env, cacheControl);
|
||||
}
|
||||
std::string expiresStr;
|
||||
if (expires != nullptr) {
|
||||
expiresStr = mbgl::android::std_string_from_jstring(env, expires);
|
||||
}
|
||||
jbyte* bodyData = env->GetByteArrayElements(body, nullptr);
|
||||
std::string bodyStr(reinterpret_cast<char*>(bodyData), env->GetArrayLength(body));
|
||||
env->ReleaseByteArrayElements(body, bodyData, JNI_ABORT);
|
||||
return request->onResponse(code, messageStr, etagStr, modifiedStr, cacheControlStr, expiresStr, bodyStr);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -11,12 +11,6 @@
|
||||
|
||||
#include <curl/curl.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <mbgl/android/jni.hpp>
|
||||
#include <zip.h>
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
|
||||
#include <queue>
|
||||
#include <map>
|
||||
#include <cassert>
|
||||
@@ -331,104 +325,6 @@ int HTTPCURLContext::startTimeout(CURLM * /* multi */, long timeout_ms, void *us
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
#ifdef __ANDROID__
|
||||
|
||||
// This function is called to load the CA bundle
|
||||
// from http://curl.haxx.se/libcurl/c/cacertinmem.html¯
|
||||
static CURLcode sslctx_function(CURL * /* curl */, void *sslctx, void * /* parm */) {
|
||||
|
||||
int error = 0;
|
||||
struct zip *apk = zip_open(mbgl::android::apkPath.c_str(), 0, &error);
|
||||
if (apk == nullptr) {
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
struct zip_file *apkFile = zip_fopen(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE);
|
||||
if (apkFile == nullptr) {
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
struct zip_stat stat;
|
||||
if (zip_stat(apk, "assets/ca-bundle.crt", ZIP_FL_NOCASE, &stat) != 0) {
|
||||
zip_fclose(apkFile);
|
||||
apkFile = nullptr;
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
if (stat.size > std::numeric_limits<int>::max()) {
|
||||
zip_fclose(apkFile);
|
||||
apkFile = nullptr;
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
const auto pem = std::make_unique<char[]>(stat.size);
|
||||
|
||||
if (static_cast<zip_uint64_t>(zip_fread(apkFile, reinterpret_cast<void *>(pem.get()), stat.size)) != stat.size) {
|
||||
zip_fclose(apkFile);
|
||||
apkFile = nullptr;
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
// get a pointer to the X509 certificate store (which may be empty!)
|
||||
X509_STORE *store = SSL_CTX_get_cert_store((SSL_CTX *)sslctx);
|
||||
if (store == nullptr) {
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
// get a BIO
|
||||
BIO *bio = BIO_new_mem_buf(pem.get(), static_cast<int>(stat.size));
|
||||
if (bio == nullptr) {
|
||||
store = nullptr;
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
// use it to read the PEM formatted certificate from memory into an X509
|
||||
// structure that SSL can use
|
||||
X509 *cert = nullptr;
|
||||
while (PEM_read_bio_X509(bio, &cert, 0, nullptr) != nullptr) {
|
||||
if (cert == nullptr) {
|
||||
BIO_free(bio);
|
||||
bio = nullptr;
|
||||
store = nullptr;
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
// add our certificate to this store
|
||||
if (X509_STORE_add_cert(store, cert) == 0) {
|
||||
X509_free(cert);
|
||||
cert = nullptr;
|
||||
BIO_free(bio);
|
||||
bio = nullptr;
|
||||
store = nullptr;
|
||||
return CURLE_SSL_CACERT_BADFILE;
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
cert = nullptr;
|
||||
}
|
||||
|
||||
// decrease reference counts
|
||||
BIO_free(bio);
|
||||
bio = nullptr;
|
||||
|
||||
zip_fclose(apkFile);
|
||||
apkFile = nullptr;
|
||||
zip_close(apk);
|
||||
apk = nullptr;
|
||||
|
||||
// all set to go
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& resource_, Callback callback_, uv_loop_t*, std::shared_ptr<const Response> response_)
|
||||
: HTTPRequestBase(resource_, callback_),
|
||||
context(context_),
|
||||
@@ -458,12 +354,7 @@ HTTPCURLRequest::HTTPCURLRequest(HTTPCURLContext* context_, const Resource& reso
|
||||
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_PRIVATE, this));
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error));
|
||||
#ifdef __ANDROID__
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_SSLCERTTYPE, "PEM"));
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_SSL_CTX_FUNCTION, sslctx_function));
|
||||
#else
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_CAINFO, "ca-bundle.crt"));
|
||||
#endif
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1));
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_URL, resource.url.c_str()));
|
||||
handleError(curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, writeCallback));
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BOOST_VERSION=1.57.0
|
||||
LIBCURL_VERSION=7.40.0
|
||||
OPENSSL_VERSION=1.0.1l
|
||||
LIBPNG_VERSION=1.6.16
|
||||
JPEG_VERSION=v9a
|
||||
SQLITE_VERSION=3.8.8.1
|
||||
@@ -11,4 +9,4 @@ ZLIB_VERSION=system
|
||||
NUNICODE_VERSION=1.5.1
|
||||
LIBZIP_VERSION=0.11.2
|
||||
|
||||
export MASON_ANDROID_ABI=${MASON_PLATFORM_VERSION}
|
||||
export MASON_ANDROID_ABI=${MASON_PLATFORM_VERSION}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
HEADLESS ?= none
|
||||
PLATFORM ?= android
|
||||
ASSET ?= zip
|
||||
HTTP ?= curl
|
||||
HTTP ?= android
|
||||
CACHE ?= sqlite
|
||||
|
||||
GYP_FLAVOR_SUFFIX=-android
|
||||
|
||||
Reference in New Issue
Block a user