diff options
Diffstat (limited to 'services/jni')
-rw-r--r-- | services/jni/Android.mk | 1 | ||||
-rw-r--r-- | services/jni/com_android_server_InputManager.cpp | 254 | ||||
-rw-r--r-- | services/jni/com_android_server_connectivity_Vpn.cpp | 450 | ||||
-rw-r--r-- | services/jni/onload.cpp | 2 |
4 files changed, 589 insertions, 118 deletions
diff --git a/services/jni/Android.mk b/services/jni/Android.mk index a1c3283..f33920d 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -16,6 +16,7 @@ LOCAL_SRC_FILES:= \ com_android_server_UsbHostManager.cpp \ com_android_server_VibratorService.cpp \ com_android_server_location_GpsLocationProvider.cpp \ + com_android_server_connectivity_Vpn.cpp \ onload.cpp LOCAL_C_INCLUDES += \ diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 1f10d9c..7c5084f 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -53,6 +53,11 @@ namespace android { +// The exponent used to calculate the pointer speed scaling factor. +// The scaling factor is calculated as 2 ^ (speed * exponent), +// where the speed ranges from -7 to + 7 and is supplied by the user. +static const float POINTER_SPEED_EXPONENT = 1.0f / 4; + static struct { jmethodID notifyConfigurationChanged; jmethodID notifyLidSwitchChanged; @@ -71,6 +76,10 @@ static struct { jmethodID getKeyRepeatTimeout; jmethodID getKeyRepeatDelay; jmethodID getMaxEventsPerSecond; + jmethodID getHoverTapTimeout; + jmethodID getHoverTapSlop; + jmethodID getDoubleTapTimeout; + jmethodID getLongPressTimeout; jmethodID getPointerLayer; jmethodID getPointerIcon; } gCallbacksClassInfo; @@ -104,6 +113,16 @@ static struct { // --- Global functions --- +template<typename T> +inline static T min(const T& a, const T& b) { + return a < b ? a : b; +} + +template<typename T> +inline static T max(const T& a, const T& b) { + return a > b ? a : b; +} + static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env, const sp<InputApplicationHandle>& inputApplicationHandle) { if (inputApplicationHandle == NULL) { @@ -162,15 +181,13 @@ public: void setFocusedApplication(JNIEnv* env, jobject applicationObj); void setInputDispatchMode(bool enabled, bool frozen); void setSystemUiVisibility(int32_t visibility); + void setPointerSpeed(int32_t speed); /* --- InputReaderPolicyInterface implementation --- */ virtual bool getDisplayInfo(int32_t displayId, int32_t* width, int32_t* height, int32_t* orientation); - virtual bool filterTouchEvents(); - virtual bool filterJumpyTouchEvents(); - virtual nsecs_t getVirtualKeyQuietTime(); - virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames); + virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -181,10 +198,9 @@ public: virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, const sp<InputWindowHandle>& inputWindowHandle); virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle); - virtual nsecs_t getKeyRepeatTimeout(); - virtual nsecs_t getKeyRepeatDelay(); - virtual int32_t getMaxEventsPerSecond(); virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags); + virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig); + virtual bool isKeyRepeatEnabled(); virtual void interceptKeyBeforeQueueing(const KeyEvent* keyEvent, uint32_t& policyFlags); virtual void interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags); virtual bool interceptKeyBeforeDispatching(const sp<InputWindowHandle>& inputWindowHandle, @@ -206,18 +222,6 @@ private: jobject mCallbacksObj; sp<Looper> mLooper; - // Cached filtering policies. - int32_t mFilterTouchEvents; - int32_t mFilterJumpyTouchEvents; - nsecs_t mVirtualKeyQuietTime; - - // Cached key repeat policy. - nsecs_t mKeyRepeatTimeout; - nsecs_t mKeyRepeatDelay; - - // Cached throttling policy. - int32_t mMaxEventsPerSecond; - Mutex mLock; struct Locked { // Display size information. @@ -227,6 +231,9 @@ private: // System UI visibility. int32_t systemUiVisibility; + // Pointer speed. + int32_t pointerSpeed; + // Sprite controller singleton, created on first use. sp<SpriteController> spriteController; @@ -253,10 +260,7 @@ private: NativeInputManager::NativeInputManager(jobject contextObj, jobject callbacksObj, const sp<Looper>& looper) : - mLooper(looper), - mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1), - mKeyRepeatTimeout(-1), mKeyRepeatDelay(-1), - mMaxEventsPerSecond(-1) { + mLooper(looper) { JNIEnv* env = jniEnv(); mContextObj = env->NewGlobalRef(contextObj); @@ -269,6 +273,7 @@ NativeInputManager::NativeInputManager(jobject contextObj, mLocked.displayOrientation = ROTATION_0; mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; + mLocked.pointerSpeed = 0; } sp<EventHub> eventHub = new EventHub(); @@ -369,74 +374,76 @@ bool NativeInputManager::getDisplayInfo(int32_t displayId, return result; } -bool NativeInputManager::filterTouchEvents() { - if (mFilterTouchEvents < 0) { - JNIEnv* env = jniEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacksObj, - gCallbacksClassInfo.filterTouchEvents); - if (checkAndClearExceptionFromCallback(env, "filterTouchEvents")) { - result = false; - } +void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) { + JNIEnv* env = jniEnv(); - mFilterTouchEvents = result ? 1 : 0; + jboolean filterTouchEvents = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.filterTouchEvents); + if (!checkAndClearExceptionFromCallback(env, "filterTouchEvents")) { + outConfig->filterTouchEvents = filterTouchEvents; } - return mFilterTouchEvents; -} - -bool NativeInputManager::filterJumpyTouchEvents() { - if (mFilterJumpyTouchEvents < 0) { - JNIEnv* env = jniEnv(); - - jboolean result = env->CallBooleanMethod(mCallbacksObj, - gCallbacksClassInfo.filterJumpyTouchEvents); - if (checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) { - result = false; - } - mFilterJumpyTouchEvents = result ? 1 : 0; + jboolean filterJumpyTouchEvents = env->CallBooleanMethod(mCallbacksObj, + gCallbacksClassInfo.filterJumpyTouchEvents); + if (!checkAndClearExceptionFromCallback(env, "filterJumpyTouchEvents")) { + outConfig->filterJumpyTouchEvents = filterJumpyTouchEvents; } - return mFilterJumpyTouchEvents; -} - -nsecs_t NativeInputManager::getVirtualKeyQuietTime() { - if (mVirtualKeyQuietTime < 0) { - JNIEnv* env = jniEnv(); - jint result = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getVirtualKeyQuietTimeMillis); - if (checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) { - result = 0; - } - if (result < 0) { - result = 0; - } - - mVirtualKeyQuietTime = milliseconds_to_nanoseconds(result); + jint virtualKeyQuietTime = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getVirtualKeyQuietTimeMillis); + if (!checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) { + outConfig->virtualKeyQuietTime = milliseconds_to_nanoseconds(virtualKeyQuietTime); } - return mVirtualKeyQuietTime; -} -void NativeInputManager::getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames) { - outExcludedDeviceNames.clear(); - - JNIEnv* env = jniEnv(); - - jobjectArray result = jobjectArray(env->CallObjectMethod(mCallbacksObj, + outConfig->excludedDeviceNames.clear(); + jobjectArray excludedDeviceNames = jobjectArray(env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getExcludedDeviceNames)); - if (! checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && result) { - jsize length = env->GetArrayLength(result); + if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) { + jsize length = env->GetArrayLength(excludedDeviceNames); for (jsize i = 0; i < length; i++) { - jstring item = jstring(env->GetObjectArrayElement(result, i)); - + jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i)); const char* deviceNameChars = env->GetStringUTFChars(item, NULL); - outExcludedDeviceNames.add(String8(deviceNameChars)); + outConfig->excludedDeviceNames.add(String8(deviceNameChars)); env->ReleaseStringUTFChars(item, deviceNameChars); - env->DeleteLocalRef(item); } - env->DeleteLocalRef(result); + env->DeleteLocalRef(excludedDeviceNames); + } + + jint hoverTapTimeout = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getHoverTapTimeout); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) { + jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getDoubleTapTimeout); + if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) { + jint longPressTimeout = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getLongPressTimeout); + if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) { + outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout); + + // We must ensure that the tap-drag interval is significantly shorter than + // the long-press timeout because the tap is held down for the entire duration + // of the double-tap timeout. + jint tapDragInterval = max(min(longPressTimeout - 100, + doubleTapTimeout), hoverTapTimeout); + outConfig->pointerGestureTapDragInterval = + milliseconds_to_nanoseconds(tapDragInterval); + } + } + } + + jint hoverTapSlop = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getHoverTapSlop); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) { + outConfig->pointerGestureTapSlop = hoverTapSlop; } + + { // acquire lock + AutoMutex _l(mLock); + + outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed + * POINTER_SPEED_EXPONENT); + } // release lock } sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t deviceId) { @@ -557,54 +564,31 @@ void NativeInputManager::notifyInputChannelBroken(const sp<InputWindowHandle>& i } } -nsecs_t NativeInputManager::getKeyRepeatTimeout() { - if (! isScreenOn()) { - // Disable key repeat when the screen is off. - return -1; - } else { - if (mKeyRepeatTimeout < 0) { - JNIEnv* env = jniEnv(); - - jint result = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getKeyRepeatTimeout); - if (checkAndClearExceptionFromCallback(env, "getKeyRepeatTimeout")) { - result = 500; - } +void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) { + JNIEnv* env = jniEnv(); - mKeyRepeatTimeout = milliseconds_to_nanoseconds(result); - } - return mKeyRepeatTimeout; + jint keyRepeatTimeout = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getKeyRepeatTimeout); + if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatTimeout")) { + outConfig->keyRepeatTimeout = milliseconds_to_nanoseconds(keyRepeatTimeout); } -} -nsecs_t NativeInputManager::getKeyRepeatDelay() { - if (mKeyRepeatDelay < 0) { - JNIEnv* env = jniEnv(); - - jint result = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getKeyRepeatDelay); - if (checkAndClearExceptionFromCallback(env, "getKeyRepeatDelay")) { - result = 50; - } + jint keyRepeatDelay = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getKeyRepeatDelay); + if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatDelay")) { + outConfig->keyRepeatDelay = milliseconds_to_nanoseconds(keyRepeatDelay); + } - mKeyRepeatDelay = milliseconds_to_nanoseconds(result); + jint maxEventsPerSecond = env->CallIntMethod(mCallbacksObj, + gCallbacksClassInfo.getMaxEventsPerSecond); + if (!checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) { + outConfig->maxEventsPerSecond = maxEventsPerSecond; } - return mKeyRepeatDelay; } -int32_t NativeInputManager::getMaxEventsPerSecond() { - if (mMaxEventsPerSecond < 0) { - JNIEnv* env = jniEnv(); - - jint result = env->CallIntMethod(mCallbacksObj, - gCallbacksClassInfo.getMaxEventsPerSecond); - if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) { - result = 60; - } - - mMaxEventsPerSecond = result; - } - return mMaxEventsPerSecond; +bool NativeInputManager::isKeyRepeatEnabled() { + // Only enable automatic key repeating when the screen is on. + return isScreenOn(); } void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) { @@ -665,6 +649,17 @@ void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerControlle : PointerController::INACTIVITY_TIMEOUT_NORMAL); } +void NativeInputManager::setPointerSpeed(int32_t speed) { + AutoMutex _l(mLock); + + if (mLocked.pointerSpeed != speed) { + LOGI("Setting pointer speed to %d.", speed); + mLocked.pointerSpeed = speed; + + mInputManager->getReader()->refreshConfiguration(); + } +} + bool NativeInputManager::isScreenOn() { return android_server_PowerManagerService_isScreenOn(); } @@ -1254,6 +1249,15 @@ static jboolean android_server_InputManager_nativeTransferTouchFocus(JNIEnv* env transferTouchFocus(fromChannel, toChannel); } +static void android_server_InputManager_nativeSetPointerSpeed(JNIEnv* env, + jclass clazz, jint speed) { + if (checkInputManagerUnitialized(env)) { + return; + } + + gNativeInputManager->setPointerSpeed(speed); +} + static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) { if (checkInputManagerUnitialized(env)) { return NULL; @@ -1310,6 +1314,8 @@ static JNINativeMethod gInputManagerMethods[] = { (void*) android_server_InputManager_nativeGetInputConfiguration }, { "nativeTransferTouchFocus", "(Landroid/view/InputChannel;Landroid/view/InputChannel;)Z", (void*) android_server_InputManager_nativeTransferTouchFocus }, + { "nativeSetPointerSpeed", "(I)V", + (void*) android_server_InputManager_nativeSetPointerSpeed }, { "nativeDump", "()Ljava/lang/String;", (void*) android_server_InputManager_nativeDump }, }; @@ -1388,6 +1394,18 @@ int register_android_server_InputManager(JNIEnv* env) { GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz, "getKeyRepeatDelay", "()I"); + GET_METHOD_ID(gCallbacksClassInfo.getHoverTapTimeout, clazz, + "getHoverTapTimeout", "()I"); + + GET_METHOD_ID(gCallbacksClassInfo.getHoverTapSlop, clazz, + "getHoverTapSlop", "()I"); + + GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, clazz, + "getDoubleTapTimeout", "()I"); + + GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, clazz, + "getLongPressTimeout", "()I"); + GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz, "getMaxEventsPerSecond", "()I"); diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp new file mode 100644 index 0000000..374fd3b --- /dev/null +++ b/services/jni/com_android_server_connectivity_Vpn.cpp @@ -0,0 +1,450 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_NDEBUG 0 + +#define LOG_TAG "VpnJni" +#include <cutils/log.h> +#include <cutils/properties.h> + +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <fcntl.h> + +#include <linux/if.h> +#include <linux/if_tun.h> +#include <linux/route.h> +#include <linux/ipv6_route.h> + +#include "jni.h" +#include "JNIHelp.h" +#include "android_util_Binder.h" + +namespace android +{ + +static inline void init_sockaddr(sockaddr *sa) { + ((sockaddr_in *)sa)->sin_family = AF_INET; + ((sockaddr_in *)sa)->sin_port = 0; +} + +static inline in_addr_t *as_in_addr(sockaddr *sa) { + return &((sockaddr_in *)sa)->sin_addr.s_addr; +} + +static inline in_addr_t *as_in_addr(sockaddr_storage *ss) { + return &((sockaddr_in *)ss)->sin_addr.s_addr; +} + +static inline in6_addr *as_in6_addr(sockaddr_storage *ss) { + return &((sockaddr_in6 *)&ss)->sin6_addr; +} + +//------------------------------------------------------------------------------ + +#define SYSTEM_ERROR -1 +#define BAD_ARGUMENT -2 + +static int create_interface(char *name, int *index) +{ + int tun = open("/dev/tun", O_RDWR); + int inet4 = socket(AF_INET, SOCK_DGRAM, 0); + + ifreq ifr4; + memset(&ifr4, 0, sizeof(ifr4)); + + // Allocate interface. + ifr4.ifr_flags = IFF_TUN; + if (ioctl(tun, TUNSETIFF, &ifr4)) { + LOGE("Cannot allocate TUN: %s", strerror(errno)); + goto error; + } + + // Activate interface. + ifr4.ifr_flags = IFF_UP; + if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) { + LOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno)); + goto error; + } + + // Get interface index. + if (ioctl(inet4, SIOGIFINDEX, &ifr4)) { + LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno)); + goto error; + } + + strcpy(name, ifr4.ifr_name); + *index = ifr4.ifr_ifindex; + close(inet4); + return tun; + +error: + close(tun); + close(inet4); + return SYSTEM_ERROR; +} + +static int set_addresses(const char *name, int index, const char *addresses) +{ + int inet4 = socket(AF_INET, SOCK_DGRAM, 0); + int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + + ifreq ifr4; + memset(&ifr4, 0, sizeof(ifr4)); + strcpy(ifr4.ifr_name, name); + init_sockaddr(&ifr4.ifr_addr); + + in6_ifreq ifr6; + memset(&ifr6, 0, sizeof(ifr6)); + ifr6.ifr6_ifindex = index; + + char address[65]; + int prefix; + + int chars; + int count = 0; + + while (sscanf(addresses, " %64[^/]/%d %n", address, &prefix, &chars) == 2) { + addresses += chars; + + if (strchr(address, ':')) { + // Add an IPv6 address. + if (inet_pton(AF_INET6, address, &ifr6.ifr6_addr) != 1 || + prefix < 0 || prefix > 128) { + count = BAD_ARGUMENT; + break; + } + + ifr6.ifr6_prefixlen = prefix; + if (ioctl(inet6, SIOCSIFADDR, &ifr6)) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + } else { + // Add an IPv4 address. + if (inet_pton(AF_INET, address, as_in_addr(&ifr4.ifr_addr)) != 1 || + prefix < 0 || prefix > 32) { + count = BAD_ARGUMENT; + break; + } + + if (count) { + sprintf(ifr4.ifr_name, "%s:%d", name, count); + } + if (ioctl(inet4, SIOCSIFADDR, &ifr4)) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + + in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0; + *as_in_addr(&ifr4.ifr_addr) = htonl(mask); + if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + } + LOGV("Address added on %s: %s/%d", name, address, prefix); + ++count; + } + + if (count == BAD_ARGUMENT) { + LOGE("Invalid address: %s/%d", address, prefix); + } else if (count == SYSTEM_ERROR) { + LOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno)); + } else if (*addresses) { + LOGE("Invalid address: %s", addresses); + count = BAD_ARGUMENT; + } + + close(inet4); + close(inet6); + return count; +} + +static int set_routes(const char *name, int index, const char *routes) +{ + int inet4 = socket(AF_INET, SOCK_DGRAM, 0); + int inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + + rtentry rt4; + memset(&rt4, 0, sizeof(rt4)); + rt4.rt_dev = (char *)name; + rt4.rt_flags = RTF_UP; + init_sockaddr(&rt4.rt_dst); + init_sockaddr(&rt4.rt_genmask); + init_sockaddr(&rt4.rt_gateway); + + in6_rtmsg rt6; + memset(&rt6, 0, sizeof(rt6)); + rt6.rtmsg_ifindex = index; + rt6.rtmsg_flags = RTF_UP; + + char address[65]; + int prefix; + char gateway[65]; + + int chars; + int count = 0; + + while (sscanf(routes, " %64[^/]/%d>%64[^ ] %n", + address, &prefix, gateway, &chars) == 3) { + routes += chars; + + if (strchr(address, ':')) { + // Add an IPv6 route. + if (inet_pton(AF_INET6, gateway, &rt6.rtmsg_gateway) != 1 || + inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 || + prefix < 0 || prefix > 128) { + count = BAD_ARGUMENT; + break; + } + + rt6.rtmsg_dst_len = prefix; + if (memcmp(&rt6.rtmsg_gateway, &in6addr_any, sizeof(in6addr_any))) { + rt6.rtmsg_flags |= RTF_GATEWAY; + } + if (ioctl(inet6, SIOCADDRT, &rt6)) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + } else { + // Add an IPv4 route. + if (inet_pton(AF_INET, gateway, as_in_addr(&rt4.rt_gateway)) != 1 || + inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 || + prefix < 0 || prefix > 32) { + count = BAD_ARGUMENT; + break; + } + + in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0; + *as_in_addr(&rt4.rt_genmask) = htonl(mask); + if (*as_in_addr(&rt4.rt_gateway)) { + rt4.rt_flags |= RTF_GATEWAY; + } + if (ioctl(inet4, SIOCADDRT, &rt4)) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + } + LOGV("Route added on %s: %s/%d -> %s", name, address, prefix, gateway); + ++count; + } + + if (count == BAD_ARGUMENT) { + LOGE("Invalid route: %s/%d -> %s", address, prefix, gateway); + } else if (count == SYSTEM_ERROR) { + LOGE("Cannot add route: %s/%d -> %s: %s", + address, prefix, gateway, strerror(errno)); + } else if (*routes) { + LOGE("Invalid route: %s", routes); + count = BAD_ARGUMENT; + } + + close(inet4); + close(inet6); + return count; +} + +static int get_interface_name(char *name, int tun) +{ + ifreq ifr4; + if (ioctl(tun, TUNGETIFF, &ifr4)) { + LOGE("Cannot get interface name: %s", strerror(errno)); + return SYSTEM_ERROR; + } + strcpy(name, ifr4.ifr_name); + return 0; +} + +static int reset_interface(const char *name) +{ + int inet4 = socket(AF_INET, SOCK_DGRAM, 0); + + ifreq ifr4; + ifr4.ifr_flags = 0; + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + + if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) { + LOGE("Cannot reset %s: %s", name, strerror(errno)); + close(inet4); + return SYSTEM_ERROR; + } + close(inet4); + return 0; +} + +static int check_interface(const char *name) +{ + int inet4 = socket(AF_INET, SOCK_DGRAM, 0); + + ifreq ifr4; + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + + if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) { + LOGE("Cannot check %s: %s", name, strerror(errno)); + ifr4.ifr_flags = 0; + } + close(inet4); + return ifr4.ifr_flags; +} + +static int bind_to_interface(int fd, const char *name) +{ + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) { + LOGE("Cannot bind socket to %s: %s", name, strerror(errno)); + return SYSTEM_ERROR; + } + return 0; +} + +//------------------------------------------------------------------------------ + +static void throwException(JNIEnv *env, int error, const char *message) +{ + if (error == SYSTEM_ERROR) { + jniThrowException(env, "java/lang/IllegalStateException", message); + } else { + jniThrowException(env, "java/lang/IllegalArgumentException", message); + } +} + +static jobject configure(JNIEnv *env, jobject thiz, + jstring jAddresses, jstring jRoutes) +{ + char name[IFNAMSIZ]; + int index; + int tun = create_interface(name, &index); + if (tun < 0) { + throwException(env, tun, "Cannot create interface"); + return NULL; + } + LOGD("%s is created", name); + + const char *addresses; + const char *routes; + int count; + + // Addresses are required. + addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL; + if (!addresses) { + jniThrowNullPointerException(env, "address"); + goto error; + } + count = set_addresses(name, index, addresses); + env->ReleaseStringUTFChars(jAddresses, addresses); + if (count <= 0) { + throwException(env, count, "Cannot set address"); + goto error; + } + LOGD("Configured %d address(es) on %s", count, name); + + // Routes are optional. + routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL; + if (routes) { + count = set_routes(name, index, routes); + env->ReleaseStringUTFChars(jRoutes, routes); + if (count < 0) { + throwException(env, count, "Cannot set route"); + goto error; + } + LOGD("Configured %d route(s) on %s", count, name); + } + + return newParcelFileDescriptor(env, jniCreateFileDescriptor(env, tun)); + +error: + close(tun); + LOGD("%s is destroyed", name); + return NULL; +} + +static jstring getName(JNIEnv *env, jobject thiz, jint fd) +{ + char name[IFNAMSIZ]; + if (get_interface_name(name, fd) < 0) { + throwException(env, SYSTEM_ERROR, "Cannot get interface name"); + return NULL; + } + return env->NewStringUTF(name); +} + +static void reset(JNIEnv *env, jobject thiz, jstring jName) +{ + const char *name = jName ? + env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + return; + } + if (reset_interface(name) < 0) { + throwException(env, SYSTEM_ERROR, "Cannot reset interface"); + } else { + LOGD("%s is deactivated", name); + } + env->ReleaseStringUTFChars(jName, name); +} + +static jint check(JNIEnv *env, jobject thiz, jstring jName) +{ + const char *name = jName ? + env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + return 0; + } + int flags = check_interface(name); + env->ReleaseStringUTFChars(jName, name); + return flags; +} + +static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName) +{ + const char *name = jName ? + env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + return; + } + if (bind_to_interface(fd, name) < 0) { + throwException(env, SYSTEM_ERROR, "Cannot protect socket"); + } + env->ReleaseStringUTFChars(jName, name); +} + +//------------------------------------------------------------------------------ + +static JNINativeMethod gMethods[] = { + {"nativeConfigure", "(Ljava/lang/String;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", (void *)configure}, + {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName}, + {"nativeReset", "(Ljava/lang/String;)V", (void *)reset}, + {"nativeCheck", "(Ljava/lang/String;)I", (void *)check}, + {"nativeProtect", "(ILjava/lang/String;)V", (void *)protect}, +}; + +int register_android_server_connectivity_Vpn(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn", + gMethods, NELEM(gMethods)); +} + +}; diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp index 469e818..9dff48b 100644 --- a/services/jni/onload.cpp +++ b/services/jni/onload.cpp @@ -34,6 +34,7 @@ int register_android_server_UsbHostManager(JNIEnv* env); int register_android_server_VibratorService(JNIEnv* env); int register_android_server_SystemServer(JNIEnv* env); int register_android_server_location_GpsLocationProvider(JNIEnv* env); +int register_android_server_connectivity_Vpn(JNIEnv* env); }; using namespace android; @@ -63,6 +64,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_VibratorService(env); register_android_server_SystemServer(env); register_android_server_location_GpsLocationProvider(env); + register_android_server_connectivity_Vpn(env); return JNI_VERSION_1_4; } |