summaryrefslogtreecommitdiffstats
path: root/services/jni
diff options
context:
space:
mode:
Diffstat (limited to 'services/jni')
-rw-r--r--services/jni/Android.mk1
-rw-r--r--services/jni/com_android_server_InputManager.cpp254
-rw-r--r--services/jni/com_android_server_connectivity_Vpn.cpp450
-rw-r--r--services/jni/onload.cpp2
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;
}