diff options
Diffstat (limited to 'services/core/jni')
21 files changed, 6136 insertions, 0 deletions
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk new file mode 100644 index 0000000..1a3ce63 --- /dev/null +++ b/services/core/jni/Android.mk @@ -0,0 +1,56 @@ +# This file is included by the top level services directory to collect source +# files +LOCAL_REL_DIR := core/jni + +LOCAL_SRC_FILES += \ + $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \ + $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \ + $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \ + $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \ + $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \ + $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \ + $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \ + $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \ + $(LOCAL_REL_DIR)/onload.cpp + +LOCAL_C_INCLUDES += \ + $(JNI_H_INCLUDE) \ + frameworks/base/services \ + frameworks/base/libs \ + frameworks/base/core/jni \ + frameworks/native/services \ + external/skia/include/core \ + libcore/include \ + libcore/include/libsuspend \ + $(call include-path-for, libhardware)/hardware \ + $(call include-path-for, libhardware_legacy)/hardware_legacy \ + +LOCAL_SHARED_LIBRARIES += \ + libandroid_runtime \ + libandroidfw \ + libbinder \ + libcutils \ + liblog \ + libhardware \ + libhardware_legacy \ + libnativehelper \ + libutils \ + libui \ + libinput \ + libinputservice \ + libsensorservice \ + libskia \ + libgui \ + libusbhost \ + libsuspend \ + libEGL \ + libGLESv2 + diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp new file mode 100644 index 0000000..342515b --- /dev/null +++ b/services/core/jni/com_android_server_AlarmManagerService.cpp @@ -0,0 +1,321 @@ +/* //device/libs/android_runtime/android_server_AlarmManagerService.cpp +** +** Copyright 2006, 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_TAG "AlarmManagerService" + +#include "JNIHelp.h" +#include "jni.h" +#include <utils/Log.h> +#include <utils/misc.h> + +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/epoll.h> +#include <sys/timerfd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <linux/ioctl.h> +#include <linux/android_alarm.h> + +namespace android { + +static const size_t N_ANDROID_TIMERFDS = ANDROID_ALARM_TYPE_COUNT + 1; +static const clockid_t android_alarm_to_clockid[N_ANDROID_TIMERFDS] = { + CLOCK_REALTIME_ALARM, + CLOCK_REALTIME, + CLOCK_BOOTTIME_ALARM, + CLOCK_BOOTTIME, + CLOCK_MONOTONIC, + CLOCK_REALTIME, +}; +/* to match the legacy alarm driver implementation, we need an extra + CLOCK_REALTIME fd which exists specifically to be canceled on RTC changes */ + +class AlarmImpl +{ +public: + AlarmImpl(int *fds, size_t n_fds); + virtual ~AlarmImpl(); + + virtual int set(int type, struct timespec *ts) = 0; + virtual int waitForAlarm() = 0; + +protected: + int *fds; + size_t n_fds; +}; + +class AlarmImplAlarmDriver : public AlarmImpl +{ +public: + AlarmImplAlarmDriver(int fd) : AlarmImpl(&fd, 1) { } + + int set(int type, struct timespec *ts); + int waitForAlarm(); +}; + +class AlarmImplTimerFd : public AlarmImpl +{ +public: + AlarmImplTimerFd(int fds[N_ANDROID_TIMERFDS], int epollfd) : + AlarmImpl(fds, N_ANDROID_TIMERFDS), epollfd(epollfd) { } + ~AlarmImplTimerFd(); + + int set(int type, struct timespec *ts); + int waitForAlarm(); + +private: + int epollfd; +}; + +AlarmImpl::AlarmImpl(int *fds_, size_t n_fds) : fds(new int[n_fds]), + n_fds(n_fds) +{ + memcpy(fds, fds_, n_fds * sizeof(fds[0])); +} + +AlarmImpl::~AlarmImpl() +{ + for (size_t i = 0; i < n_fds; i++) { + close(fds[i]); + } + delete [] fds; +} + +int AlarmImplAlarmDriver::set(int type, struct timespec *ts) +{ + return ioctl(fds[0], ANDROID_ALARM_SET(type), ts); +} + +int AlarmImplAlarmDriver::waitForAlarm() +{ + return ioctl(fds[0], ANDROID_ALARM_WAIT); +} + +AlarmImplTimerFd::~AlarmImplTimerFd() +{ + for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) { + epoll_ctl(epollfd, EPOLL_CTL_DEL, fds[i], NULL); + } + close(epollfd); +} + +int AlarmImplTimerFd::set(int type, struct timespec *ts) +{ + if (type > ANDROID_ALARM_TYPE_COUNT) { + errno = EINVAL; + return -1; + } + + if (!ts->tv_nsec && !ts->tv_sec) { + ts->tv_nsec = 1; + } + /* timerfd interprets 0 = disarm, so replace with a practically + equivalent deadline of 1 ns */ + + struct itimerspec spec; + memset(&spec, 0, sizeof(spec)); + memcpy(&spec.it_value, ts, sizeof(spec.it_value)); + + return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL); +} + +int AlarmImplTimerFd::waitForAlarm() +{ + epoll_event events[N_ANDROID_TIMERFDS]; + + int nevents = epoll_wait(epollfd, events, N_ANDROID_TIMERFDS, -1); + if (nevents < 0) { + return nevents; + } + + int result = 0; + for (int i = 0; i < nevents; i++) { + uint32_t alarm_idx = events[i].data.u32; + uint64_t unused; + ssize_t err = read(fds[alarm_idx], &unused, sizeof(unused)); + if (err < 0) { + if (alarm_idx == ANDROID_ALARM_TYPE_COUNT && errno == ECANCELED) { + result |= ANDROID_ALARM_TIME_CHANGE_MASK; + } else { + return err; + } + } else { + result |= (1 << alarm_idx); + } + } + + return result; +} + +static jint android_server_AlarmManagerService_setKernelTimezone(JNIEnv*, jobject, jlong, jint minswest) +{ + struct timezone tz; + + tz.tz_minuteswest = minswest; + tz.tz_dsttime = 0; + + int result = settimeofday(NULL, &tz); + if (result < 0) { + ALOGE("Unable to set kernel timezone to %d: %s\n", minswest, strerror(errno)); + return -1; + } else { + ALOGD("Kernel timezone updated to %d minutes west of GMT\n", minswest); + } + + return 0; +} + +static jlong init_alarm_driver() +{ + int fd = open("/dev/alarm", O_RDWR); + if (fd < 0) { + ALOGV("opening alarm driver failed: %s", strerror(errno)); + return 0; + } + + AlarmImpl *ret = new AlarmImplAlarmDriver(fd); + return reinterpret_cast<jlong>(ret); +} + +static jlong init_timerfd() +{ + int epollfd; + int fds[N_ANDROID_TIMERFDS]; + + epollfd = epoll_create(N_ANDROID_TIMERFDS); + if (epollfd < 0) { + ALOGV("epoll_create(%u) failed: %s", N_ANDROID_TIMERFDS, + strerror(errno)); + return 0; + } + + for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) { + fds[i] = timerfd_create(android_alarm_to_clockid[i], 0); + if (fds[i] < 0) { + ALOGV("timerfd_create(%u) failed: %s", android_alarm_to_clockid[i], + strerror(errno)); + close(epollfd); + for (size_t j = 0; j < i; j++) { + close(fds[j]); + } + return 0; + } + } + + AlarmImpl *ret = new AlarmImplTimerFd(fds, epollfd); + + for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) { + epoll_event event; + event.events = EPOLLIN | EPOLLWAKEUP; + event.data.u32 = i; + + int err = epoll_ctl(epollfd, EPOLL_CTL_ADD, fds[i], &event); + if (err < 0) { + ALOGV("epoll_ctl(EPOLL_CTL_ADD) failed: %s", strerror(errno)); + delete ret; + return 0; + } + } + + struct itimerspec spec; + memset(&spec, 0, sizeof(spec)); + /* 0 = disarmed; the timerfd doesn't need to be armed to get + RTC change notifications, just set up as cancelable */ + + int err = timerfd_settime(fds[ANDROID_ALARM_TYPE_COUNT], + TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET, &spec, NULL); + if (err < 0) { + ALOGV("timerfd_settime() failed: %s", strerror(errno)); + delete ret; + return 0; + } + + return reinterpret_cast<jlong>(ret); +} + +static jlong android_server_AlarmManagerService_init(JNIEnv*, jobject) +{ + jlong ret = init_alarm_driver(); + if (ret) { + return ret; + } + + return init_timerfd(); +} + +static void android_server_AlarmManagerService_close(JNIEnv*, jobject, jlong nativeData) +{ + AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); + delete impl; +} + +static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativeData, jint type, jlong seconds, jlong nanoseconds) +{ + AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); + struct timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; + + int result = impl->set(type, &ts); + if (result < 0) + { + ALOGE("Unable to set alarm to %lld.%09lld: %s\n", seconds, nanoseconds, strerror(errno)); + } +} + +static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv*, jobject, jlong nativeData) +{ + AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData); + int result = 0; + + do + { + result = impl->waitForAlarm(); + } while (result < 0 && errno == EINTR); + + if (result < 0) + { + ALOGE("Unable to wait on alarm: %s\n", strerror(errno)); + return 0; + } + + return result; +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"init", "()J", (void*)android_server_AlarmManagerService_init}, + {"close", "(J)V", (void*)android_server_AlarmManagerService_close}, + {"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set}, + {"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm}, + {"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone}, +}; + +int register_android_server_AlarmManagerService(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService", + sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp new file mode 100644 index 0000000..4a1b55d --- /dev/null +++ b/services/core/jni/com_android_server_AssetAtlasService.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2013 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_TAG "AssetAtlasService" + +#include "jni.h" +#include "JNIHelp.h" + +#include <android_view_GraphicBuffer.h> +#include <cutils/log.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <SkCanvas.h> +#include <SkBitmap.h> + +namespace android { + +// ---------------------------------------------------------------------------- +// Defines +// ---------------------------------------------------------------------------- + +// Defines how long to wait for the GPU when uploading the atlas +// This timeout is defined in nanoseconds (see EGL_KHR_fence_sync extension) +#define FENCE_TIMEOUT 2000000000 + +// ---------------------------------------------------------------------------- +// JNI Helpers +// ---------------------------------------------------------------------------- + +static struct { + jfieldID mFinalizer; + jfieldID mNativeCanvas; +} gCanvasClassInfo; + +static struct { + jfieldID mNativeCanvas; +} gCanvasFinalizerClassInfo; + +#define GET_LONG(object, field) \ + env->GetLongField(object, field) + +#define SET_LONG(object, field, value) \ + env->SetLongField(object, field, value) + +// ---------------------------------------------------------------------------- +// Canvas management +// ---------------------------------------------------------------------------- + +static inline void swapCanvasPtr(JNIEnv* env, jobject canvasObj, SkCanvas* newCanvas) { + jobject canvasFinalizerObj = env->GetObjectField(canvasObj, gCanvasClassInfo.mFinalizer); + SkCanvas* previousCanvas = reinterpret_cast<SkCanvas*>( + GET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas)); + SET_LONG(canvasObj, gCanvasClassInfo.mNativeCanvas, (long) newCanvas); + SET_LONG(canvasFinalizerObj, gCanvasFinalizerClassInfo.mNativeCanvas, (long) newCanvas); + SkSafeUnref(previousCanvas); +} + +static SkBitmap* com_android_server_AssetAtlasService_acquireCanvas(JNIEnv* env, jobject, + jobject canvas, jint width, jint height) { + + SkBitmap* bitmap = new SkBitmap; + bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap->allocPixels(); + bitmap->eraseColor(0); + + SkCanvas* nativeCanvas = SkNEW_ARGS(SkCanvas, (*bitmap)); + swapCanvasPtr(env, canvas, nativeCanvas); + + return bitmap; +} + +static void com_android_server_AssetAtlasService_releaseCanvas(JNIEnv* env, jobject, + jobject canvas, SkBitmap* bitmap) { + + SkCanvas* nativeCanvas = SkNEW(SkCanvas); + swapCanvasPtr(env, canvas, nativeCanvas); + + delete bitmap; +} + +#define CLEANUP_GL_AND_RETURN(result) \ + if (fence != EGL_NO_SYNC_KHR) eglDestroySyncKHR(display, fence); \ + if (image) eglDestroyImageKHR(display, image); \ + if (texture) glDeleteTextures(1, &texture); \ + if (surface != EGL_NO_SURFACE) eglDestroySurface(display, surface); \ + if (context != EGL_NO_CONTEXT) eglDestroyContext(display, context); \ + eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); \ + eglReleaseThread(); \ + eglTerminate(display); \ + return result; + +static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject, + jobject graphicBuffer, SkBitmap* bitmap) { + + // The goal of this method is to copy the bitmap into the GraphicBuffer + // using the GPU to swizzle the texture content + sp<GraphicBuffer> buffer(graphicBufferForJavaObject(env, graphicBuffer)); + + if (buffer != NULL) { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) return false; + + EGLint major; + EGLint minor; + if (!eglInitialize(display, &major, &minor)) { + ALOGW("Could not initialize EGL"); + return false; + } + + // We're going to use a 1x1 pbuffer surface later on + // The configuration doesn't really matter for what we're trying to do + EGLint configAttrs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 0, + EGL_STENCIL_SIZE, 0, + EGL_NONE + }; + EGLConfig configs[1]; + EGLint configCount; + if (!eglChooseConfig(display, configAttrs, configs, 1, &configCount)) { + ALOGW("Could not select EGL configuration"); + eglReleaseThread(); + eglTerminate(display); + return false; + } + if (configCount <= 0) { + ALOGW("Could not find EGL configuration"); + eglReleaseThread(); + eglTerminate(display); + return false; + } + + // These objects are initialized below but the default "null" + // values are used to cleanup properly at any point in the + // initialization sequence + GLuint texture = 0; + EGLImageKHR image = EGL_NO_IMAGE_KHR; + EGLSurface surface = EGL_NO_SURFACE; + EGLSyncKHR fence = EGL_NO_SYNC_KHR; + + EGLint attrs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; + EGLContext context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, attrs); + if (context == EGL_NO_CONTEXT) { + ALOGW("Could not create EGL context"); + CLEANUP_GL_AND_RETURN(false); + } + + // Create the 1x1 pbuffer + EGLint surfaceAttrs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; + surface = eglCreatePbufferSurface(display, configs[0], surfaceAttrs); + if (surface == EGL_NO_SURFACE) { + ALOGW("Could not create EGL surface"); + CLEANUP_GL_AND_RETURN(false); + } + + if (!eglMakeCurrent(display, surface, surface, context)) { + ALOGW("Could not change current EGL context"); + CLEANUP_GL_AND_RETURN(false); + } + + // We use an EGLImage to access the content of the GraphicBuffer + // The EGL image is later bound to a 2D texture + EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer(); + EGLint imageAttrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + image = eglCreateImageKHR(display, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs); + if (image == EGL_NO_IMAGE_KHR) { + ALOGW("Could not create EGL image"); + CLEANUP_GL_AND_RETURN(false); + } + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image); + if (glGetError() != GL_NO_ERROR) { + ALOGW("Could not create/bind texture"); + CLEANUP_GL_AND_RETURN(false); + } + + // Upload the content of the bitmap in the GraphicBuffer + glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bitmap->width(), bitmap->height(), + GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels()); + if (glGetError() != GL_NO_ERROR) { + ALOGW("Could not upload to texture"); + CLEANUP_GL_AND_RETURN(false); + } + + // The fence is used to wait for the texture upload to finish + // properly. We cannot rely on glFlush() and glFinish() as + // some drivers completely ignore these API calls + fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL); + if (fence == EGL_NO_SYNC_KHR) { + ALOGW("Could not create sync fence %#x", eglGetError()); + CLEANUP_GL_AND_RETURN(false); + } + + // The flag EGL_SYNC_FLUSH_COMMANDS_BIT_KHR will trigger a + // pipeline flush (similar to what a glFlush() would do.) + EGLint waitStatus = eglClientWaitSyncKHR(display, fence, + EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, FENCE_TIMEOUT); + if (waitStatus != EGL_CONDITION_SATISFIED_KHR) { + ALOGW("Failed to wait for the fence %#x", eglGetError()); + CLEANUP_GL_AND_RETURN(false); + } + + CLEANUP_GL_AND_RETURN(true); + } + + return false; +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +const char* const kClassPathName = "com/android/server/AssetAtlasService"; + +static JNINativeMethod gMethods[] = { + { "nAcquireAtlasCanvas", "(Landroid/graphics/Canvas;II)I", + (void*) com_android_server_AssetAtlasService_acquireCanvas }, + { "nReleaseAtlasCanvas", "(Landroid/graphics/Canvas;I)V", + (void*) com_android_server_AssetAtlasService_releaseCanvas }, + { "nUploadAtlas", "(Landroid/view/GraphicBuffer;I)Z", + (void*) com_android_server_AssetAtlasService_upload }, +}; + +int register_android_server_AssetAtlasService(JNIEnv* env) { + jclass clazz; + + FIND_CLASS(clazz, "android/graphics/Canvas"); + GET_FIELD_ID(gCanvasClassInfo.mFinalizer, clazz, "mFinalizer", + "Landroid/graphics/Canvas$CanvasFinalizer;"); + GET_FIELD_ID(gCanvasClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); + + FIND_CLASS(clazz, "android/graphics/Canvas$CanvasFinalizer"); + GET_FIELD_ID(gCanvasFinalizerClassInfo.mNativeCanvas, clazz, "mNativeCanvas", "J"); + + return jniRegisterNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp new file mode 100644 index 0000000..004c0aa --- /dev/null +++ b/services/core/jni/com_android_server_ConsumerIrService.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2013 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_TAG "ConsumerIrService" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <stdlib.h> +#include <utils/misc.h> +#include <utils/Log.h> +#include <hardware/hardware.h> +#include <hardware/consumerir.h> +#include <ScopedPrimitiveArray.h> + +namespace android { + +static jint halOpen(JNIEnv *env, jobject obj) { + hw_module_t const* module; + consumerir_device_t *dev; + int err; + + err = hw_get_module(CONSUMERIR_HARDWARE_MODULE_ID, &module); + if (err != 0) { + ALOGE("Can't open consumer IR HW Module, error: %d", err); + return 0; + } + + err = module->methods->open(module, CONSUMERIR_TRANSMITTER, + (hw_device_t **) &dev); + if (err < 0) { + ALOGE("Can't open consumer IR transmitter, error: %d", err); + return 0; + } + + return reinterpret_cast<jint>(dev); +} + +static jint halTransmit(JNIEnv *env, jobject obj, jint halObject, + jint carrierFrequency, jintArray pattern) { + int ret; + + consumerir_device_t *dev = reinterpret_cast<consumerir_device_t*>(halObject); + ScopedIntArrayRO cPattern(env, pattern); + if (cPattern.get() == NULL) { + return -EINVAL; + } + jsize patternLength = cPattern.size(); + + ret = dev->transmit(dev, carrierFrequency, cPattern.get(), patternLength); + + return reinterpret_cast<jint>(ret); +} + +static jintArray halGetCarrierFrequencies(JNIEnv *env, jobject obj, + jint halObject) { + consumerir_device_t *dev = (consumerir_device_t *) halObject; + consumerir_freq_range_t *ranges; + int len; + + len = dev->get_num_carrier_freqs(dev); + if (len <= 0) + return NULL; + + ranges = new consumerir_freq_range_t[len]; + + len = dev->get_carrier_freqs(dev, len, ranges); + if (len <= 0) { + delete[] ranges; + return NULL; + } + + int i; + ScopedIntArrayRW freqsOut(env, env->NewIntArray(len*2)); + jint *arr = freqsOut.get(); + if (arr == NULL) { + delete[] ranges; + return NULL; + } + for (i = 0; i < len; i++) { + arr[i*2] = ranges[i].min; + arr[i*2+1] = ranges[i].max; + } + + delete[] ranges; + return freqsOut.getJavaArray(); +} + +static JNINativeMethod method_table[] = { + { "halOpen", "()I", (void *)halOpen }, + { "halTransmit", "(II[I)I", (void *)halTransmit }, + { "halGetCarrierFrequencies", "(I)[I", (void *)halGetCarrierFrequencies}, +}; + +int register_android_server_ConsumerIrService(JNIEnv *env) { + return jniRegisterNativeMethods(env, "com/android/server/ConsumerIrService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/core/jni/com_android_server_SerialService.cpp b/services/core/jni/com_android_server_SerialService.cpp new file mode 100644 index 0000000..b889b78 --- /dev/null +++ b/services/core/jni/com_android_server_SerialService.cpp @@ -0,0 +1,82 @@ +/* + * 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_TAG "SerialServiceJNI" +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> + +namespace android +{ + +static struct parcel_file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; +} gParcelFileDescriptorOffsets; + +static jobject android_server_SerialService_open(JNIEnv *env, jobject thiz, jstring path) +{ + const char *pathStr = env->GetStringUTFChars(path, NULL); + + int fd = open(pathStr, O_RDWR | O_NOCTTY); + if (fd < 0) { + ALOGE("could not open %s", pathStr); + env->ReleaseStringUTFChars(path, pathStr); + return NULL; + } + env->ReleaseStringUTFChars(path, pathStr); + + jobject fileDescriptor = jniCreateFileDescriptor(env, fd); + if (fileDescriptor == NULL) { + return NULL; + } + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); +} + + +static JNINativeMethod method_table[] = { + { "native_open", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", + (void*)android_server_SerialService_open }, +}; + +int register_android_server_SerialService(JNIEnv *env) +{ + jclass clazz = env->FindClass("com/android/server/SerialService"); + if (clazz == NULL) { + ALOGE("Can't find com/android/server/SerialService"); + return -1; + } + + clazz = env->FindClass("android/os/ParcelFileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); + gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, + "Unable to find constructor for android.os.ParcelFileDescriptor"); + + return jniRegisterNativeMethods(env, "com/android/server/SerialService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp new file mode 100644 index 0000000..0625544 --- /dev/null +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2007 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. + */ + +#include <jni.h> +#include <JNIHelp.h> + +#include <sensorservice/SensorService.h> + +#include <cutils/properties.h> +#include <utils/Log.h> +#include <utils/misc.h> + +namespace android { + +static void android_server_SystemServer_nativeInit(JNIEnv* env, jobject clazz) { + char propBuf[PROPERTY_VALUE_MAX]; + property_get("system_init.startsensorservice", propBuf, "1"); + if (strcmp(propBuf, "1") == 0) { + // Start the sensor service + SensorService::instantiate(); + } +} + +/* + * JNI registration. + */ +static JNINativeMethod gMethods[] = { + /* name, signature, funcPtr */ + { "nativeInit", "()V", (void*) android_server_SystemServer_nativeInit }, +}; + +int register_android_server_SystemServer(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/server/SystemServer", + gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/services/core/jni/com_android_server_UsbDeviceManager.cpp b/services/core/jni/com_android_server_UsbDeviceManager.cpp new file mode 100644 index 0000000..3551733 --- /dev/null +++ b/services/core/jni/com_android_server_UsbDeviceManager.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2010 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_TAG "UsbDeviceManagerJNI" +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" + +#include <stdio.h> +#include <asm/byteorder.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/usb/f_accessory.h> + +#define DRIVER_NAME "/dev/usb_accessory" + +namespace android +{ + +static struct parcel_file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; +} gParcelFileDescriptorOffsets; + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index) +{ + char buffer[256]; + + buffer[0] = 0; + int length = ioctl(fd, cmd, buffer); + if (buffer[0]) { + jstring obj = env->NewStringUTF(buffer); + env->SetObjectArrayElement(strArray, index, obj); + env->DeleteLocalRef(obj); + } +} + + +static jobjectArray android_server_UsbDeviceManager_getAccessoryStrings(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + ALOGE("could not open %s", DRIVER_NAME); + return NULL; + } + jclass stringClass = env->FindClass("java/lang/String"); + jobjectArray strArray = env->NewObjectArray(6, stringClass, NULL); + if (!strArray) goto out; + set_accessory_string(env, fd, ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_DESCRIPTION, strArray, 2); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_URI, strArray, 4); + set_accessory_string(env, fd, ACCESSORY_GET_STRING_SERIAL, strArray, 5); + +out: + close(fd); + return strArray; +} + +static jobject android_server_UsbDeviceManager_openAccessory(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + ALOGE("could not open %s", DRIVER_NAME); + return NULL; + } + jobject fileDescriptor = jniCreateFileDescriptor(env, fd); + if (fileDescriptor == NULL) { + return NULL; + } + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); +} + +static jboolean android_server_UsbDeviceManager_isStartRequested(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + ALOGE("could not open %s", DRIVER_NAME); + return false; + } + int result = ioctl(fd, ACCESSORY_IS_START_REQUESTED); + close(fd); + return (result == 1); +} + +static jint android_server_UsbDeviceManager_getAudioMode(JNIEnv *env, jobject thiz) +{ + int fd = open(DRIVER_NAME, O_RDWR); + if (fd < 0) { + ALOGE("could not open %s", DRIVER_NAME); + return false; + } + int result = ioctl(fd, ACCESSORY_GET_AUDIO_MODE); + close(fd); + return result; +} + +static JNINativeMethod method_table[] = { + { "nativeGetAccessoryStrings", "()[Ljava/lang/String;", + (void*)android_server_UsbDeviceManager_getAccessoryStrings }, + { "nativeOpenAccessory", "()Landroid/os/ParcelFileDescriptor;", + (void*)android_server_UsbDeviceManager_openAccessory }, + { "nativeIsStartRequested", "()Z", + (void*)android_server_UsbDeviceManager_isStartRequested }, + { "nativeGetAudioMode", "()I", + (void*)android_server_UsbDeviceManager_getAudioMode }, +}; + +int register_android_server_UsbDeviceManager(JNIEnv *env) +{ + jclass clazz = env->FindClass("com/android/server/usb/UsbDeviceManager"); + if (clazz == NULL) { + ALOGE("Can't find com/android/server/usb/UsbDeviceManager"); + return -1; + } + + clazz = env->FindClass("android/os/ParcelFileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); + gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, + "Unable to find constructor for android.os.ParcelFileDescriptor"); + + return jniRegisterNativeMethods(env, "com/android/server/usb/UsbDeviceManager", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/core/jni/com_android_server_UsbHostManager.cpp b/services/core/jni/com_android_server_UsbHostManager.cpp new file mode 100644 index 0000000..f1fa6cf --- /dev/null +++ b/services/core/jni/com_android_server_UsbHostManager.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2010 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_TAG "UsbHostManagerJNI" +#include "utils/Log.h" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" +#include "utils/Vector.h" + +#include <usbhost/usbhost.h> + +#include <stdio.h> +#include <asm/byteorder.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> + +namespace android +{ + +static struct parcel_file_descriptor_offsets_t +{ + jclass mClass; + jmethodID mConstructor; +} gParcelFileDescriptorOffsets; + +static jmethodID method_usbDeviceAdded; +static jmethodID method_usbDeviceRemoved; + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +static int usb_device_added(const char *devname, void* client_data) { + struct usb_descriptor_header* desc; + struct usb_descriptor_iter iter; + + struct usb_device *device = usb_device_open(devname); + if (!device) { + ALOGE("usb_device_open failed\n"); + return 0; + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject thiz = (jobject)client_data; + Vector<int> interfaceValues; + Vector<int> endpointValues; + const usb_device_descriptor* deviceDesc = usb_device_get_device_descriptor(device); + + uint16_t vendorId = usb_device_get_vendor_id(device); + uint16_t productId = usb_device_get_product_id(device); + uint8_t deviceClass = deviceDesc->bDeviceClass; + uint8_t deviceSubClass = deviceDesc->bDeviceSubClass; + uint8_t protocol = deviceDesc->bDeviceProtocol; + char *manufacturer = usb_device_get_manufacturer_name(device); + char *product = usb_device_get_product_name(device); + char *serial = usb_device_get_serial(device); + + usb_descriptor_iter_init(device, &iter); + + while ((desc = usb_descriptor_iter_next(&iter)) != NULL) { + if (desc->bDescriptorType == USB_DT_INTERFACE) { + struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc; + + // push class, subclass, protocol and number of endpoints into interfaceValues vector + interfaceValues.add(interface->bInterfaceNumber); + interfaceValues.add(interface->bInterfaceClass); + interfaceValues.add(interface->bInterfaceSubClass); + interfaceValues.add(interface->bInterfaceProtocol); + interfaceValues.add(interface->bNumEndpoints); + } else if (desc->bDescriptorType == USB_DT_ENDPOINT) { + struct usb_endpoint_descriptor *endpoint = (struct usb_endpoint_descriptor *)desc; + + // push address, attributes, max packet size and interval into endpointValues vector + endpointValues.add(endpoint->bEndpointAddress); + endpointValues.add(endpoint->bmAttributes); + endpointValues.add(__le16_to_cpu(endpoint->wMaxPacketSize)); + endpointValues.add(endpoint->bInterval); + } + } + + usb_device_close(device); + + // handle generic device notification + int length = interfaceValues.size(); + jintArray interfaceArray = env->NewIntArray(length); + env->SetIntArrayRegion(interfaceArray, 0, length, interfaceValues.array()); + + length = endpointValues.size(); + jintArray endpointArray = env->NewIntArray(length); + env->SetIntArrayRegion(endpointArray, 0, length, endpointValues.array()); + + jstring deviceName = env->NewStringUTF(devname); + jstring manufacturerName = env->NewStringUTF(manufacturer); + jstring productName = env->NewStringUTF(product); + jstring serialNumber = env->NewStringUTF(serial); + env->CallVoidMethod(thiz, method_usbDeviceAdded, + deviceName, vendorId, productId, deviceClass, + deviceSubClass, protocol, manufacturerName, + productName, serialNumber, interfaceArray, endpointArray); + + env->DeleteLocalRef(interfaceArray); + env->DeleteLocalRef(endpointArray); + env->DeleteLocalRef(serialNumber); + env->DeleteLocalRef(productName); + env->DeleteLocalRef(manufacturerName); + env->DeleteLocalRef(deviceName); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + + return 0; +} + +static int usb_device_removed(const char *devname, void* client_data) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject thiz = (jobject)client_data; + + jstring deviceName = env->NewStringUTF(devname); + env->CallVoidMethod(thiz, method_usbDeviceRemoved, deviceName); + env->DeleteLocalRef(deviceName); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + return 0; +} + +static void android_server_UsbHostManager_monitorUsbHostBus(JNIEnv *env, jobject thiz) +{ + struct usb_host_context* context = usb_host_init(); + if (!context) { + ALOGE("usb_host_init failed"); + return; + } + // this will never return so it is safe to pass thiz directly + usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)thiz); +} + +static jobject android_server_UsbHostManager_openDevice(JNIEnv *env, jobject thiz, jstring deviceName) +{ + const char *deviceNameStr = env->GetStringUTFChars(deviceName, NULL); + struct usb_device* device = usb_device_open(deviceNameStr); + env->ReleaseStringUTFChars(deviceName, deviceNameStr); + + if (!device) + return NULL; + + int fd = usb_device_get_fd(device); + if (fd < 0) + return NULL; + int newFD = dup(fd); + usb_device_close(device); + + jobject fileDescriptor = jniCreateFileDescriptor(env, newFD); + if (fileDescriptor == NULL) { + return NULL; + } + return env->NewObject(gParcelFileDescriptorOffsets.mClass, + gParcelFileDescriptorOffsets.mConstructor, fileDescriptor); +} + +static JNINativeMethod method_table[] = { + { "monitorUsbHostBus", "()V", (void*)android_server_UsbHostManager_monitorUsbHostBus }, + { "nativeOpenDevice", "(Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", + (void*)android_server_UsbHostManager_openDevice }, +}; + +int register_android_server_UsbHostManager(JNIEnv *env) +{ + jclass clazz = env->FindClass("com/android/server/usb/UsbHostManager"); + if (clazz == NULL) { + ALOGE("Can't find com/android/server/usb/UsbHostManager"); + return -1; + } + method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I)V"); + if (method_usbDeviceAdded == NULL) { + ALOGE("Can't find usbDeviceAdded"); + return -1; + } + method_usbDeviceRemoved = env->GetMethodID(clazz, "usbDeviceRemoved", "(Ljava/lang/String;)V"); + if (method_usbDeviceRemoved == NULL) { + ALOGE("Can't find usbDeviceRemoved"); + return -1; + } + + clazz = env->FindClass("android/os/ParcelFileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); + gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); + gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V"); + LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, + "Unable to find constructor for android.os.ParcelFileDescriptor"); + + return jniRegisterNativeMethods(env, "com/android/server/usb/UsbHostManager", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp new file mode 100644 index 0000000..2b3f74a --- /dev/null +++ b/services/core/jni/com_android_server_VibratorService.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2009 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_TAG "VibratorService" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <utils/misc.h> +#include <utils/Log.h> +#include <hardware_legacy/vibrator.h> + +#include <stdio.h> + +namespace android +{ + +static jboolean vibratorExists(JNIEnv *env, jobject clazz) +{ + return vibrator_exists() > 0 ? JNI_TRUE : JNI_FALSE; +} + +static void vibratorOn(JNIEnv *env, jobject clazz, jlong timeout_ms) +{ + // ALOGI("vibratorOn\n"); + vibrator_on(timeout_ms); +} + +static void vibratorOff(JNIEnv *env, jobject clazz) +{ + // ALOGI("vibratorOff\n"); + vibrator_off(); +} + +static JNINativeMethod method_table[] = { + { "vibratorExists", "()Z", (void*)vibratorExists }, + { "vibratorOn", "(J)V", (void*)vibratorOn }, + { "vibratorOff", "()V", (void*)vibratorOff } +}; + +int register_android_server_VibratorService(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "com/android/server/VibratorService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/core/jni/com_android_server_connectivity_Vpn.cpp b/services/core/jni/com_android_server_connectivity_Vpn.cpp new file mode 100644 index 0000000..ab8c959 --- /dev/null +++ b/services/core/jni/com_android_server_connectivity_Vpn.cpp @@ -0,0 +1,473 @@ +/* + * 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 <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" + +namespace android +{ + +static int inet4 = -1; +static int inet6 = -1; + +static inline in_addr_t *as_in_addr(sockaddr *sa) { + return &((sockaddr_in *)sa)->sin_addr.s_addr; +} + +//------------------------------------------------------------------------------ + +#define SYSTEM_ERROR -1 +#define BAD_ARGUMENT -2 + +static int create_interface(int mtu) +{ + int tun = open("/dev/tun", O_RDWR | O_NONBLOCK); + + ifreq ifr4; + memset(&ifr4, 0, sizeof(ifr4)); + + // Allocate interface. + ifr4.ifr_flags = IFF_TUN | IFF_NO_PI; + if (ioctl(tun, TUNSETIFF, &ifr4)) { + ALOGE("Cannot allocate TUN: %s", strerror(errno)); + goto error; + } + + // Activate interface. + ifr4.ifr_flags = IFF_UP; + if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) { + ALOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno)); + goto error; + } + + // Set MTU if it is specified. + ifr4.ifr_mtu = mtu; + if (mtu > 0 && ioctl(inet4, SIOCSIFMTU, &ifr4)) { + ALOGE("Cannot set MTU on %s: %s", ifr4.ifr_name, strerror(errno)); + goto error; + } + + return tun; + +error: + close(tun); + return SYSTEM_ERROR; +} + +static int get_interface_name(char *name, int tun) +{ + ifreq ifr4; + if (ioctl(tun, TUNGETIFF, &ifr4)) { + ALOGE("Cannot get interface name: %s", strerror(errno)); + return SYSTEM_ERROR; + } + strncpy(name, ifr4.ifr_name, IFNAMSIZ); + return 0; +} + +static int get_interface_index(const char *name) +{ + ifreq ifr4; + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + if (ioctl(inet4, SIOGIFINDEX, &ifr4)) { + ALOGE("Cannot get index of %s: %s", name, strerror(errno)); + return SYSTEM_ERROR; + } + return ifr4.ifr_ifindex; +} + +static int set_addresses(const char *name, const char *addresses) +{ + int index = get_interface_index(name); + if (index < 0) { + return index; + } + + ifreq ifr4; + memset(&ifr4, 0, sizeof(ifr4)); + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + ifr4.ifr_addr.sa_family = AF_INET; + ifr4.ifr_netmask.sa_family = AF_INET; + + 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_netmask) = htonl(mask); + if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + } + ALOGD("Address added on %s: %s/%d", name, address, prefix); + ++count; + } + + if (count == BAD_ARGUMENT) { + ALOGE("Invalid address: %s/%d", address, prefix); + } else if (count == SYSTEM_ERROR) { + ALOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno)); + } else if (*addresses) { + ALOGE("Invalid address: %s", addresses); + count = BAD_ARGUMENT; + } + + return count; +} + +static int set_routes(const char *name, const char *routes) +{ + int index = get_interface_index(name); + if (index < 0) { + return index; + } + + rtentry rt4; + memset(&rt4, 0, sizeof(rt4)); + rt4.rt_dev = (char *)name; + rt4.rt_flags = RTF_UP; + rt4.rt_dst.sa_family = AF_INET; + rt4.rt_genmask.sa_family = AF_INET; + + in6_rtmsg rt6; + memset(&rt6, 0, sizeof(rt6)); + rt6.rtmsg_ifindex = index; + rt6.rtmsg_flags = RTF_UP; + + char address[65]; + int prefix; + int chars; + int count = 0; + + while (sscanf(routes, " %64[^/]/%d %n", address, &prefix, &chars) == 2) { + routes += chars; + + if (strchr(address, ':')) { + // Add an IPv6 route. + if (inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 || + prefix < 0 || prefix > 128) { + count = BAD_ARGUMENT; + break; + } + + rt6.rtmsg_dst_len = prefix ? prefix : 1; + if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + + if (!prefix) { + // Split the route instead of replacing the default route. + rt6.rtmsg_dst.s6_addr[0] ^= 0x80; + if (ioctl(inet6, SIOCADDRT, &rt6) && errno != EEXIST) { + count = SYSTEM_ERROR; + break; + } + } + } else { + // Add an IPv4 route. + if (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)) : 0x80000000; + *as_in_addr(&rt4.rt_genmask) = htonl(mask); + if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) { + count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR; + break; + } + + if (!prefix) { + // Split the route instead of replacing the default route. + *as_in_addr(&rt4.rt_dst) ^= htonl(0x80000000); + if (ioctl(inet4, SIOCADDRT, &rt4) && errno != EEXIST) { + count = SYSTEM_ERROR; + break; + } + } + } + ALOGD("Route added on %s: %s/%d", name, address, prefix); + ++count; + } + + if (count == BAD_ARGUMENT) { + ALOGE("Invalid route: %s/%d", address, prefix); + } else if (count == SYSTEM_ERROR) { + ALOGE("Cannot add route: %s/%d: %s", + address, prefix, strerror(errno)); + } else if (*routes) { + ALOGE("Invalid route: %s", routes); + count = BAD_ARGUMENT; + } + + return count; +} + +static int reset_interface(const char *name) +{ + ifreq ifr4; + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + ifr4.ifr_flags = 0; + + if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) { + ALOGE("Cannot reset %s: %s", name, strerror(errno)); + return SYSTEM_ERROR; + } + return 0; +} + +static int check_interface(const char *name) +{ + ifreq ifr4; + strncpy(ifr4.ifr_name, name, IFNAMSIZ); + ifr4.ifr_flags = 0; + + if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) { + ALOGE("Cannot check %s: %s", name, strerror(errno)); + } + return ifr4.ifr_flags; +} + +static int bind_to_interface(int socket, const char *name) +{ + if (setsockopt(socket, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) { + ALOGE("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 jint create(JNIEnv *env, jobject thiz, jint mtu) +{ + int tun = create_interface(mtu); + if (tun < 0) { + throwException(env, tun, "Cannot create interface"); + return -1; + } + return tun; +} + +static jstring getName(JNIEnv *env, jobject thiz, jint tun) +{ + char name[IFNAMSIZ]; + if (get_interface_name(name, tun) < 0) { + throwException(env, SYSTEM_ERROR, "Cannot get interface name"); + return NULL; + } + return env->NewStringUTF(name); +} + +static jint setAddresses(JNIEnv *env, jobject thiz, jstring jName, + jstring jAddresses) +{ + const char *name = NULL; + const char *addresses = NULL; + int count = -1; + + name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + goto error; + } + addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL; + if (!addresses) { + jniThrowNullPointerException(env, "addresses"); + goto error; + } + count = set_addresses(name, addresses); + if (count < 0) { + throwException(env, count, "Cannot set address"); + count = -1; + } + +error: + if (name) { + env->ReleaseStringUTFChars(jName, name); + } + if (addresses) { + env->ReleaseStringUTFChars(jAddresses, addresses); + } + return count; +} + +static jint setRoutes(JNIEnv *env, jobject thiz, jstring jName, + jstring jRoutes) +{ + const char *name = NULL; + const char *routes = NULL; + int count = -1; + + name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + goto error; + } + routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL; + if (!routes) { + jniThrowNullPointerException(env, "routes"); + goto error; + } + count = set_routes(name, routes); + if (count < 0) { + throwException(env, count, "Cannot set route"); + count = -1; + } + +error: + if (name) { + env->ReleaseStringUTFChars(jName, name); + } + if (routes) { + env->ReleaseStringUTFChars(jRoutes, routes); + } + return count; +} + +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"); + } + 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 socket, jstring jName) +{ + const char *name = jName ? env->GetStringUTFChars(jName, NULL) : NULL; + if (!name) { + jniThrowNullPointerException(env, "name"); + return; + } + if (bind_to_interface(socket, name) < 0) { + throwException(env, SYSTEM_ERROR, "Cannot protect socket"); + } + env->ReleaseStringUTFChars(jName, name); +} + +//------------------------------------------------------------------------------ + +static JNINativeMethod gMethods[] = { + {"jniCreate", "(I)I", (void *)create}, + {"jniGetName", "(I)Ljava/lang/String;", (void *)getName}, + {"jniSetAddresses", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setAddresses}, + {"jniSetRoutes", "(Ljava/lang/String;Ljava/lang/String;)I", (void *)setRoutes}, + {"jniReset", "(Ljava/lang/String;)V", (void *)reset}, + {"jniCheck", "(Ljava/lang/String;)I", (void *)check}, + {"jniProtect", "(ILjava/lang/String;)V", (void *)protect}, +}; + +int register_android_server_connectivity_Vpn(JNIEnv *env) +{ + if (inet4 == -1) { + inet4 = socket(AF_INET, SOCK_DGRAM, 0); + } + if (inet6 == -1) { + inet6 = socket(AF_INET6, SOCK_DGRAM, 0); + } + return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn", + gMethods, NELEM(gMethods)); +} + +}; diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp new file mode 100644 index 0000000..f943d16 --- /dev/null +++ b/services/core/jni/com_android_server_input_InputApplicationHandle.cpp @@ -0,0 +1,158 @@ +/* + * 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_TAG "InputApplicationHandle" + +#include "JNIHelp.h" +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> +#include <utils/threads.h> + +#include "com_android_server_input_InputApplicationHandle.h" + +namespace android { + +static struct { + jfieldID ptr; + jfieldID name; + jfieldID dispatchingTimeoutNanos; +} gInputApplicationHandleClassInfo; + +static Mutex gHandleMutex; + + +// --- NativeInputApplicationHandle --- + +NativeInputApplicationHandle::NativeInputApplicationHandle(jweak objWeak) : + mObjWeak(objWeak) { +} + +NativeInputApplicationHandle::~NativeInputApplicationHandle() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mObjWeak); +} + +jobject NativeInputApplicationHandle::getInputApplicationHandleObjLocalRef(JNIEnv* env) { + return env->NewLocalRef(mObjWeak); +} + +bool NativeInputApplicationHandle::updateInfo() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject obj = env->NewLocalRef(mObjWeak); + if (!obj) { + releaseInfo(); + return false; + } + + if (!mInfo) { + mInfo = new InputApplicationInfo(); + } + + jstring nameObj = jstring(env->GetObjectField(obj, + gInputApplicationHandleClassInfo.name)); + if (nameObj) { + const char* nameStr = env->GetStringUTFChars(nameObj, NULL); + mInfo->name.setTo(nameStr); + env->ReleaseStringUTFChars(nameObj, nameStr); + env->DeleteLocalRef(nameObj); + } else { + mInfo->name.setTo("<null>"); + } + + mInfo->dispatchingTimeout = env->GetLongField(obj, + gInputApplicationHandleClassInfo.dispatchingTimeoutNanos); + + env->DeleteLocalRef(obj); + return true; +} + + +// --- Global functions --- + +sp<InputApplicationHandle> android_server_InputApplicationHandle_getHandle( + JNIEnv* env, jobject inputApplicationHandleObj) { + if (!inputApplicationHandleObj) { + return NULL; + } + + AutoMutex _l(gHandleMutex); + + jlong ptr = env->GetLongField(inputApplicationHandleObj, gInputApplicationHandleClassInfo.ptr); + NativeInputApplicationHandle* handle; + if (ptr) { + handle = reinterpret_cast<NativeInputApplicationHandle*>(ptr); + } else { + jweak objWeak = env->NewWeakGlobalRef(inputApplicationHandleObj); + handle = new NativeInputApplicationHandle(objWeak); + handle->incStrong((void*)android_server_InputApplicationHandle_getHandle); + env->SetLongField(inputApplicationHandleObj, gInputApplicationHandleClassInfo.ptr, + reinterpret_cast<jlong>(handle)); + } + return handle; +} + + +// --- JNI --- + +static void android_server_InputApplicationHandle_nativeDispose(JNIEnv* env, jobject obj) { + AutoMutex _l(gHandleMutex); + + jlong ptr = env->GetLongField(obj, gInputApplicationHandleClassInfo.ptr); + if (ptr) { + env->SetLongField(obj, gInputApplicationHandleClassInfo.ptr, 0); + + NativeInputApplicationHandle* handle = reinterpret_cast<NativeInputApplicationHandle*>(ptr); + handle->decStrong((void*)android_server_InputApplicationHandle_getHandle); + } +} + + +static JNINativeMethod gInputApplicationHandleMethods[] = { + /* name, signature, funcPtr */ + { "nativeDispose", "()V", + (void*) android_server_InputApplicationHandle_nativeDispose }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_server_InputApplicationHandle(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/input/InputApplicationHandle", + gInputApplicationHandleMethods, NELEM(gInputApplicationHandleMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass clazz; + FIND_CLASS(clazz, "com/android/server/input/InputApplicationHandle"); + + GET_FIELD_ID(gInputApplicationHandleClassInfo.ptr, clazz, + "ptr", "J"); + + GET_FIELD_ID(gInputApplicationHandleClassInfo.name, clazz, + "name", "Ljava/lang/String;"); + + GET_FIELD_ID(gInputApplicationHandleClassInfo.dispatchingTimeoutNanos, + clazz, + "dispatchingTimeoutNanos", "J"); + + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.h b/services/core/jni/com_android_server_input_InputApplicationHandle.h new file mode 100644 index 0000000..89d48c6 --- /dev/null +++ b/services/core/jni/com_android_server_input_InputApplicationHandle.h @@ -0,0 +1,46 @@ +/* + * 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. + */ + +#ifndef _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H +#define _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H + +#include <input/InputApplication.h> + +#include "JNIHelp.h" +#include "jni.h" + +namespace android { + +class NativeInputApplicationHandle : public InputApplicationHandle { +public: + NativeInputApplicationHandle(jweak objWeak); + virtual ~NativeInputApplicationHandle(); + + jobject getInputApplicationHandleObjLocalRef(JNIEnv* env); + + virtual bool updateInfo(); + +private: + jweak mObjWeak; +}; + + +extern sp<InputApplicationHandle> android_server_InputApplicationHandle_getHandle( + JNIEnv* env, jobject inputApplicationHandleObj); + +} // namespace android + +#endif // _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp new file mode 100644 index 0000000..0542ce0 --- /dev/null +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -0,0 +1,1472 @@ +/* + * Copyright (C) 2010 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_TAG "InputManager-JNI" + +//#define LOG_NDEBUG 0 + +// Log debug messages about InputReaderPolicy +#define DEBUG_INPUT_READER_POLICY 0 + +// Log debug messages about InputDispatcherPolicy +#define DEBUG_INPUT_DISPATCHER_POLICY 0 + + +#include "JNIHelp.h" +#include "jni.h" +#include <limits.h> +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> + +#include <utils/Log.h> +#include <utils/Looper.h> +#include <utils/threads.h> + +#include <input/InputManager.h> +#include <input/PointerController.h> +#include <input/SpriteController.h> + +#include <android_os_MessageQueue.h> +#include <android_view_InputDevice.h> +#include <android_view_KeyEvent.h> +#include <android_view_MotionEvent.h> +#include <android_view_InputChannel.h> +#include <android_view_PointerIcon.h> +#include <android/graphics/GraphicsJNI.h> + +#include <ScopedLocalRef.h> +#include <ScopedUtfChars.h> + +#include "com_android_server_power_PowerManagerService.h" +#include "com_android_server_input_InputApplicationHandle.h" +#include "com_android_server_input_InputWindowHandle.h" + +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 notifyInputDevicesChanged; + jmethodID notifySwitch; + jmethodID notifyInputChannelBroken; + jmethodID notifyANR; + jmethodID filterInputEvent; + jmethodID interceptKeyBeforeQueueing; + jmethodID interceptMotionBeforeQueueingWhenScreenOff; + jmethodID interceptKeyBeforeDispatching; + jmethodID dispatchUnhandledKey; + jmethodID checkInjectEventsPermission; + jmethodID getVirtualKeyQuietTimeMillis; + jmethodID getExcludedDeviceNames; + jmethodID getKeyRepeatTimeout; + jmethodID getKeyRepeatDelay; + jmethodID getHoverTapTimeout; + jmethodID getHoverTapSlop; + jmethodID getDoubleTapTimeout; + jmethodID getLongPressTimeout; + jmethodID getPointerLayer; + jmethodID getPointerIcon; + jmethodID getKeyboardLayoutOverlay; + jmethodID getDeviceAlias; +} gServiceClassInfo; + +static struct { + jclass clazz; +} gInputDeviceClassInfo; + +static struct { + jclass clazz; +} gKeyEventClassInfo; + +static struct { + jclass clazz; +} gMotionEventClassInfo; + + +// --- 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) { + return NULL; + } + return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())-> + getInputApplicationHandleObjLocalRef(env); +} + +static jobject getInputWindowHandleObjLocalRef(JNIEnv* env, + const sp<InputWindowHandle>& inputWindowHandle) { + if (inputWindowHandle == NULL) { + return NULL; + } + return static_cast<NativeInputWindowHandle*>(inputWindowHandle.get())-> + getInputWindowHandleObjLocalRef(env); +} + +static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t style, + SpriteIcon* outSpriteIcon) { + PointerIcon pointerIcon; + status_t status = android_view_PointerIcon_loadSystemIcon(env, + contextObj, style, &pointerIcon); + if (!status) { + pointerIcon.bitmap.copyTo(&outSpriteIcon->bitmap, SkBitmap::kARGB_8888_Config); + outSpriteIcon->hotSpotX = pointerIcon.hotSpotX; + outSpriteIcon->hotSpotY = pointerIcon.hotSpotY; + } +} + +enum { + WM_ACTION_PASS_TO_USER = 1, + WM_ACTION_WAKE_UP = 2, + WM_ACTION_GO_TO_SLEEP = 4, +}; + + +// --- NativeInputManager --- + +class NativeInputManager : public virtual RefBase, + public virtual InputReaderPolicyInterface, + public virtual InputDispatcherPolicyInterface, + public virtual PointerControllerPolicyInterface { +protected: + virtual ~NativeInputManager(); + +public: + NativeInputManager(jobject contextObj, jobject serviceObj, const sp<Looper>& looper); + + inline sp<InputManager> getInputManager() const { return mInputManager; } + + void dump(String8& dump); + + void setDisplayViewport(bool external, const DisplayViewport& viewport); + + status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, + const sp<InputWindowHandle>& inputWindowHandle, bool monitor); + status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); + + void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray); + void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj); + void setInputDispatchMode(bool enabled, bool frozen); + void setSystemUiVisibility(int32_t visibility); + void setPointerSpeed(int32_t speed); + void setShowTouches(bool enabled); + + /* --- InputReaderPolicyInterface implementation --- */ + + virtual void getReaderConfiguration(InputReaderConfiguration* outConfig); + virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); + virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices); + virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor); + virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier); + + /* --- InputDispatcherPolicyInterface implementation --- */ + + virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, + uint32_t policyFlags); + virtual void notifyConfigurationChanged(nsecs_t when); + virtual nsecs_t notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<InputWindowHandle>& inputWindowHandle, + const String8& reason); + virtual void notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle); + 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 nsecs_t interceptKeyBeforeDispatching( + const sp<InputWindowHandle>& inputWindowHandle, + const KeyEvent* keyEvent, uint32_t policyFlags); + virtual bool dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle, + const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent); + virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType); + virtual bool checkInjectEventsPermissionNonReentrant( + int32_t injectorPid, int32_t injectorUid); + + /* --- PointerControllerPolicyInterface implementation --- */ + + virtual void loadPointerResources(PointerResources* outResources); + +private: + sp<InputManager> mInputManager; + + jobject mContextObj; + jobject mServiceObj; + sp<Looper> mLooper; + + Mutex mLock; + struct Locked { + // Display size information. + DisplayViewport internalViewport; + DisplayViewport externalViewport; + + // System UI visibility. + int32_t systemUiVisibility; + + // Pointer speed. + int32_t pointerSpeed; + + // True if pointer gestures are enabled. + bool pointerGesturesEnabled; + + // Show touches feature enable/disable. + bool showTouches; + + // Sprite controller singleton, created on first use. + sp<SpriteController> spriteController; + + // Pointer controller singleton, created and destroyed as needed. + wp<PointerController> pointerController; + } mLocked; + + void updateInactivityTimeoutLocked(const sp<PointerController>& controller); + void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags); + void ensureSpriteControllerLocked(); + + // Power manager interactions. + bool isScreenOn(); + bool isScreenBright(); + + static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName); + + static inline JNIEnv* jniEnv() { + return AndroidRuntime::getJNIEnv(); + } +}; + + + +NativeInputManager::NativeInputManager(jobject contextObj, + jobject serviceObj, const sp<Looper>& looper) : + mLooper(looper) { + JNIEnv* env = jniEnv(); + + mContextObj = env->NewGlobalRef(contextObj); + mServiceObj = env->NewGlobalRef(serviceObj); + + { + AutoMutex _l(mLock); + mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; + mLocked.pointerSpeed = 0; + mLocked.pointerGesturesEnabled = true; + mLocked.showTouches = false; + } + + sp<EventHub> eventHub = new EventHub(); + mInputManager = new InputManager(eventHub, this, this); +} + +NativeInputManager::~NativeInputManager() { + JNIEnv* env = jniEnv(); + + env->DeleteGlobalRef(mContextObj); + env->DeleteGlobalRef(mServiceObj); +} + +void NativeInputManager::dump(String8& dump) { + mInputManager->getReader()->dump(dump); + dump.append("\n"); + + mInputManager->getDispatcher()->dump(dump); + dump.append("\n"); +} + +bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + return true; + } + return false; +} + +void NativeInputManager::setDisplayViewport(bool external, const DisplayViewport& viewport) { + bool changed = false; + { + AutoMutex _l(mLock); + + DisplayViewport& v = external ? mLocked.externalViewport : mLocked.internalViewport; + if (v != viewport) { + changed = true; + v = viewport; + + if (!external) { + sp<PointerController> controller = mLocked.pointerController.promote(); + if (controller != NULL) { + controller->setDisplayViewport( + viewport.logicalRight - viewport.logicalLeft, + viewport.logicalBottom - viewport.logicalTop, + viewport.orientation); + } + } + } + } + + if (changed) { + mInputManager->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_DISPLAY_INFO); + } +} + +status_t NativeInputManager::registerInputChannel(JNIEnv* env, + const sp<InputChannel>& inputChannel, + const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { + return mInputManager->getDispatcher()->registerInputChannel( + inputChannel, inputWindowHandle, monitor); +} + +status_t NativeInputManager::unregisterInputChannel(JNIEnv* env, + const sp<InputChannel>& inputChannel) { + return mInputManager->getDispatcher()->unregisterInputChannel(inputChannel); +} + +void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outConfig) { + JNIEnv* env = jniEnv(); + + jint virtualKeyQuietTime = env->CallIntMethod(mServiceObj, + gServiceClassInfo.getVirtualKeyQuietTimeMillis); + if (!checkAndClearExceptionFromCallback(env, "getVirtualKeyQuietTimeMillis")) { + outConfig->virtualKeyQuietTime = milliseconds_to_nanoseconds(virtualKeyQuietTime); + } + + outConfig->excludedDeviceNames.clear(); + jobjectArray excludedDeviceNames = jobjectArray(env->CallObjectMethod(mServiceObj, + gServiceClassInfo.getExcludedDeviceNames)); + if (!checkAndClearExceptionFromCallback(env, "getExcludedDeviceNames") && excludedDeviceNames) { + jsize length = env->GetArrayLength(excludedDeviceNames); + for (jsize i = 0; i < length; i++) { + jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i)); + const char* deviceNameChars = env->GetStringUTFChars(item, NULL); + outConfig->excludedDeviceNames.add(String8(deviceNameChars)); + env->ReleaseStringUTFChars(item, deviceNameChars); + env->DeleteLocalRef(item); + } + env->DeleteLocalRef(excludedDeviceNames); + } + + jint hoverTapTimeout = env->CallIntMethod(mServiceObj, + gServiceClassInfo.getHoverTapTimeout); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) { + jint doubleTapTimeout = env->CallIntMethod(mServiceObj, + gServiceClassInfo.getDoubleTapTimeout); + if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) { + jint longPressTimeout = env->CallIntMethod(mServiceObj, + gServiceClassInfo.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(mServiceObj, + gServiceClassInfo.getHoverTapSlop); + if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) { + outConfig->pointerGestureTapSlop = hoverTapSlop; + } + + { // acquire lock + AutoMutex _l(mLock); + + outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed + * POINTER_SPEED_EXPONENT); + outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled; + + outConfig->showTouches = mLocked.showTouches; + + outConfig->setDisplayInfo(false /*external*/, mLocked.internalViewport); + outConfig->setDisplayInfo(true /*external*/, mLocked.externalViewport); + } // release lock +} + +sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32_t deviceId) { + AutoMutex _l(mLock); + + sp<PointerController> controller = mLocked.pointerController.promote(); + if (controller == NULL) { + ensureSpriteControllerLocked(); + + controller = new PointerController(this, mLooper, mLocked.spriteController); + mLocked.pointerController = controller; + + DisplayViewport& v = mLocked.internalViewport; + controller->setDisplayViewport( + v.logicalRight - v.logicalLeft, + v.logicalBottom - v.logicalTop, + v.orientation); + + JNIEnv* env = jniEnv(); + jobject pointerIconObj = env->CallObjectMethod(mServiceObj, + gServiceClassInfo.getPointerIcon); + if (!checkAndClearExceptionFromCallback(env, "getPointerIcon")) { + PointerIcon pointerIcon; + status_t status = android_view_PointerIcon_load(env, pointerIconObj, + mContextObj, &pointerIcon); + if (!status && !pointerIcon.isNullIcon()) { + controller->setPointerIcon(SpriteIcon(pointerIcon.bitmap, + pointerIcon.hotSpotX, pointerIcon.hotSpotY)); + } else { + controller->setPointerIcon(SpriteIcon()); + } + env->DeleteLocalRef(pointerIconObj); + } + + updateInactivityTimeoutLocked(controller); + } + return controller; +} + +void NativeInputManager::ensureSpriteControllerLocked() { + if (mLocked.spriteController == NULL) { + JNIEnv* env = jniEnv(); + jint layer = env->CallIntMethod(mServiceObj, gServiceClassInfo.getPointerLayer); + if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) { + layer = -1; + } + mLocked.spriteController = new SpriteController(mLooper, layer); + } +} + +void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) { + JNIEnv* env = jniEnv(); + + size_t count = inputDevices.size(); + jobjectArray inputDevicesObjArray = env->NewObjectArray( + count, gInputDeviceClassInfo.clazz, NULL); + if (inputDevicesObjArray) { + bool error = false; + for (size_t i = 0; i < count; i++) { + jobject inputDeviceObj = android_view_InputDevice_create(env, inputDevices.itemAt(i)); + if (!inputDeviceObj) { + error = true; + break; + } + + env->SetObjectArrayElement(inputDevicesObjArray, i, inputDeviceObj); + env->DeleteLocalRef(inputDeviceObj); + } + + if (!error) { + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputDevicesChanged, + inputDevicesObjArray); + } + + env->DeleteLocalRef(inputDevicesObjArray); + } + + checkAndClearExceptionFromCallback(env, "notifyInputDevicesChanged"); +} + +sp<KeyCharacterMap> NativeInputManager::getKeyboardLayoutOverlay( + const String8& inputDeviceDescriptor) { + JNIEnv* env = jniEnv(); + + sp<KeyCharacterMap> result; + ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string())); + ScopedLocalRef<jobjectArray> arrayObj(env, jobjectArray(env->CallObjectMethod(mServiceObj, + gServiceClassInfo.getKeyboardLayoutOverlay, descriptorObj.get()))); + if (arrayObj.get()) { + ScopedLocalRef<jstring> filenameObj(env, + jstring(env->GetObjectArrayElement(arrayObj.get(), 0))); + ScopedLocalRef<jstring> contentsObj(env, + jstring(env->GetObjectArrayElement(arrayObj.get(), 1))); + ScopedUtfChars filenameChars(env, filenameObj.get()); + ScopedUtfChars contentsChars(env, contentsObj.get()); + + KeyCharacterMap::loadContents(String8(filenameChars.c_str()), + String8(contentsChars.c_str()), KeyCharacterMap::FORMAT_OVERLAY, &result); + } + checkAndClearExceptionFromCallback(env, "getKeyboardLayoutOverlay"); + return result; +} + +String8 NativeInputManager::getDeviceAlias(const InputDeviceIdentifier& identifier) { + JNIEnv* env = jniEnv(); + + ScopedLocalRef<jstring> uniqueIdObj(env, env->NewStringUTF(identifier.uniqueId.string())); + ScopedLocalRef<jstring> aliasObj(env, jstring(env->CallObjectMethod(mServiceObj, + gServiceClassInfo.getDeviceAlias, uniqueIdObj.get()))); + String8 result; + if (aliasObj.get()) { + ScopedUtfChars aliasChars(env, aliasObj.get()); + result.setTo(aliasChars.c_str()); + } + checkAndClearExceptionFromCallback(env, "getDeviceAlias"); + return result; +} + +void NativeInputManager::notifySwitch(nsecs_t when, + uint32_t switchValues, uint32_t switchMask, uint32_t policyFlags) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("notifySwitch - when=%lld, switchValues=0x%08x, switchMask=0x%08x, policyFlags=0x%x", + when, switchValues, switchMask, policyFlags); +#endif + + JNIEnv* env = jniEnv(); + + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifySwitch, + when, switchValues, switchMask); + checkAndClearExceptionFromCallback(env, "notifySwitch"); +} + +void NativeInputManager::notifyConfigurationChanged(nsecs_t when) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("notifyConfigurationChanged - when=%lld", when); +#endif + + JNIEnv* env = jniEnv(); + + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyConfigurationChanged, when); + checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged"); +} + +nsecs_t NativeInputManager::notifyANR(const sp<InputApplicationHandle>& inputApplicationHandle, + const sp<InputWindowHandle>& inputWindowHandle, const String8& reason) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("notifyANR"); +#endif + + JNIEnv* env = jniEnv(); + + jobject inputApplicationHandleObj = + getInputApplicationHandleObjLocalRef(env, inputApplicationHandle); + jobject inputWindowHandleObj = + getInputWindowHandleObjLocalRef(env, inputWindowHandle); + jstring reasonObj = env->NewStringUTF(reason.string()); + + jlong newTimeout = env->CallLongMethod(mServiceObj, + gServiceClassInfo.notifyANR, inputApplicationHandleObj, inputWindowHandleObj, + reasonObj); + if (checkAndClearExceptionFromCallback(env, "notifyANR")) { + newTimeout = 0; // abort dispatch + } else { + assert(newTimeout >= 0); + } + + env->DeleteLocalRef(reasonObj); + env->DeleteLocalRef(inputWindowHandleObj); + env->DeleteLocalRef(inputApplicationHandleObj); + return newTimeout; +} + +void NativeInputManager::notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("notifyInputChannelBroken"); +#endif + + JNIEnv* env = jniEnv(); + + jobject inputWindowHandleObj = + getInputWindowHandleObjLocalRef(env, inputWindowHandle); + if (inputWindowHandleObj) { + env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputChannelBroken, + inputWindowHandleObj); + checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken"); + + env->DeleteLocalRef(inputWindowHandleObj); + } +} + +void NativeInputManager::getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) { + JNIEnv* env = jniEnv(); + + jint keyRepeatTimeout = env->CallIntMethod(mServiceObj, + gServiceClassInfo.getKeyRepeatTimeout); + if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatTimeout")) { + outConfig->keyRepeatTimeout = milliseconds_to_nanoseconds(keyRepeatTimeout); + } + + jint keyRepeatDelay = env->CallIntMethod(mServiceObj, + gServiceClassInfo.getKeyRepeatDelay); + if (!checkAndClearExceptionFromCallback(env, "getKeyRepeatDelay")) { + outConfig->keyRepeatDelay = milliseconds_to_nanoseconds(keyRepeatDelay); + } +} + +bool NativeInputManager::isKeyRepeatEnabled() { + // Only enable automatic key repeating when the screen is on. + return isScreenOn(); +} + +void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) { + Vector<sp<InputWindowHandle> > windowHandles; + + if (windowHandleObjArray) { + jsize length = env->GetArrayLength(windowHandleObjArray); + for (jsize i = 0; i < length; i++) { + jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i); + if (! windowHandleObj) { + break; // found null element indicating end of used portion of the array + } + + sp<InputWindowHandle> windowHandle = + android_server_InputWindowHandle_getHandle(env, windowHandleObj); + if (windowHandle != NULL) { + windowHandles.push(windowHandle); + } + env->DeleteLocalRef(windowHandleObj); + } + } + + mInputManager->getDispatcher()->setInputWindows(windowHandles); + + // Do this after the dispatcher has updated the window handle state. + bool newPointerGesturesEnabled = true; + size_t numWindows = windowHandles.size(); + for (size_t i = 0; i < numWindows; i++) { + const sp<InputWindowHandle>& windowHandle = windowHandles.itemAt(i); + const InputWindowInfo* windowInfo = windowHandle->getInfo(); + if (windowInfo && windowInfo->hasFocus && (windowInfo->inputFeatures + & InputWindowInfo::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES)) { + newPointerGesturesEnabled = false; + } + } + + uint32_t changes = 0; + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) { + mLocked.pointerGesturesEnabled = newPointerGesturesEnabled; + changes |= InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT; + } + } // release lock + + if (changes) { + mInputManager->getReader()->requestRefreshConfiguration(changes); + } +} + +void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationHandleObj) { + sp<InputApplicationHandle> applicationHandle = + android_server_InputApplicationHandle_getHandle(env, applicationHandleObj); + mInputManager->getDispatcher()->setFocusedApplication(applicationHandle); +} + +void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) { + mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen); +} + +void NativeInputManager::setSystemUiVisibility(int32_t visibility) { + AutoMutex _l(mLock); + + if (mLocked.systemUiVisibility != visibility) { + mLocked.systemUiVisibility = visibility; + + sp<PointerController> controller = mLocked.pointerController.promote(); + if (controller != NULL) { + updateInactivityTimeoutLocked(controller); + } + } +} + +void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) { + bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; + controller->setInactivityTimeout(lightsOut + ? PointerController::INACTIVITY_TIMEOUT_SHORT + : PointerController::INACTIVITY_TIMEOUT_NORMAL); +} + +void NativeInputManager::setPointerSpeed(int32_t speed) { + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.pointerSpeed == speed) { + return; + } + + ALOGI("Setting pointer speed to %d.", speed); + mLocked.pointerSpeed = speed; + } // release lock + + mInputManager->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_POINTER_SPEED); +} + +void NativeInputManager::setShowTouches(bool enabled) { + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.showTouches == enabled) { + return; + } + + ALOGI("Setting show touches feature to %s.", enabled ? "enabled" : "disabled"); + mLocked.showTouches = enabled; + } // release lock + + mInputManager->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_SHOW_TOUCHES); +} + +bool NativeInputManager::isScreenOn() { + return android_server_PowerManagerService_isScreenOn(); +} + +bool NativeInputManager::isScreenBright() { + return android_server_PowerManagerService_isScreenBright(); +} + +bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) { + jobject inputEventObj; + + JNIEnv* env = jniEnv(); + switch (inputEvent->getType()) { + case AINPUT_EVENT_TYPE_KEY: + inputEventObj = android_view_KeyEvent_fromNative(env, + static_cast<const KeyEvent*>(inputEvent)); + break; + case AINPUT_EVENT_TYPE_MOTION: + inputEventObj = android_view_MotionEvent_obtainAsCopy(env, + static_cast<const MotionEvent*>(inputEvent)); + break; + default: + return true; // dispatch the event normally + } + + if (!inputEventObj) { + ALOGE("Failed to obtain input event object for filterInputEvent."); + return true; // dispatch the event normally + } + + // The callee is responsible for recycling the event. + jboolean pass = env->CallBooleanMethod(mServiceObj, gServiceClassInfo.filterInputEvent, + inputEventObj, policyFlags); + if (checkAndClearExceptionFromCallback(env, "filterInputEvent")) { + pass = true; + } + env->DeleteLocalRef(inputEventObj); + return pass; +} + +void NativeInputManager::interceptKeyBeforeQueueing(const KeyEvent* keyEvent, + uint32_t& policyFlags) { + // Policy: + // - Ignore untrusted events and pass them along. + // - Ask the window manager what to do with normal events and trusted injected events. + // - For normal events wake and brighten the screen if currently off or dim. + if ((policyFlags & POLICY_FLAG_TRUSTED)) { + nsecs_t when = keyEvent->getEventTime(); + bool isScreenOn = this->isScreenOn(); + bool isScreenBright = this->isScreenBright(); + + JNIEnv* env = jniEnv(); + jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); + jint wmActions; + if (keyEventObj) { + wmActions = env->CallIntMethod(mServiceObj, + gServiceClassInfo.interceptKeyBeforeQueueing, + keyEventObj, policyFlags, isScreenOn); + if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeQueueing")) { + wmActions = 0; + } + android_view_KeyEvent_recycle(env, keyEventObj); + env->DeleteLocalRef(keyEventObj); + } else { + ALOGE("Failed to obtain key event object for interceptKeyBeforeQueueing."); + wmActions = 0; + } + + if (!(policyFlags & POLICY_FLAG_INJECTED)) { + if (!isScreenOn) { + policyFlags |= POLICY_FLAG_WOKE_HERE; + } + + if (!isScreenBright) { + policyFlags |= POLICY_FLAG_BRIGHT_HERE; + } + } + + handleInterceptActions(wmActions, when, /*byref*/ policyFlags); + } else { + policyFlags |= POLICY_FLAG_PASS_TO_USER; + } +} + +void NativeInputManager::interceptMotionBeforeQueueing(nsecs_t when, uint32_t& policyFlags) { + // Policy: + // - Ignore untrusted events and pass them along. + // - No special filtering for injected events required at this time. + // - Filter normal events based on screen state. + // - For normal events brighten (but do not wake) the screen if currently dim. + if ((policyFlags & POLICY_FLAG_TRUSTED) && !(policyFlags & POLICY_FLAG_INJECTED)) { + if (isScreenOn()) { + policyFlags |= POLICY_FLAG_PASS_TO_USER; + + if (!isScreenBright()) { + policyFlags |= POLICY_FLAG_BRIGHT_HERE; + } + } else { + JNIEnv* env = jniEnv(); + jint wmActions = env->CallIntMethod(mServiceObj, + gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff, + policyFlags); + if (checkAndClearExceptionFromCallback(env, + "interceptMotionBeforeQueueingWhenScreenOff")) { + wmActions = 0; + } + + policyFlags |= POLICY_FLAG_WOKE_HERE | POLICY_FLAG_BRIGHT_HERE; + handleInterceptActions(wmActions, when, /*byref*/ policyFlags); + } + } else { + policyFlags |= POLICY_FLAG_PASS_TO_USER; + } +} + +void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when, + uint32_t& policyFlags) { + if (wmActions & WM_ACTION_GO_TO_SLEEP) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("handleInterceptActions: Going to sleep."); +#endif + android_server_PowerManagerService_goToSleep(when); + } + + if (wmActions & WM_ACTION_WAKE_UP) { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("handleInterceptActions: Waking up."); +#endif + android_server_PowerManagerService_wakeUp(when); + } + + if (wmActions & WM_ACTION_PASS_TO_USER) { + policyFlags |= POLICY_FLAG_PASS_TO_USER; + } else { +#if DEBUG_INPUT_DISPATCHER_POLICY + ALOGD("handleInterceptActions: Not passing key to user."); +#endif + } +} + +nsecs_t NativeInputManager::interceptKeyBeforeDispatching( + const sp<InputWindowHandle>& inputWindowHandle, + const KeyEvent* keyEvent, uint32_t policyFlags) { + // Policy: + // - Ignore untrusted events and pass them along. + // - Filter normal events and trusted injected events through the window manager policy to + // handle the HOME key and the like. + nsecs_t result = 0; + if (policyFlags & POLICY_FLAG_TRUSTED) { + JNIEnv* env = jniEnv(); + + // Note: inputWindowHandle may be null. + jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle); + jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); + if (keyEventObj) { + jlong delayMillis = env->CallLongMethod(mServiceObj, + gServiceClassInfo.interceptKeyBeforeDispatching, + inputWindowHandleObj, keyEventObj, policyFlags); + bool error = checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching"); + android_view_KeyEvent_recycle(env, keyEventObj); + env->DeleteLocalRef(keyEventObj); + if (!error) { + if (delayMillis < 0) { + result = -1; + } else if (delayMillis > 0) { + result = milliseconds_to_nanoseconds(delayMillis); + } + } + } else { + ALOGE("Failed to obtain key event object for interceptKeyBeforeDispatching."); + } + env->DeleteLocalRef(inputWindowHandleObj); + } + return result; +} + +bool NativeInputManager::dispatchUnhandledKey(const sp<InputWindowHandle>& inputWindowHandle, + const KeyEvent* keyEvent, uint32_t policyFlags, KeyEvent* outFallbackKeyEvent) { + // Policy: + // - Ignore untrusted events and do not perform default handling. + bool result = false; + if (policyFlags & POLICY_FLAG_TRUSTED) { + JNIEnv* env = jniEnv(); + + // Note: inputWindowHandle may be null. + jobject inputWindowHandleObj = getInputWindowHandleObjLocalRef(env, inputWindowHandle); + jobject keyEventObj = android_view_KeyEvent_fromNative(env, keyEvent); + if (keyEventObj) { + jobject fallbackKeyEventObj = env->CallObjectMethod(mServiceObj, + gServiceClassInfo.dispatchUnhandledKey, + inputWindowHandleObj, keyEventObj, policyFlags); + if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) { + fallbackKeyEventObj = NULL; + } + android_view_KeyEvent_recycle(env, keyEventObj); + env->DeleteLocalRef(keyEventObj); + + if (fallbackKeyEventObj) { + // Note: outFallbackKeyEvent may be the same object as keyEvent. + if (!android_view_KeyEvent_toNative(env, fallbackKeyEventObj, + outFallbackKeyEvent)) { + result = true; + } + android_view_KeyEvent_recycle(env, fallbackKeyEventObj); + env->DeleteLocalRef(fallbackKeyEventObj); + } + } else { + ALOGE("Failed to obtain key event object for dispatchUnhandledKey."); + } + env->DeleteLocalRef(inputWindowHandleObj); + } + return result; +} + +void NativeInputManager::pokeUserActivity(nsecs_t eventTime, int32_t eventType) { + android_server_PowerManagerService_userActivity(eventTime, eventType); +} + + +bool NativeInputManager::checkInjectEventsPermissionNonReentrant( + int32_t injectorPid, int32_t injectorUid) { + JNIEnv* env = jniEnv(); + jboolean result = env->CallBooleanMethod(mServiceObj, + gServiceClassInfo.checkInjectEventsPermission, injectorPid, injectorUid); + if (checkAndClearExceptionFromCallback(env, "checkInjectEventsPermission")) { + result = false; + } + return result; +} + +void NativeInputManager::loadPointerResources(PointerResources* outResources) { + JNIEnv* env = jniEnv(); + + loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_HOVER, + &outResources->spotHover); + loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_TOUCH, + &outResources->spotTouch); + loadSystemIconAsSprite(env, mContextObj, POINTER_ICON_STYLE_SPOT_ANCHOR, + &outResources->spotAnchor); +} + + +// ---------------------------------------------------------------------------- + +static jlong nativeInit(JNIEnv* env, jclass clazz, + jobject serviceObj, jobject contextObj, jobject messageQueueObj) { + sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); + if (messageQueue == NULL) { + jniThrowRuntimeException(env, "MessageQueue is not initialized."); + return 0; + } + + NativeInputManager* im = new NativeInputManager(contextObj, serviceObj, + messageQueue->getLooper()); + im->incStrong(0); + return reinterpret_cast<jlong>(im); +} + +static void nativeStart(JNIEnv* env, jclass clazz, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + status_t result = im->getInputManager()->start(); + if (result) { + jniThrowRuntimeException(env, "Input manager could not be started."); + } +} + +static void nativeSetDisplayViewport(JNIEnv* env, jclass clazz, jlong ptr, jboolean external, + jint displayId, jint orientation, + jint logicalLeft, jint logicalTop, jint logicalRight, jint logicalBottom, + jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom, + jint deviceWidth, jint deviceHeight) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + DisplayViewport v; + v.displayId = displayId; + v.orientation = orientation; + v.logicalLeft = logicalLeft; + v.logicalTop = logicalTop; + v.logicalRight = logicalRight; + v.logicalBottom = logicalBottom; + v.physicalLeft = physicalLeft; + v.physicalTop = physicalTop; + v.physicalRight = physicalRight; + v.physicalBottom = physicalBottom; + v.deviceWidth = deviceWidth; + v.deviceHeight = deviceHeight; + im->setDisplayViewport(external, v); +} + +static jint nativeGetScanCodeState(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint sourceMask, jint scanCode) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + return (jint) im->getInputManager()->getReader()->getScanCodeState( + deviceId, uint32_t(sourceMask), scanCode); +} + +static jint nativeGetKeyCodeState(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint sourceMask, jint keyCode) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + return (jint) im->getInputManager()->getReader()->getKeyCodeState( + deviceId, uint32_t(sourceMask), keyCode); +} + +static jint nativeGetSwitchState(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint sourceMask, jint sw) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + return (jint) im->getInputManager()->getReader()->getSwitchState( + deviceId, uint32_t(sourceMask), sw); +} + +static jboolean nativeHasKeys(JNIEnv* env, jclass clazz, + jlong ptr, jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + int32_t* codes = env->GetIntArrayElements(keyCodes, NULL); + uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL); + jsize numCodes = env->GetArrayLength(keyCodes); + jboolean result; + if (numCodes == env->GetArrayLength(keyCodes)) { + if (im->getInputManager()->getReader()->hasKeys( + deviceId, uint32_t(sourceMask), numCodes, codes, flags)) { + result = JNI_TRUE; + } else { + result = JNI_FALSE; + } + } else { + result = JNI_FALSE; + } + + env->ReleaseBooleanArrayElements(outFlags, flags, 0); + env->ReleaseIntArrayElements(keyCodes, codes, 0); + return result; +} + +static void throwInputChannelNotInitialized(JNIEnv* env) { + jniThrowException(env, "java/lang/IllegalStateException", + "inputChannel is not initialized"); +} + +static void handleInputChannelDisposed(JNIEnv* env, + jobject inputChannelObj, const sp<InputChannel>& inputChannel, void* data) { + NativeInputManager* im = static_cast<NativeInputManager*>(data); + + ALOGW("Input channel object '%s' was disposed without first being unregistered with " + "the input manager!", inputChannel->getName().string()); + im->unregisterInputChannel(env, inputChannel); +} + +static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz, + jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, + inputChannelObj); + if (inputChannel == NULL) { + throwInputChannelNotInitialized(env); + return; + } + + sp<InputWindowHandle> inputWindowHandle = + android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj); + + status_t status = im->registerInputChannel( + env, inputChannel, inputWindowHandle, monitor); + if (status) { + String8 message; + message.appendFormat("Failed to register input channel. status=%d", status); + jniThrowRuntimeException(env, message.string()); + return; + } + + if (! monitor) { + android_view_InputChannel_setDisposeCallback(env, inputChannelObj, + handleInputChannelDisposed, im); + } +} + +static void nativeUnregisterInputChannel(JNIEnv* env, jclass clazz, + jlong ptr, jobject inputChannelObj) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, + inputChannelObj); + if (inputChannel == NULL) { + throwInputChannelNotInitialized(env); + return; + } + + android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); + + status_t status = im->unregisterInputChannel(env, inputChannel); + if (status && status != BAD_VALUE) { // ignore already unregistered channel + String8 message; + message.appendFormat("Failed to unregister input channel. status=%d", status); + jniThrowRuntimeException(env, message.string()); + } +} + +static void nativeSetInputFilterEnabled(JNIEnv* env, jclass clazz, + jlong ptr, jboolean enabled) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getDispatcher()->setInputFilterEnabled(enabled); +} + +static jint nativeInjectInputEvent(JNIEnv* env, jclass clazz, + jlong ptr, jobject inputEventObj, jint displayId, jint injectorPid, jint injectorUid, + jint syncMode, jint timeoutMillis, jint policyFlags) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + if (env->IsInstanceOf(inputEventObj, gKeyEventClassInfo.clazz)) { + KeyEvent keyEvent; + status_t status = android_view_KeyEvent_toNative(env, inputEventObj, & keyEvent); + if (status) { + jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); + return INPUT_EVENT_INJECTION_FAILED; + } + + return (jint) im->getInputManager()->getDispatcher()->injectInputEvent( + & keyEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis, + uint32_t(policyFlags)); + } else if (env->IsInstanceOf(inputEventObj, gMotionEventClassInfo.clazz)) { + const MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, inputEventObj); + if (!motionEvent) { + jniThrowRuntimeException(env, "Could not read contents of MotionEvent object."); + return INPUT_EVENT_INJECTION_FAILED; + } + + return (jint) im->getInputManager()->getDispatcher()->injectInputEvent( + motionEvent, displayId, injectorPid, injectorUid, syncMode, timeoutMillis, + uint32_t(policyFlags)); + } else { + jniThrowRuntimeException(env, "Invalid input event type."); + return INPUT_EVENT_INJECTION_FAILED; + } +} + +static void nativeSetInputWindows(JNIEnv* env, jclass clazz, + jlong ptr, jobjectArray windowHandleObjArray) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->setInputWindows(env, windowHandleObjArray); +} + +static void nativeSetFocusedApplication(JNIEnv* env, jclass clazz, + jlong ptr, jobject applicationHandleObj) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->setFocusedApplication(env, applicationHandleObj); +} + +static void nativeSetInputDispatchMode(JNIEnv* env, + jclass clazz, jlong ptr, jboolean enabled, jboolean frozen) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->setInputDispatchMode(enabled, frozen); +} + +static void nativeSetSystemUiVisibility(JNIEnv* env, + jclass clazz, jlong ptr, jint visibility) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->setSystemUiVisibility(visibility); +} + +static jboolean nativeTransferTouchFocus(JNIEnv* env, + jclass clazz, jlong ptr, jobject fromChannelObj, jobject toChannelObj) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + sp<InputChannel> fromChannel = + android_view_InputChannel_getInputChannel(env, fromChannelObj); + sp<InputChannel> toChannel = + android_view_InputChannel_getInputChannel(env, toChannelObj); + + if (fromChannel == NULL || toChannel == NULL) { + return JNI_FALSE; + } + + if (im->getInputManager()->getDispatcher()-> + transferTouchFocus(fromChannel, toChannel)) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} + +static void nativeSetPointerSpeed(JNIEnv* env, + jclass clazz, jlong ptr, jint speed) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->setPointerSpeed(speed); +} + +static void nativeSetShowTouches(JNIEnv* env, + jclass clazz, jlong ptr, jboolean enabled) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->setShowTouches(enabled); +} + +static void nativeVibrate(JNIEnv* env, + jclass clazz, jlong ptr, jint deviceId, jlongArray patternObj, + jint repeat, jint token) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + size_t patternSize = env->GetArrayLength(patternObj); + if (patternSize > MAX_VIBRATE_PATTERN_SIZE) { + ALOGI("Skipped requested vibration because the pattern size is %d " + "which is more than the maximum supported size of %d.", + patternSize, MAX_VIBRATE_PATTERN_SIZE); + return; // limit to reasonable size + } + + jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical( + patternObj, NULL)); + nsecs_t pattern[patternSize]; + for (size_t i = 0; i < patternSize; i++) { + pattern[i] = max(jlong(0), min(patternMillis[i], + (jlong)(MAX_VIBRATE_PATTERN_DELAY_NSECS / 1000000LL))) * 1000000LL; + } + env->ReleasePrimitiveArrayCritical(patternObj, patternMillis, JNI_ABORT); + + im->getInputManager()->getReader()->vibrate(deviceId, pattern, patternSize, repeat, token); +} + +static void nativeCancelVibrate(JNIEnv* env, + jclass clazz, jlong ptr, jint deviceId, jint token) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getReader()->cancelVibrate(deviceId, token); +} + +static void nativeReloadKeyboardLayouts(JNIEnv* env, + jclass clazz, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS); +} + +static void nativeReloadDeviceAliases(JNIEnv* env, + jclass clazz, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_DEVICE_ALIAS); +} + +static jstring nativeDump(JNIEnv* env, jclass clazz, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + String8 dump; + im->dump(dump); + return env->NewStringUTF(dump.string()); +} + +static void nativeMonitor(JNIEnv* env, jclass clazz, jlong ptr) { + NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); + + im->getInputManager()->getReader()->monitor(); + im->getInputManager()->getDispatcher()->monitor(); +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gInputManagerMethods[] = { + /* name, signature, funcPtr */ + { "nativeInit", + "(Lcom/android/server/input/InputManagerService;Landroid/content/Context;Landroid/os/MessageQueue;)J", + (void*) nativeInit }, + { "nativeStart", "(J)V", + (void*) nativeStart }, + { "nativeSetDisplayViewport", "(JZIIIIIIIIIIII)V", + (void*) nativeSetDisplayViewport }, + { "nativeGetScanCodeState", "(JIII)I", + (void*) nativeGetScanCodeState }, + { "nativeGetKeyCodeState", "(JIII)I", + (void*) nativeGetKeyCodeState }, + { "nativeGetSwitchState", "(JIII)I", + (void*) nativeGetSwitchState }, + { "nativeHasKeys", "(JII[I[Z)Z", + (void*) nativeHasKeys }, + { "nativeRegisterInputChannel", + "(JLandroid/view/InputChannel;Lcom/android/server/input/InputWindowHandle;Z)V", + (void*) nativeRegisterInputChannel }, + { "nativeUnregisterInputChannel", "(JLandroid/view/InputChannel;)V", + (void*) nativeUnregisterInputChannel }, + { "nativeSetInputFilterEnabled", "(JZ)V", + (void*) nativeSetInputFilterEnabled }, + { "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIIII)I", + (void*) nativeInjectInputEvent }, + { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V", + (void*) nativeSetInputWindows }, + { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V", + (void*) nativeSetFocusedApplication }, + { "nativeSetInputDispatchMode", "(JZZ)V", + (void*) nativeSetInputDispatchMode }, + { "nativeSetSystemUiVisibility", "(JI)V", + (void*) nativeSetSystemUiVisibility }, + { "nativeTransferTouchFocus", "(JLandroid/view/InputChannel;Landroid/view/InputChannel;)Z", + (void*) nativeTransferTouchFocus }, + { "nativeSetPointerSpeed", "(JI)V", + (void*) nativeSetPointerSpeed }, + { "nativeSetShowTouches", "(JZ)V", + (void*) nativeSetShowTouches }, + { "nativeVibrate", "(JI[JII)V", + (void*) nativeVibrate }, + { "nativeCancelVibrate", "(JII)V", + (void*) nativeCancelVibrate }, + { "nativeReloadKeyboardLayouts", "(J)V", + (void*) nativeReloadKeyboardLayouts }, + { "nativeReloadDeviceAliases", "(J)V", + (void*) nativeReloadDeviceAliases }, + { "nativeDump", "(J)Ljava/lang/String;", + (void*) nativeDump }, + { "nativeMonitor", "(J)V", + (void*) nativeMonitor }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); + +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_server_InputManager(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/input/InputManagerService", + gInputManagerMethods, NELEM(gInputManagerMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + // Callbacks + + jclass clazz; + FIND_CLASS(clazz, "com/android/server/input/InputManagerService"); + + GET_METHOD_ID(gServiceClassInfo.notifyConfigurationChanged, clazz, + "notifyConfigurationChanged", "(J)V"); + + GET_METHOD_ID(gServiceClassInfo.notifyInputDevicesChanged, clazz, + "notifyInputDevicesChanged", "([Landroid/view/InputDevice;)V"); + + GET_METHOD_ID(gServiceClassInfo.notifySwitch, clazz, + "notifySwitch", "(JII)V"); + + GET_METHOD_ID(gServiceClassInfo.notifyInputChannelBroken, clazz, + "notifyInputChannelBroken", "(Lcom/android/server/input/InputWindowHandle;)V"); + + GET_METHOD_ID(gServiceClassInfo.notifyANR, clazz, + "notifyANR", + "(Lcom/android/server/input/InputApplicationHandle;Lcom/android/server/input/InputWindowHandle;Ljava/lang/String;)J"); + + GET_METHOD_ID(gServiceClassInfo.filterInputEvent, clazz, + "filterInputEvent", "(Landroid/view/InputEvent;I)Z"); + + GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeQueueing, clazz, + "interceptKeyBeforeQueueing", "(Landroid/view/KeyEvent;IZ)I"); + + GET_METHOD_ID(gServiceClassInfo.interceptMotionBeforeQueueingWhenScreenOff, + clazz, + "interceptMotionBeforeQueueingWhenScreenOff", "(I)I"); + + GET_METHOD_ID(gServiceClassInfo.interceptKeyBeforeDispatching, clazz, + "interceptKeyBeforeDispatching", + "(Lcom/android/server/input/InputWindowHandle;Landroid/view/KeyEvent;I)J"); + + GET_METHOD_ID(gServiceClassInfo.dispatchUnhandledKey, clazz, + "dispatchUnhandledKey", + "(Lcom/android/server/input/InputWindowHandle;Landroid/view/KeyEvent;I)Landroid/view/KeyEvent;"); + + GET_METHOD_ID(gServiceClassInfo.checkInjectEventsPermission, clazz, + "checkInjectEventsPermission", "(II)Z"); + + GET_METHOD_ID(gServiceClassInfo.getVirtualKeyQuietTimeMillis, clazz, + "getVirtualKeyQuietTimeMillis", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getExcludedDeviceNames, clazz, + "getExcludedDeviceNames", "()[Ljava/lang/String;"); + + GET_METHOD_ID(gServiceClassInfo.getKeyRepeatTimeout, clazz, + "getKeyRepeatTimeout", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getKeyRepeatDelay, clazz, + "getKeyRepeatDelay", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getHoverTapTimeout, clazz, + "getHoverTapTimeout", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getHoverTapSlop, clazz, + "getHoverTapSlop", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getDoubleTapTimeout, clazz, + "getDoubleTapTimeout", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getLongPressTimeout, clazz, + "getLongPressTimeout", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getPointerLayer, clazz, + "getPointerLayer", "()I"); + + GET_METHOD_ID(gServiceClassInfo.getPointerIcon, clazz, + "getPointerIcon", "()Landroid/view/PointerIcon;"); + + GET_METHOD_ID(gServiceClassInfo.getKeyboardLayoutOverlay, clazz, + "getKeyboardLayoutOverlay", "(Ljava/lang/String;)[Ljava/lang/String;"); + + GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz, + "getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;"); + + // InputDevice + + FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice"); + gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz)); + + // KeyEvent + + FIND_CLASS(gKeyEventClassInfo.clazz, "android/view/KeyEvent"); + gKeyEventClassInfo.clazz = jclass(env->NewGlobalRef(gKeyEventClassInfo.clazz)); + + // MotionEvent + + FIND_CLASS(gMotionEventClassInfo.clazz, "android/view/MotionEvent"); + gMotionEventClassInfo.clazz = jclass(env->NewGlobalRef(gMotionEventClassInfo.clazz)); + + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/services/core/jni/com_android_server_input_InputWindowHandle.cpp new file mode 100644 index 0000000..b80183c --- /dev/null +++ b/services/core/jni/com_android_server_input_InputWindowHandle.cpp @@ -0,0 +1,309 @@ +/* + * 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_TAG "InputWindowHandle" + +#include "JNIHelp.h" +#include "jni.h" +#include <android_runtime/AndroidRuntime.h> +#include <utils/threads.h> + +#include <android_view_InputChannel.h> +#include <android/graphics/Region.h> + +#include "com_android_server_input_InputWindowHandle.h" +#include "com_android_server_input_InputApplicationHandle.h" + +namespace android { + +static struct { + jfieldID ptr; + jfieldID inputApplicationHandle; + jfieldID inputChannel; + jfieldID name; + jfieldID layoutParamsFlags; + jfieldID layoutParamsPrivateFlags; + jfieldID layoutParamsType; + jfieldID dispatchingTimeoutNanos; + jfieldID frameLeft; + jfieldID frameTop; + jfieldID frameRight; + jfieldID frameBottom; + jfieldID scaleFactor; + jfieldID touchableRegion; + jfieldID visible; + jfieldID canReceiveKeys; + jfieldID hasFocus; + jfieldID hasWallpaper; + jfieldID paused; + jfieldID layer; + jfieldID ownerPid; + jfieldID ownerUid; + jfieldID inputFeatures; + jfieldID displayId; +} gInputWindowHandleClassInfo; + +static Mutex gHandleMutex; + + +// --- NativeInputWindowHandle --- + +NativeInputWindowHandle::NativeInputWindowHandle( + const sp<InputApplicationHandle>& inputApplicationHandle, jweak objWeak) : + InputWindowHandle(inputApplicationHandle), + mObjWeak(objWeak) { +} + +NativeInputWindowHandle::~NativeInputWindowHandle() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->DeleteWeakGlobalRef(mObjWeak); +} + +jobject NativeInputWindowHandle::getInputWindowHandleObjLocalRef(JNIEnv* env) { + return env->NewLocalRef(mObjWeak); +} + +bool NativeInputWindowHandle::updateInfo() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jobject obj = env->NewLocalRef(mObjWeak); + if (!obj) { + releaseInfo(); + return false; + } + + if (!mInfo) { + mInfo = new InputWindowInfo(); + } + + jobject inputChannelObj = env->GetObjectField(obj, + gInputWindowHandleClassInfo.inputChannel); + if (inputChannelObj) { + mInfo->inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); + env->DeleteLocalRef(inputChannelObj); + } else { + mInfo->inputChannel.clear(); + } + + jstring nameObj = jstring(env->GetObjectField(obj, + gInputWindowHandleClassInfo.name)); + if (nameObj) { + const char* nameStr = env->GetStringUTFChars(nameObj, NULL); + mInfo->name.setTo(nameStr); + env->ReleaseStringUTFChars(nameObj, nameStr); + env->DeleteLocalRef(nameObj); + } else { + mInfo->name.setTo("<null>"); + } + + mInfo->layoutParamsFlags = env->GetIntField(obj, + gInputWindowHandleClassInfo.layoutParamsFlags); + mInfo->layoutParamsPrivateFlags = env->GetIntField(obj, + gInputWindowHandleClassInfo.layoutParamsPrivateFlags); + mInfo->layoutParamsType = env->GetIntField(obj, + gInputWindowHandleClassInfo.layoutParamsType); + mInfo->dispatchingTimeout = env->GetLongField(obj, + gInputWindowHandleClassInfo.dispatchingTimeoutNanos); + mInfo->frameLeft = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameLeft); + mInfo->frameTop = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameTop); + mInfo->frameRight = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameRight); + mInfo->frameBottom = env->GetIntField(obj, + gInputWindowHandleClassInfo.frameBottom); + mInfo->scaleFactor = env->GetFloatField(obj, + gInputWindowHandleClassInfo.scaleFactor); + + jobject regionObj = env->GetObjectField(obj, + gInputWindowHandleClassInfo.touchableRegion); + if (regionObj) { + SkRegion* region = android_graphics_Region_getSkRegion(env, regionObj); + mInfo->touchableRegion.set(*region); + env->DeleteLocalRef(regionObj); + } else { + mInfo->touchableRegion.setEmpty(); + } + + mInfo->visible = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.visible); + mInfo->canReceiveKeys = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.canReceiveKeys); + mInfo->hasFocus = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.hasFocus); + mInfo->hasWallpaper = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.hasWallpaper); + mInfo->paused = env->GetBooleanField(obj, + gInputWindowHandleClassInfo.paused); + mInfo->layer = env->GetIntField(obj, + gInputWindowHandleClassInfo.layer); + mInfo->ownerPid = env->GetIntField(obj, + gInputWindowHandleClassInfo.ownerPid); + mInfo->ownerUid = env->GetIntField(obj, + gInputWindowHandleClassInfo.ownerUid); + mInfo->inputFeatures = env->GetIntField(obj, + gInputWindowHandleClassInfo.inputFeatures); + mInfo->displayId = env->GetIntField(obj, + gInputWindowHandleClassInfo.displayId); + + env->DeleteLocalRef(obj); + return true; +} + + +// --- Global functions --- + +sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle( + JNIEnv* env, jobject inputWindowHandleObj) { + if (!inputWindowHandleObj) { + return NULL; + } + + AutoMutex _l(gHandleMutex); + + jlong ptr = env->GetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr); + NativeInputWindowHandle* handle; + if (ptr) { + handle = reinterpret_cast<NativeInputWindowHandle*>(ptr); + } else { + jobject inputApplicationHandleObj = env->GetObjectField(inputWindowHandleObj, + gInputWindowHandleClassInfo.inputApplicationHandle); + sp<InputApplicationHandle> inputApplicationHandle = + android_server_InputApplicationHandle_getHandle(env, inputApplicationHandleObj); + env->DeleteLocalRef(inputApplicationHandleObj); + + jweak objWeak = env->NewWeakGlobalRef(inputWindowHandleObj); + handle = new NativeInputWindowHandle(inputApplicationHandle, objWeak); + handle->incStrong((void*)android_server_InputWindowHandle_getHandle); + env->SetLongField(inputWindowHandleObj, gInputWindowHandleClassInfo.ptr, + reinterpret_cast<jlong>(handle)); + } + return handle; +} + + +// --- JNI --- + +static void android_server_InputWindowHandle_nativeDispose(JNIEnv* env, jobject obj) { + AutoMutex _l(gHandleMutex); + + jlong ptr = env->GetLongField(obj, gInputWindowHandleClassInfo.ptr); + if (ptr) { + env->SetLongField(obj, gInputWindowHandleClassInfo.ptr, 0); + + NativeInputWindowHandle* handle = reinterpret_cast<NativeInputWindowHandle*>(ptr); + handle->decStrong((void*)android_server_InputWindowHandle_getHandle); + } +} + + +static JNINativeMethod gInputWindowHandleMethods[] = { + /* name, signature, funcPtr */ + { "nativeDispose", "()V", + (void*) android_server_InputWindowHandle_nativeDispose }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_server_InputWindowHandle(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/input/InputWindowHandle", + gInputWindowHandleMethods, NELEM(gInputWindowHandleMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + jclass clazz; + FIND_CLASS(clazz, "com/android/server/input/InputWindowHandle"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.ptr, clazz, + "ptr", "J"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.inputApplicationHandle, + clazz, + "inputApplicationHandle", "Lcom/android/server/input/InputApplicationHandle;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.inputChannel, clazz, + "inputChannel", "Landroid/view/InputChannel;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.name, clazz, + "name", "Ljava/lang/String;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsFlags, clazz, + "layoutParamsFlags", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsPrivateFlags, clazz, + "layoutParamsPrivateFlags", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.layoutParamsType, clazz, + "layoutParamsType", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.dispatchingTimeoutNanos, clazz, + "dispatchingTimeoutNanos", "J"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameLeft, clazz, + "frameLeft", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameTop, clazz, + "frameTop", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameRight, clazz, + "frameRight", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.frameBottom, clazz, + "frameBottom", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.scaleFactor, clazz, + "scaleFactor", "F"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.touchableRegion, clazz, + "touchableRegion", "Landroid/graphics/Region;"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.visible, clazz, + "visible", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.canReceiveKeys, clazz, + "canReceiveKeys", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.hasFocus, clazz, + "hasFocus", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.hasWallpaper, clazz, + "hasWallpaper", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.paused, clazz, + "paused", "Z"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.layer, clazz, + "layer", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.ownerPid, clazz, + "ownerPid", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.ownerUid, clazz, + "ownerUid", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.inputFeatures, clazz, + "inputFeatures", "I"); + + GET_FIELD_ID(gInputWindowHandleClassInfo.displayId, clazz, + "displayId", "I"); + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.h b/services/core/jni/com_android_server_input_InputWindowHandle.h new file mode 100644 index 0000000..2cfa17d3 --- /dev/null +++ b/services/core/jni/com_android_server_input_InputWindowHandle.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#ifndef _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H +#define _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H + +#include <input/InputWindow.h> + +#include "JNIHelp.h" +#include "jni.h" + +namespace android { + +class NativeInputWindowHandle : public InputWindowHandle { +public: + NativeInputWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle, + jweak objWeak); + virtual ~NativeInputWindowHandle(); + + jobject getInputWindowHandleObjLocalRef(JNIEnv* env); + + virtual bool updateInfo(); + +private: + jweak mObjWeak; +}; + + +extern sp<NativeInputWindowHandle> android_server_InputWindowHandle_getHandle( + JNIEnv* env, jobject inputWindowHandleObj); + +} // namespace android + +#endif // _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp new file mode 100644 index 0000000..d51e044 --- /dev/null +++ b/services/core/jni/com_android_server_lights_LightsService.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2009 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_TAG "LightsService" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" + +#include <utils/misc.h> +#include <utils/Log.h> +#include <hardware/hardware.h> +#include <hardware/lights.h> + +#include <stdio.h> + +namespace android +{ + +// These values must correspond with the LIGHT_ID constants in +// LightsService.java +enum { + LIGHT_INDEX_BACKLIGHT = 0, + LIGHT_INDEX_KEYBOARD = 1, + LIGHT_INDEX_BUTTONS = 2, + LIGHT_INDEX_BATTERY = 3, + LIGHT_INDEX_NOTIFICATIONS = 4, + LIGHT_INDEX_ATTENTION = 5, + LIGHT_INDEX_BLUETOOTH = 6, + LIGHT_INDEX_WIFI = 7, + LIGHT_COUNT +}; + +struct Devices { + light_device_t* lights[LIGHT_COUNT]; +}; + +static light_device_t* get_device(hw_module_t* module, char const* name) +{ + int err; + hw_device_t* device; + err = module->methods->open(module, name, &device); + if (err == 0) { + return (light_device_t*)device; + } else { + return NULL; + } +} + +static jlong init_native(JNIEnv *env, jobject clazz) +{ + int err; + hw_module_t* module; + Devices* devices; + + devices = (Devices*)malloc(sizeof(Devices)); + + err = hw_get_module(LIGHTS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); + if (err == 0) { + devices->lights[LIGHT_INDEX_BACKLIGHT] + = get_device(module, LIGHT_ID_BACKLIGHT); + devices->lights[LIGHT_INDEX_KEYBOARD] + = get_device(module, LIGHT_ID_KEYBOARD); + devices->lights[LIGHT_INDEX_BUTTONS] + = get_device(module, LIGHT_ID_BUTTONS); + devices->lights[LIGHT_INDEX_BATTERY] + = get_device(module, LIGHT_ID_BATTERY); + devices->lights[LIGHT_INDEX_NOTIFICATIONS] + = get_device(module, LIGHT_ID_NOTIFICATIONS); + devices->lights[LIGHT_INDEX_ATTENTION] + = get_device(module, LIGHT_ID_ATTENTION); + devices->lights[LIGHT_INDEX_BLUETOOTH] + = get_device(module, LIGHT_ID_BLUETOOTH); + devices->lights[LIGHT_INDEX_WIFI] + = get_device(module, LIGHT_ID_WIFI); + } else { + memset(devices, 0, sizeof(Devices)); + } + + return (jlong)devices; +} + +static void finalize_native(JNIEnv *env, jobject clazz, jlong ptr) +{ + Devices* devices = (Devices*)ptr; + if (devices == NULL) { + return; + } + + free(devices); +} + +static void setLight_native(JNIEnv *env, jobject clazz, jlong ptr, + jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode) +{ + Devices* devices = (Devices*)ptr; + light_state_t state; + + if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) { + return ; + } + + memset(&state, 0, sizeof(light_state_t)); + state.color = colorARGB; + state.flashMode = flashMode; + state.flashOnMS = onMS; + state.flashOffMS = offMS; + state.brightnessMode = brightnessMode; + + { + ALOGD_IF_SLOW(50, "Excessive delay setting light"); + devices->lights[light]->set_light(devices->lights[light], &state); + } +} + +static JNINativeMethod method_table[] = { + { "init_native", "()J", (void*)init_native }, + { "finalize_native", "(J)V", (void*)finalize_native }, + { "setLight_native", "(JIIIIII)V", (void*)setLight_native }, +}; + +int register_android_server_LightsService(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp new file mode 100644 index 0000000..6c14887 --- /dev/null +++ b/services/core/jni/com_android_server_location_FlpHardwareProvider.cpp @@ -0,0 +1,1002 @@ +/* + * Copyright (C) 2013 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/license/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_TAG "FlpHardwareProvider" +#define LOG_NDEBUG 0 + +#define WAKE_LOCK_NAME "FLP" +#define LOCATION_CLASS_NAME "android/location/Location" + +#include "jni.h" +#include "JNIHelp.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" +#include "hardware/fused_location.h" +#include "hardware_legacy/power.h" + +static jobject sCallbacksObj = NULL; +static JNIEnv *sCallbackEnv = NULL; +static hw_device_t* sHardwareDevice = NULL; + +static jmethodID sOnLocationReport = NULL; +static jmethodID sOnDataReport = NULL; +static jmethodID sOnGeofenceTransition = NULL; +static jmethodID sOnGeofenceMonitorStatus = NULL; +static jmethodID sOnGeofenceAdd = NULL; +static jmethodID sOnGeofenceRemove = NULL; +static jmethodID sOnGeofencePause = NULL; +static jmethodID sOnGeofenceResume = NULL; + +static const FlpLocationInterface* sFlpInterface = NULL; +static const FlpDiagnosticInterface* sFlpDiagnosticInterface = NULL; +static const FlpGeofencingInterface* sFlpGeofencingInterface = NULL; +static const FlpDeviceContextInterface* sFlpDeviceContextInterface = NULL; + +namespace android { + +static inline void CheckExceptions(JNIEnv* env, const char* methodName) { + if(!env->ExceptionCheck()) { + return; + } + + ALOGE("An exception was thrown by '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); +} + +static inline void ThrowOnError( + JNIEnv* env, + int resultCode, + const char* methodName) { + if(resultCode == FLP_RESULT_SUCCESS) { + return; + } + + ALOGE("Error %d in '%s'", resultCode, methodName); + env->FatalError(methodName); +} + +static bool IsValidCallbackThread() { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + if(sCallbackEnv == NULL || sCallbackEnv != env) { + ALOGE("CallbackThread check fail: env=%p, expected=%p", env, sCallbackEnv); + return false; + } + + return true; +} + +static int SetThreadEvent(ThreadEvent event) { + JavaVM* javaVm = AndroidRuntime::getJavaVM(); + + switch(event) { + case ASSOCIATE_JVM: + { + if(sCallbackEnv != NULL) { + ALOGE( + "Attempted to associate callback in '%s'. Callback already associated.", + __FUNCTION__ + ); + return FLP_RESULT_ERROR; + } + + JavaVMAttachArgs args = { + JNI_VERSION_1_6, + "FLP Service Callback Thread", + /* group */ NULL + }; + + jint attachResult = javaVm->AttachCurrentThread(&sCallbackEnv, &args); + if (attachResult != 0) { + ALOGE("Callback thread attachment error: %d", attachResult); + return FLP_RESULT_ERROR; + } + + ALOGV("Callback thread attached: %p", sCallbackEnv); + break; + } + case DISASSOCIATE_JVM: + { + if (!IsValidCallbackThread()) { + ALOGE( + "Attempted to dissasociate an unnownk callback thread : '%s'.", + __FUNCTION__ + ); + return FLP_RESULT_ERROR; + } + + if (javaVm->DetachCurrentThread() != 0) { + return FLP_RESULT_ERROR; + } + + sCallbackEnv = NULL; + break; + } + default: + ALOGE("Invalid ThreadEvent request %d", event); + return FLP_RESULT_ERROR; + } + + return FLP_RESULT_SUCCESS; +} + +/* + * Initializes the FlpHardwareProvider class from the native side by opening + * the HW module and obtaining the proper interfaces. + */ +static void ClassInit(JNIEnv* env, jclass clazz) { + // get references to the Java provider methods + sOnLocationReport = env->GetMethodID( + clazz, + "onLocationReport", + "([Landroid/location/Location;)V"); + sOnDataReport = env->GetMethodID( + clazz, + "onDataReport", + "(Ljava/lang/String;)V" + ); + sOnGeofenceTransition = env->GetMethodID( + clazz, + "onGeofenceTransition", + "(ILandroid/location/Location;IJI)V" + ); + sOnGeofenceMonitorStatus = env->GetMethodID( + clazz, + "onGeofenceMonitorStatus", + "(IILandroid/location/Location;)V" + ); + sOnGeofenceAdd = env->GetMethodID(clazz, "onGeofenceAdd", "(II)V"); + sOnGeofenceRemove = env->GetMethodID(clazz, "onGeofenceRemove", "(II)V"); + sOnGeofencePause = env->GetMethodID(clazz, "onGeofencePause", "(II)V"); + sOnGeofenceResume = env->GetMethodID(clazz, "onGeofenceResume", "(II)V"); +} + +/* + * Helper function to unwrap a java object back into a FlpLocation structure. + */ +static void TranslateFromObject( + JNIEnv* env, + jobject locationObject, + FlpLocation& location) { + location.size = sizeof(FlpLocation); + location.flags = 0; + + jclass locationClass = env->GetObjectClass(locationObject); + + jmethodID getLatitude = env->GetMethodID(locationClass, "getLatitude", "()D"); + location.latitude = env->CallDoubleMethod(locationObject, getLatitude); + jmethodID getLongitude = env->GetMethodID(locationClass, "getLongitude", "()D"); + location.longitude = env->CallDoubleMethod(locationObject, getLongitude); + jmethodID getTime = env->GetMethodID(locationClass, "getTime", "()J"); + location.timestamp = env->CallLongMethod(locationObject, getTime); + location.flags |= FLP_LOCATION_HAS_LAT_LONG; + + jmethodID hasAltitude = env->GetMethodID(locationClass, "hasAltitude", "()Z"); + if (env->CallBooleanMethod(locationObject, hasAltitude)) { + jmethodID getAltitude = env->GetMethodID(locationClass, "getAltitude", "()D"); + location.altitude = env->CallDoubleMethod(locationObject, getAltitude); + location.flags |= FLP_LOCATION_HAS_ALTITUDE; + } + + jmethodID hasSpeed = env->GetMethodID(locationClass, "hasSpeed", "()Z"); + if (env->CallBooleanMethod(locationObject, hasSpeed)) { + jmethodID getSpeed = env->GetMethodID(locationClass, "getSpeed", "()F"); + location.speed = env->CallFloatMethod(locationObject, getSpeed); + location.flags |= FLP_LOCATION_HAS_SPEED; + } + + jmethodID hasBearing = env->GetMethodID(locationClass, "hasBearing", "()Z"); + if (env->CallBooleanMethod(locationObject, hasBearing)) { + jmethodID getBearing = env->GetMethodID(locationClass, "getBearing", "()F"); + location.bearing = env->CallFloatMethod(locationObject, getBearing); + location.flags |= FLP_LOCATION_HAS_BEARING; + } + + jmethodID hasAccuracy = env->GetMethodID(locationClass, "hasAccuracy", "()Z"); + if (env->CallBooleanMethod(locationObject, hasAccuracy)) { + jmethodID getAccuracy = env->GetMethodID( + locationClass, + "getAccuracy", + "()F" + ); + location.accuracy = env->CallFloatMethod(locationObject, getAccuracy); + location.flags |= FLP_LOCATION_HAS_ACCURACY; + } + + // TODO: wire sources_used if Location class exposes them + + env->DeleteLocalRef(locationClass); +} + +/* + * Helper function to unwrap FlpBatchOptions from the Java Runtime calls. + */ +static void TranslateFromObject( + JNIEnv* env, + jobject batchOptionsObject, + FlpBatchOptions& batchOptions) { + jclass batchOptionsClass = env->GetObjectClass(batchOptionsObject); + + jmethodID getMaxPower = env->GetMethodID( + batchOptionsClass, + "getMaxPowerAllocationInMW", + "()D" + ); + batchOptions.max_power_allocation_mW = env->CallDoubleMethod( + batchOptionsObject, + getMaxPower + ); + + jmethodID getPeriod = env->GetMethodID( + batchOptionsClass, + "getPeriodInNS", + "()J" + ); + batchOptions.period_ns = env->CallLongMethod(batchOptionsObject, getPeriod); + + jmethodID getSourcesToUse = env->GetMethodID( + batchOptionsClass, + "getSourcesToUse", + "()I" + ); + batchOptions.sources_to_use = env->CallIntMethod( + batchOptionsObject, + getSourcesToUse + ); + + jmethodID getFlags = env->GetMethodID(batchOptionsClass, "getFlags", "()I"); + batchOptions.flags = env->CallIntMethod(batchOptionsObject, getFlags); + + env->DeleteLocalRef(batchOptionsClass); +} + +/* + * Helper function to unwrap Geofence structures from the Java Runtime calls. + */ +static void TranslateGeofenceFromGeofenceHardwareRequestParcelable( + JNIEnv* env, + jobject geofenceRequestObject, + Geofence& geofence) { + jclass geofenceRequestClass = env->GetObjectClass(geofenceRequestObject); + + jmethodID getId = env->GetMethodID(geofenceRequestClass, "getId", "()I"); + geofence.geofence_id = env->CallIntMethod(geofenceRequestObject, getId); + + jmethodID getType = env->GetMethodID(geofenceRequestClass, "getType", "()I"); + // this works because GeofenceHardwareRequest.java and fused_location.h have + // the same notion of geofence types + GeofenceType type = (GeofenceType)env->CallIntMethod(geofenceRequestObject, getType); + if(type != TYPE_CIRCLE) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + geofence.data->type = type; + GeofenceCircle& circle = geofence.data->geofence.circle; + + jmethodID getLatitude = env->GetMethodID( + geofenceRequestClass, + "getLatitude", + "()D"); + circle.latitude = env->CallDoubleMethod(geofenceRequestObject, getLatitude); + + jmethodID getLongitude = env->GetMethodID( + geofenceRequestClass, + "getLongitude", + "()D"); + circle.longitude = env->CallDoubleMethod(geofenceRequestObject, getLongitude); + + jmethodID getRadius = env->GetMethodID(geofenceRequestClass, "getRadius", "()D"); + circle.radius_m = env->CallDoubleMethod(geofenceRequestObject, getRadius); + + GeofenceOptions* options = geofence.options; + jmethodID getMonitorTransitions = env->GetMethodID( + geofenceRequestClass, + "getMonitorTransitions", + "()I"); + options->monitor_transitions = env->CallIntMethod( + geofenceRequestObject, + getMonitorTransitions); + + jmethodID getUnknownTimer = env->GetMethodID( + geofenceRequestClass, + "getUnknownTimer", + "()I"); + options->unknown_timer_ms = env->CallIntMethod(geofenceRequestObject, getUnknownTimer); + + jmethodID getNotificationResponsiveness = env->GetMethodID( + geofenceRequestClass, + "getNotificationResponsiveness", + "()I"); + options->notification_responsivenes_ms = env->CallIntMethod( + geofenceRequestObject, + getNotificationResponsiveness); + + jmethodID getLastTransition = env->GetMethodID( + geofenceRequestClass, + "getLastTransition", + "()I"); + options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition); + + // TODO: set data.sources_to_use when available + + env->DeleteLocalRef(geofenceRequestClass); +} + +/* + * Helper function to transform FlpLocation into a java object. + */ +static void TranslateToObject(const FlpLocation* location, jobject& locationObject) { + jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME); + jmethodID locationCtor = sCallbackEnv->GetMethodID( + locationClass, + "<init>", + "(Ljava/lang/String;)V" + ); + + // the provider is set in the upper JVM layer + locationObject = sCallbackEnv->NewObject(locationClass, locationCtor, NULL); + jint flags = location->flags; + + // set the valid information in the object + if (flags & FLP_LOCATION_HAS_LAT_LONG) { + jmethodID setLatitude = sCallbackEnv->GetMethodID( + locationClass, + "setLatitude", + "(D)V" + ); + sCallbackEnv->CallVoidMethod(locationObject, setLatitude, location->latitude); + + jmethodID setLongitude = sCallbackEnv->GetMethodID( + locationClass, + "setLongitude", + "(D)V" + ); + sCallbackEnv->CallVoidMethod( + locationObject, + setLongitude, + location->longitude + ); + + jmethodID setTime = sCallbackEnv->GetMethodID( + locationClass, + "setTime", + "(J)V" + ); + sCallbackEnv->CallVoidMethod(locationObject, setTime, location->timestamp); + } + + if (flags & FLP_LOCATION_HAS_ALTITUDE) { + jmethodID setAltitude = sCallbackEnv->GetMethodID( + locationClass, + "setAltitude", + "(D)V" + ); + sCallbackEnv->CallVoidMethod(locationObject, setAltitude, location->altitude); + } + + if (flags & FLP_LOCATION_HAS_SPEED) { + jmethodID setSpeed = sCallbackEnv->GetMethodID( + locationClass, + "setSpeed", + "(F)V" + ); + sCallbackEnv->CallVoidMethod(locationObject, setSpeed, location->speed); + } + + if (flags & FLP_LOCATION_HAS_BEARING) { + jmethodID setBearing = sCallbackEnv->GetMethodID( + locationClass, + "setBearing", + "(F)V" + ); + sCallbackEnv->CallVoidMethod(locationObject, setBearing, location->bearing); + } + + if (flags & FLP_LOCATION_HAS_ACCURACY) { + jmethodID setAccuracy = sCallbackEnv->GetMethodID( + locationClass, + "setAccuracy", + "(F)V" + ); + sCallbackEnv->CallVoidMethod(locationObject, setAccuracy, location->accuracy); + } + + // TODO: wire FlpLocation::sources_used when needed + + sCallbackEnv->DeleteLocalRef(locationClass); +} + +/* + * Helper function to serialize FlpLocation structures. + */ +static void TranslateToObjectArray( + int32_t locationsCount, + FlpLocation** locations, + jobjectArray& locationsArray) { + jclass locationClass = sCallbackEnv->FindClass(LOCATION_CLASS_NAME); + locationsArray = sCallbackEnv->NewObjectArray( + locationsCount, + locationClass, + /* initialElement */ NULL + ); + + for (int i = 0; i < locationsCount; ++i) { + jobject locationObject = NULL; + TranslateToObject(locations[i], locationObject); + sCallbackEnv->SetObjectArrayElement(locationsArray, i, locationObject); + sCallbackEnv->DeleteLocalRef(locationObject); + } + + sCallbackEnv->DeleteLocalRef(locationClass); +} + +static void LocationCallback(int32_t locationsCount, FlpLocation** locations) { + if(!IsValidCallbackThread()) { + return; + } + + if(locationsCount == 0 || locations == NULL) { + ALOGE( + "Invalid LocationCallback. Count: %d, Locations: %p", + locationsCount, + locations + ); + return; + } + + jobjectArray locationsArray = NULL; + TranslateToObjectArray(locationsCount, locations, locationsArray); + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, + sOnLocationReport, + locationsArray + ); + CheckExceptions(sCallbackEnv, __FUNCTION__); + + if(locationsArray != NULL) { + sCallbackEnv->DeleteLocalRef(locationsArray); + } +} + +static void AcquireWakelock() { + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); +} + +static void ReleaseWakelock() { + release_wake_lock(WAKE_LOCK_NAME); +} + +FlpCallbacks sFlpCallbacks = { + sizeof(FlpCallbacks), + LocationCallback, + AcquireWakelock, + ReleaseWakelock, + SetThreadEvent +}; + +static void ReportData(char* data, int length) { + jstring stringData = NULL; + + if(length != 0 && data != NULL) { + stringData = sCallbackEnv->NewString(reinterpret_cast<jchar*>(data), length); + } else { + ALOGE("Invalid ReportData callback. Length: %d, Data: %p", length, data); + return; + } + + sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnDataReport, stringData); + CheckExceptions(sCallbackEnv, __FUNCTION__); +} + +FlpDiagnosticCallbacks sFlpDiagnosticCallbacks = { + sizeof(FlpDiagnosticCallbacks), + SetThreadEvent, + ReportData +}; + +static void GeofenceTransitionCallback( + int32_t geofenceId, + FlpLocation* location, + int32_t transition, + FlpUtcTime timestamp, + uint32_t sourcesUsed + ) { + if(!IsValidCallbackThread()) { + return; + } + + if(location == NULL) { + ALOGE("GeofenceTransition received with invalid location: %p", location); + return; + } + + jobject locationObject = NULL; + TranslateToObject(location, locationObject); + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, + sOnGeofenceTransition, + geofenceId, + locationObject, + transition, + timestamp, + sourcesUsed + ); + CheckExceptions(sCallbackEnv, __FUNCTION__); + + if(locationObject != NULL) { + sCallbackEnv->DeleteLocalRef(locationObject); + } +} + +static void GeofenceMonitorStatusCallback( + int32_t status, + uint32_t source, + FlpLocation* lastLocation) { + if(!IsValidCallbackThread()) { + return; + } + + jobject locationObject = NULL; + if(lastLocation != NULL) { + TranslateToObject(lastLocation, locationObject); + } + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, + sOnGeofenceMonitorStatus, + status, + source, + locationObject + ); + CheckExceptions(sCallbackEnv, __FUNCTION__); + + if(locationObject != NULL) { + sCallbackEnv->DeleteLocalRef(locationObject); + } +} + +static void GeofenceAddCallback(int32_t geofenceId, int32_t result) { + if(!IsValidCallbackThread()) { + return; + } + + sCallbackEnv->CallVoidMethod(sCallbacksObj, sOnGeofenceAdd, geofenceId, result); + CheckExceptions(sCallbackEnv, __FUNCTION__); +} + +static void GeofenceRemoveCallback(int32_t geofenceId, int32_t result) { + if(!IsValidCallbackThread()) { + return; + } + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, + sOnGeofenceRemove, + geofenceId, + result + ); + CheckExceptions(sCallbackEnv, __FUNCTION__); +} + +static void GeofencePauseCallback(int32_t geofenceId, int32_t result) { + if(!IsValidCallbackThread()) { + return; + } + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, + sOnGeofencePause, + geofenceId, + result + ); + CheckExceptions(sCallbackEnv, __FUNCTION__); +} + +static void GeofenceResumeCallback(int32_t geofenceId, int32_t result) { + if(!IsValidCallbackThread()) { + return; + } + + sCallbackEnv->CallVoidMethod( + sCallbacksObj, + sOnGeofenceResume, + geofenceId, + result + ); + CheckExceptions(sCallbackEnv, __FUNCTION__); +} + +FlpGeofenceCallbacks sFlpGeofenceCallbacks = { + sizeof(FlpGeofenceCallbacks), + GeofenceTransitionCallback, + GeofenceMonitorStatusCallback, + GeofenceAddCallback, + GeofenceRemoveCallback, + GeofencePauseCallback, + GeofenceResumeCallback, + SetThreadEvent +}; + +/* + * Initializes the Fused Location Provider in the native side. It ensures that + * the Flp interfaces are initialized properly. + */ +static void Init(JNIEnv* env, jobject obj) { + if(sHardwareDevice != NULL) { + ALOGD("Hardware Device already opened."); + return; + } + + const hw_module_t* module = NULL; + int err = hw_get_module(FUSED_LOCATION_HARDWARE_MODULE_ID, &module); + if(err != 0) { + ALOGE("Error hw_get_module '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err); + return; + } + + err = module->methods->open( + module, + FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice); + if(err != 0) { + ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err); + return; + } + + sFlpInterface = NULL; + flp_device_t* flp_device = reinterpret_cast<flp_device_t*>(sHardwareDevice); + sFlpInterface = flp_device->get_flp_interface(flp_device); + + if(sFlpInterface != NULL) { + sFlpDiagnosticInterface = reinterpret_cast<const FlpDiagnosticInterface*>( + sFlpInterface->get_extension(FLP_DIAGNOSTIC_INTERFACE) + ); + + sFlpGeofencingInterface = reinterpret_cast<const FlpGeofencingInterface*>( + sFlpInterface->get_extension(FLP_GEOFENCING_INTERFACE) + ); + + sFlpDeviceContextInterface = reinterpret_cast<const FlpDeviceContextInterface*>( + sFlpInterface->get_extension(FLP_DEVICE_CONTEXT_INTERFACE) + ); + } + + if(sCallbacksObj == NULL) { + sCallbacksObj = env->NewGlobalRef(obj); + } + + // initialize the Flp interfaces + if(sFlpInterface == NULL || sFlpInterface->init(&sFlpCallbacks) != 0) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + if(sFlpDiagnosticInterface != NULL) { + sFlpDiagnosticInterface->init(&sFlpDiagnosticCallbacks); + } + + if(sFlpGeofencingInterface != NULL) { + sFlpGeofencingInterface->init(&sFlpGeofenceCallbacks); + } + + // TODO: inject any device context if when needed +} + +static jboolean IsSupported(JNIEnv* env, jclass clazz) { + return sFlpInterface != NULL; +} + +static jint GetBatchSize(JNIEnv* env, jobject object) { + if(sFlpInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + return sFlpInterface->get_batch_size(); +} + +static void StartBatching( + JNIEnv* env, + jobject object, + jint id, + jobject optionsObject) { + if(sFlpInterface == NULL || optionsObject == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + FlpBatchOptions options; + TranslateFromObject(env, optionsObject, options); + int result = sFlpInterface->start_batching(id, &options); + ThrowOnError(env, result, __FUNCTION__); +} + +static void UpdateBatchingOptions( + JNIEnv* env, + jobject object, + jint id, + jobject optionsObject) { + if(sFlpInterface == NULL || optionsObject == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + FlpBatchOptions options; + TranslateFromObject(env, optionsObject, options); + int result = sFlpInterface->update_batching_options(id, &options); + ThrowOnError(env, result, __FUNCTION__); +} + +static void StopBatching(JNIEnv* env, jobject object, jint id) { + if(sFlpInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + sFlpInterface->stop_batching(id); +} + +static void Cleanup(JNIEnv* env, jobject object) { + if(sFlpInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + sFlpInterface->cleanup(); + + if(sCallbacksObj != NULL) { + env->DeleteGlobalRef(sCallbacksObj); + sCallbacksObj = NULL; + } + + sFlpInterface = NULL; + sFlpDiagnosticInterface = NULL; + sFlpDeviceContextInterface = NULL; + sFlpGeofencingInterface = NULL; + + if(sHardwareDevice != NULL) { + sHardwareDevice->close(sHardwareDevice); + sHardwareDevice = NULL; + } +} + +static void GetBatchedLocation(JNIEnv* env, jobject object, jint lastNLocations) { + if(sFlpInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + sFlpInterface->get_batched_location(lastNLocations); +} + +static void InjectLocation(JNIEnv* env, jobject object, jobject locationObject) { + if(locationObject == NULL) { + ALOGE("Invalid location for injection: %p", locationObject); + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + if(sFlpInterface == NULL) { + // there is no listener, bail + return; + } + + FlpLocation location; + TranslateFromObject(env, locationObject, location); + int result = sFlpInterface->inject_location(&location); + if (result != FLP_RESULT_SUCCESS) { + // do not throw but log, this operation should be fire and forget + ALOGE("Error %d in '%s'", result, __FUNCTION__); + } +} + +static jboolean IsDiagnosticSupported() { + return sFlpDiagnosticInterface != NULL; +} + +static void InjectDiagnosticData(JNIEnv* env, jobject object, jstring stringData) { + if(stringData == NULL) { + ALOGE("Invalid diagnostic data for injection: %p", stringData); + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + if(sFlpDiagnosticInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + int length = env->GetStringLength(stringData); + const jchar* data = env->GetStringChars(stringData, /* isCopy */ NULL); + if(data == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + int result = sFlpDiagnosticInterface->inject_data((char*) data, length); + ThrowOnError(env, result, __FUNCTION__); +} + +static jboolean IsDeviceContextSupported() { + return sFlpDeviceContextInterface != NULL; +} + +static void InjectDeviceContext(JNIEnv* env, jobject object, jint enabledMask) { + if(sFlpDeviceContextInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + int result = sFlpDeviceContextInterface->inject_device_context(enabledMask); + ThrowOnError(env, result, __FUNCTION__); +} + +static jboolean IsGeofencingSupported() { + return sFlpGeofencingInterface != NULL; +} + +static void AddGeofences( + JNIEnv* env, + jobject object, + jobjectArray geofenceRequestsArray) { + if(geofenceRequestsArray == NULL) { + ALOGE("Invalid Geofences to add: %p", geofenceRequestsArray); + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + if (sFlpGeofencingInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + jint geofenceRequestsCount = env->GetArrayLength(geofenceRequestsArray); + if(geofenceRequestsCount == 0) { + return; + } + + Geofence* geofences = new Geofence[geofenceRequestsCount]; + if (geofences == NULL) { + ThrowOnError(env, FLP_RESULT_INSUFFICIENT_MEMORY, __FUNCTION__); + } + + for (int i = 0; i < geofenceRequestsCount; ++i) { + geofences[i].data = new GeofenceData(); + geofences[i].options = new GeofenceOptions(); + jobject geofenceObject = env->GetObjectArrayElement(geofenceRequestsArray, i); + + TranslateGeofenceFromGeofenceHardwareRequestParcelable(env, geofenceObject, geofences[i]); + env->DeleteLocalRef(geofenceObject); + } + + sFlpGeofencingInterface->add_geofences(geofenceRequestsCount, &geofences); + if (geofences != NULL) { + for(int i = 0; i < geofenceRequestsCount; ++i) { + delete geofences[i].data; + delete geofences[i].options; + } + delete[] geofences; + } +} + +static void PauseGeofence(JNIEnv* env, jobject object, jint geofenceId) { + if(sFlpGeofencingInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + sFlpGeofencingInterface->pause_geofence(geofenceId); +} + +static void ResumeGeofence( + JNIEnv* env, + jobject object, + jint geofenceId, + jint monitorTransitions) { + if(sFlpGeofencingInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + sFlpGeofencingInterface->resume_geofence(geofenceId, monitorTransitions); +} + +static void ModifyGeofenceOption( + JNIEnv* env, + jobject object, + jint geofenceId, + jint lastTransition, + jint monitorTransitions, + jint notificationResponsiveness, + jint unknownTimer, + jint sourcesToUse) { + if(sFlpGeofencingInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + GeofenceOptions options = { + lastTransition, + monitorTransitions, + notificationResponsiveness, + unknownTimer, + (uint32_t)sourcesToUse + }; + + sFlpGeofencingInterface->modify_geofence_option(geofenceId, &options); +} + +static void RemoveGeofences( + JNIEnv* env, + jobject object, + jintArray geofenceIdsArray) { + if(sFlpGeofencingInterface == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + jsize geofenceIdsCount = env->GetArrayLength(geofenceIdsArray); + jint* geofenceIds = env->GetIntArrayElements(geofenceIdsArray, /* isCopy */ NULL); + if(geofenceIds == NULL) { + ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__); + } + + sFlpGeofencingInterface->remove_geofences(geofenceIdsCount, geofenceIds); + env->ReleaseIntArrayElements(geofenceIdsArray, geofenceIds, 0 /*mode*/); +} + +static JNINativeMethod sMethods[] = { + //{"name", "signature", functionPointer } + {"nativeClassInit", "()V", reinterpret_cast<void*>(ClassInit)}, + {"nativeInit", "()V", reinterpret_cast<void*>(Init)}, + {"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)}, + {"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)}, + {"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)}, + {"nativeStartBatching", + "(ILandroid/location/FusedBatchOptions;)V", + reinterpret_cast<void*>(StartBatching)}, + {"nativeUpdateBatchingOptions", + "(ILandroid/location/FusedBatchOptions;)V", + reinterpret_cast<void*>(UpdateBatchingOptions)}, + {"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)}, + {"nativeRequestBatchedLocation", + "(I)V", + reinterpret_cast<void*>(GetBatchedLocation)}, + {"nativeInjectLocation", + "(Landroid/location/Location;)V", + reinterpret_cast<void*>(InjectLocation)}, + {"nativeIsDiagnosticSupported", + "()Z", + reinterpret_cast<void*>(IsDiagnosticSupported)}, + {"nativeInjectDiagnosticData", + "(Ljava/lang/String;)V", + reinterpret_cast<void*>(InjectDiagnosticData)}, + {"nativeIsDeviceContextSupported", + "()Z", + reinterpret_cast<void*>(IsDeviceContextSupported)}, + {"nativeInjectDeviceContext", + "(I)V", + reinterpret_cast<void*>(InjectDeviceContext)}, + {"nativeIsGeofencingSupported", + "()Z", + reinterpret_cast<void*>(IsGeofencingSupported)}, + {"nativeAddGeofences", + "([Landroid/hardware/location/GeofenceHardwareRequestParcelable;)V", + reinterpret_cast<void*>(AddGeofences)}, + {"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)}, + {"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)}, + {"nativeModifyGeofenceOption", + "(IIIIII)V", + reinterpret_cast<void*>(ModifyGeofenceOption)}, + {"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)} +}; + +/* + * Registration method invoked on JNI Load. + */ +int register_android_server_location_FlpHardwareProvider(JNIEnv* env) { + return jniRegisterNativeMethods( + env, + "com/android/server/location/FlpHardwareProvider", + sMethods, + NELEM(sMethods) + ); +} + +} /* name-space Android */ diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp new file mode 100644 index 0000000..e9ba116 --- /dev/null +++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp @@ -0,0 +1,800 @@ +/* + * Copyright (C) 2008 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_TAG "GpsLocationProvider" + +#define LOG_NDEBUG 0 + +#include "JNIHelp.h" +#include "jni.h" +#include "hardware/hardware.h" +#include "hardware/gps.h" +#include "hardware_legacy/power.h" +#include "utils/Log.h" +#include "utils/misc.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/Log.h" + +#include <string.h> +#include <pthread.h> + +static jobject mCallbacksObj = NULL; + +static jmethodID method_reportLocation; +static jmethodID method_reportStatus; +static jmethodID method_reportSvStatus; +static jmethodID method_reportAGpsStatus; +static jmethodID method_reportNmea; +static jmethodID method_setEngineCapabilities; +static jmethodID method_xtraDownloadRequest; +static jmethodID method_reportNiNotification; +static jmethodID method_requestRefLocation; +static jmethodID method_requestSetID; +static jmethodID method_requestUtcTime; +static jmethodID method_reportGeofenceTransition; +static jmethodID method_reportGeofenceStatus; +static jmethodID method_reportGeofenceAddStatus; +static jmethodID method_reportGeofenceRemoveStatus; +static jmethodID method_reportGeofencePauseStatus; +static jmethodID method_reportGeofenceResumeStatus; + +static const GpsInterface* sGpsInterface = NULL; +static const GpsXtraInterface* sGpsXtraInterface = NULL; +static const AGpsInterface* sAGpsInterface = NULL; +static const GpsNiInterface* sGpsNiInterface = NULL; +static const GpsDebugInterface* sGpsDebugInterface = NULL; +static const AGpsRilInterface* sAGpsRilInterface = NULL; +static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL; + +// temporary storage for GPS callbacks +static GpsSvStatus sGpsSvStatus; +static const char* sNmeaString; +static int sNmeaStringLength; + +#define WAKE_LOCK_NAME "GPS" + +namespace android { + +static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + } +} + +static void location_callback(GpsLocation* location) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags, + (jdouble)location->latitude, (jdouble)location->longitude, + (jdouble)location->altitude, + (jfloat)location->speed, (jfloat)location->bearing, + (jfloat)location->accuracy, (jlong)location->timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +static void status_callback(GpsStatus* status) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_reportStatus, status->status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +static void sv_status_callback(GpsSvStatus* sv_status) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + memcpy(&sGpsSvStatus, sv_status, sizeof(sGpsSvStatus)); + env->CallVoidMethod(mCallbacksObj, method_reportSvStatus); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + // The Java code will call back to read these values + // We do this to avoid creating unnecessary String objects + sNmeaString = nmea; + sNmeaStringLength = length; + env->CallVoidMethod(mCallbacksObj, method_reportNmea, timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +static void set_capabilities_callback(uint32_t capabilities) +{ + ALOGD("set_capabilities_callback: %du\n", capabilities); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +static void acquire_wakelock_callback() +{ + acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME); +} + +static void release_wakelock_callback() +{ + release_wake_lock(WAKE_LOCK_NAME); +} + +static void request_utc_time_callback() +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestUtcTime); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg) +{ + return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg); +} + +GpsCallbacks sGpsCallbacks = { + sizeof(GpsCallbacks), + location_callback, + status_callback, + sv_status_callback, + nmea_callback, + set_capabilities_callback, + acquire_wakelock_callback, + release_wakelock_callback, + create_thread_callback, + request_utc_time_callback, +}; + +static void xtra_download_request_callback() +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_xtraDownloadRequest); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +GpsXtraCallbacks sGpsXtraCallbacks = { + xtra_download_request_callback, + create_thread_callback, +}; + +static void agps_status_callback(AGpsStatus* agps_status) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + uint32_t ipaddr; + // ipaddr field was not included in original AGpsStatus + if (agps_status->size >= sizeof(AGpsStatus)) + ipaddr = agps_status->ipaddr; + else + ipaddr = 0xFFFFFFFF; + env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, + agps_status->type, agps_status->status, ipaddr); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +AGpsCallbacks sAGpsCallbacks = { + agps_status_callback, + create_thread_callback, +}; + +static void gps_ni_notify_callback(GpsNiNotification *notification) +{ + ALOGD("gps_ni_notify_callback\n"); + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jstring requestor_id = env->NewStringUTF(notification->requestor_id); + jstring text = env->NewStringUTF(notification->text); + jstring extras = env->NewStringUTF(notification->extras); + + if (requestor_id && text && extras) { + env->CallVoidMethod(mCallbacksObj, method_reportNiNotification, + notification->notification_id, notification->ni_type, + notification->notify_flags, notification->timeout, + notification->default_response, requestor_id, text, + notification->requestor_id_encoding, + notification->text_encoding, extras); + } else { + ALOGE("out of memory in gps_ni_notify_callback\n"); + } + + if (requestor_id) + env->DeleteLocalRef(requestor_id); + if (text) + env->DeleteLocalRef(text); + if (extras) + env->DeleteLocalRef(extras); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +GpsNiCallbacks sGpsNiCallbacks = { + gps_ni_notify_callback, + create_thread_callback, +}; + +static void agps_request_set_id(uint32_t flags) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestSetID, flags); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +static void agps_request_ref_location(uint32_t flags) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + env->CallVoidMethod(mCallbacksObj, method_requestRefLocation, flags); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +} + +AGpsRilCallbacks sAGpsRilCallbacks = { + agps_request_set_id, + agps_request_ref_location, + create_thread_callback, +}; + +static void gps_geofence_transition_callback(int32_t geofence_id, GpsLocation* location, + int32_t transition, GpsUtcTime timestamp) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + env->CallVoidMethod(mCallbacksObj, method_reportGeofenceTransition, geofence_id, + location->flags, (jdouble)location->latitude, (jdouble)location->longitude, + (jdouble)location->altitude, + (jfloat)location->speed, (jfloat)location->bearing, + (jfloat)location->accuracy, (jlong)location->timestamp, + transition, timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +}; + +static void gps_geofence_status_callback(int32_t status, GpsLocation* location) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jint flags = 0; + jdouble latitude = 0; + jdouble longitude = 0; + jdouble altitude = 0; + jfloat speed = 0; + jfloat bearing = 0; + jfloat accuracy = 0; + jlong timestamp = 0; + if (location != NULL) { + flags = location->flags; + latitude = location->latitude; + longitude = location->longitude; + altitude = location->altitude; + speed = location->speed; + bearing = location->bearing; + accuracy = location->accuracy; + timestamp = location->timestamp; + } + + env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status, + flags, latitude, longitude, altitude, speed, bearing, accuracy, timestamp); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +}; + +static void gps_geofence_add_callback(int32_t geofence_id, int32_t status) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (status != GPS_GEOFENCE_OPERATION_SUCCESS) { + ALOGE("Error in geofence_add_callback: %d\n", status); + } + env->CallVoidMethod(mCallbacksObj, method_reportGeofenceAddStatus, geofence_id, status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +}; + +static void gps_geofence_remove_callback(int32_t geofence_id, int32_t status) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (status != GPS_GEOFENCE_OPERATION_SUCCESS) { + ALOGE("Error in geofence_remove_callback: %d\n", status); + } + env->CallVoidMethod(mCallbacksObj, method_reportGeofenceRemoveStatus, geofence_id, status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +}; + +static void gps_geofence_resume_callback(int32_t geofence_id, int32_t status) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (status != GPS_GEOFENCE_OPERATION_SUCCESS) { + ALOGE("Error in geofence_resume_callback: %d\n", status); + } + env->CallVoidMethod(mCallbacksObj, method_reportGeofenceResumeStatus, geofence_id, status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +}; + +static void gps_geofence_pause_callback(int32_t geofence_id, int32_t status) +{ + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (status != GPS_GEOFENCE_OPERATION_SUCCESS) { + ALOGE("Error in geofence_pause_callback: %d\n", status); + } + env->CallVoidMethod(mCallbacksObj, method_reportGeofencePauseStatus, geofence_id, status); + checkAndClearExceptionFromCallback(env, __FUNCTION__); +}; + +GpsGeofenceCallbacks sGpsGeofenceCallbacks = { + gps_geofence_transition_callback, + gps_geofence_status_callback, + gps_geofence_add_callback, + gps_geofence_remove_callback, + gps_geofence_pause_callback, + gps_geofence_resume_callback, + create_thread_callback, +}; + +static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { + int err; + hw_module_t* module; + + method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V"); + method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); + method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V"); + method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(III)V"); + method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V"); + method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V"); + method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); + method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", + "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"); + method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V"); + method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V"); + method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V"); + method_reportGeofenceTransition = env->GetMethodID(clazz,"reportGeofenceTransition", + "(IIDDDFFFJIJ)V"); + method_reportGeofenceStatus = env->GetMethodID(clazz,"reportGeofenceStatus", + "(IIDDDFFFJ)V"); + method_reportGeofenceAddStatus = env->GetMethodID(clazz,"reportGeofenceAddStatus", + "(II)V"); + method_reportGeofenceRemoveStatus = env->GetMethodID(clazz,"reportGeofenceRemoveStatus", + "(II)V"); + method_reportGeofenceResumeStatus = env->GetMethodID(clazz,"reportGeofenceResumeStatus", + "(II)V"); + method_reportGeofencePauseStatus = env->GetMethodID(clazz,"reportGeofencePauseStatus", + "(II)V"); + + err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); + if (err == 0) { + hw_device_t* device; + err = module->methods->open(module, GPS_HARDWARE_MODULE_ID, &device); + if (err == 0) { + gps_device_t* gps_device = (gps_device_t *)device; + sGpsInterface = gps_device->get_gps_interface(gps_device); + } + } + if (sGpsInterface) { + sGpsXtraInterface = + (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE); + sAGpsInterface = + (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); + sGpsNiInterface = + (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE); + sGpsDebugInterface = + (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE); + sAGpsRilInterface = + (const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE); + sGpsGeofencingInterface = + (const GpsGeofencingInterface*)sGpsInterface->get_extension(GPS_GEOFENCING_INTERFACE); + } +} + +static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) { + if (sGpsInterface != NULL) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} + +static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) +{ + // this must be set before calling into the HAL library + if (!mCallbacksObj) + mCallbacksObj = env->NewGlobalRef(obj); + + // fail if the main interface fails to initialize + if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) + return JNI_FALSE; + + // if XTRA initialization fails we will disable it by sGpsXtraInterface to NULL, + // but continue to allow the rest of the GPS interface to work. + if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0) + sGpsXtraInterface = NULL; + if (sAGpsInterface) + sAGpsInterface->init(&sAGpsCallbacks); + if (sGpsNiInterface) + sGpsNiInterface->init(&sGpsNiCallbacks); + if (sAGpsRilInterface) + sAGpsRilInterface->init(&sAGpsRilCallbacks); + if (sGpsGeofencingInterface) + sGpsGeofencingInterface->init(&sGpsGeofenceCallbacks); + + return JNI_TRUE; +} + +static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj) +{ + if (sGpsInterface) + sGpsInterface->cleanup(); +} + +static jboolean android_location_GpsLocationProvider_set_position_mode(JNIEnv* env, jobject obj, + jint mode, jint recurrence, jint min_interval, jint preferred_accuracy, jint preferred_time) +{ + if (sGpsInterface) { + if (sGpsInterface->set_position_mode(mode, recurrence, min_interval, preferred_accuracy, + preferred_time) == 0) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } + } + else + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj) +{ + if (sGpsInterface) { + if (sGpsInterface->start() == 0) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } + } + else + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj) +{ + if (sGpsInterface) { + if (sGpsInterface->stop() == 0) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } + } + else + return JNI_FALSE; +} + +static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags) +{ + if (sGpsInterface) + sGpsInterface->delete_aiding_data(flags); +} + +static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj, + jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray, + jintArray maskArray) +{ + // this should only be called from within a call to reportSvStatus + + jint* prns = env->GetIntArrayElements(prnArray, 0); + jfloat* snrs = env->GetFloatArrayElements(snrArray, 0); + jfloat* elev = env->GetFloatArrayElements(elevArray, 0); + jfloat* azim = env->GetFloatArrayElements(azumArray, 0); + jint* mask = env->GetIntArrayElements(maskArray, 0); + + int num_svs = sGpsSvStatus.num_svs; + for (int i = 0; i < num_svs; i++) { + prns[i] = sGpsSvStatus.sv_list[i].prn; + snrs[i] = sGpsSvStatus.sv_list[i].snr; + elev[i] = sGpsSvStatus.sv_list[i].elevation; + azim[i] = sGpsSvStatus.sv_list[i].azimuth; + } + mask[0] = sGpsSvStatus.ephemeris_mask; + mask[1] = sGpsSvStatus.almanac_mask; + mask[2] = sGpsSvStatus.used_in_fix_mask; + + env->ReleaseIntArrayElements(prnArray, prns, 0); + env->ReleaseFloatArrayElements(snrArray, snrs, 0); + env->ReleaseFloatArrayElements(elevArray, elev, 0); + env->ReleaseFloatArrayElements(azumArray, azim, 0); + env->ReleaseIntArrayElements(maskArray, mask, 0); + return (jint) num_svs; +} + +static void android_location_GpsLocationProvider_agps_set_reference_location_cellid(JNIEnv* env, + jobject obj, jint type, jint mcc, jint mnc, jint lac, jint cid) +{ + AGpsRefLocation location; + + if (!sAGpsRilInterface) { + ALOGE("no AGPS RIL interface in agps_set_reference_location_cellid"); + return; + } + + switch(type) { + case AGPS_REF_LOCATION_TYPE_GSM_CELLID: + case AGPS_REF_LOCATION_TYPE_UMTS_CELLID: + location.type = type; + location.u.cellID.mcc = mcc; + location.u.cellID.mnc = mnc; + location.u.cellID.lac = lac; + location.u.cellID.cid = cid; + break; + default: + ALOGE("Neither a GSM nor a UMTS cellid (%s:%d).",__FUNCTION__,__LINE__); + return; + break; + } + sAGpsRilInterface->set_ref_location(&location, sizeof(location)); +} + +static void android_location_GpsLocationProvider_agps_send_ni_message(JNIEnv* env, + jobject obj, jbyteArray ni_msg, jint size) +{ + size_t sz; + + if (!sAGpsRilInterface) { + ALOGE("no AGPS RIL interface in send_ni_message"); + return; + } + if (size < 0) + return; + sz = (size_t)size; + jbyte* b = env->GetByteArrayElements(ni_msg, 0); + sAGpsRilInterface->ni_message((uint8_t *)b,sz); + env->ReleaseByteArrayElements(ni_msg,b,0); +} + +static void android_location_GpsLocationProvider_agps_set_id(JNIEnv *env, + jobject obj, jint type, jstring setid_string) +{ + if (!sAGpsRilInterface) { + ALOGE("no AGPS RIL interface in agps_set_id"); + return; + } + + const char *setid = env->GetStringUTFChars(setid_string, NULL); + sAGpsRilInterface->set_set_id(type, setid); + env->ReleaseStringUTFChars(setid_string, setid); +} + +static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, + jbyteArray nmeaArray, jint buffer_size) +{ + // this should only be called from within a call to reportNmea + jbyte* nmea = (jbyte *)env->GetPrimitiveArrayCritical(nmeaArray, 0); + int length = sNmeaStringLength; + if (length > buffer_size) + length = buffer_size; + memcpy(nmea, sNmeaString, length); + env->ReleasePrimitiveArrayCritical(nmeaArray, nmea, JNI_ABORT); + return (jint) length; +} + +static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, + jlong time, jlong timeReference, jint uncertainty) +{ + if (sGpsInterface) + sGpsInterface->inject_time(time, timeReference, uncertainty); +} + +static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj, + jdouble latitude, jdouble longitude, jfloat accuracy) +{ + if (sGpsInterface) + sGpsInterface->inject_location(latitude, longitude, accuracy); +} + +static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) +{ + if (sGpsXtraInterface != NULL) { + return JNI_TRUE; + } else { + return JNI_FALSE; + } +} + +static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj, + jbyteArray data, jint length) +{ + if (!sGpsXtraInterface) { + ALOGE("no XTRA interface in inject_xtra_data"); + return; + } + + jbyte* bytes = (jbyte *)env->GetPrimitiveArrayCritical(data, 0); + sGpsXtraInterface->inject_xtra_data((char *)bytes, length); + env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT); +} + +static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn) +{ + if (!sAGpsInterface) { + ALOGE("no AGPS interface in agps_data_conn_open"); + return; + } + if (apn == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + const char *apnStr = env->GetStringUTFChars(apn, NULL); + sAGpsInterface->data_conn_open(apnStr); + env->ReleaseStringUTFChars(apn, apnStr); +} + +static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* env, jobject obj) +{ + if (!sAGpsInterface) { + ALOGE("no AGPS interface in agps_data_conn_closed"); + return; + } + sAGpsInterface->data_conn_closed(); +} + +static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* env, jobject obj) +{ + if (!sAGpsInterface) { + ALOGE("no AGPS interface in agps_data_conn_failed"); + return; + } + sAGpsInterface->data_conn_failed(); +} + +static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj, + jint type, jstring hostname, jint port) +{ + if (!sAGpsInterface) { + ALOGE("no AGPS interface in set_agps_server"); + return; + } + const char *c_hostname = env->GetStringUTFChars(hostname, NULL); + sAGpsInterface->set_server(type, c_hostname, port); + env->ReleaseStringUTFChars(hostname, c_hostname); +} + +static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj, + jint notifId, jint response) +{ + if (!sGpsNiInterface) { + ALOGE("no NI interface in send_ni_response"); + return; + } + + sGpsNiInterface->respond(notifId, response); +} + +static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj) +{ + jstring result = NULL; + if (sGpsDebugInterface) { + const size_t maxLength = 2047; + char buffer[maxLength+1]; + size_t length = sGpsDebugInterface->get_internal_state(buffer, maxLength); + if (length > maxLength) length = maxLength; + buffer[length] = 0; + result = env->NewStringUTF(buffer); + } + return result; +} + +static void android_location_GpsLocationProvider_update_network_state(JNIEnv* env, jobject obj, + jboolean connected, jint type, jboolean roaming, jboolean available, jstring extraInfo, jstring apn) +{ + + if (sAGpsRilInterface && sAGpsRilInterface->update_network_state) { + if (extraInfo) { + const char *extraInfoStr = env->GetStringUTFChars(extraInfo, NULL); + sAGpsRilInterface->update_network_state(connected, type, roaming, extraInfoStr); + env->ReleaseStringUTFChars(extraInfo, extraInfoStr); + } else { + sAGpsRilInterface->update_network_state(connected, type, roaming, NULL); + } + + // update_network_availability callback was not included in original AGpsRilInterface + if (sAGpsRilInterface->size >= sizeof(AGpsRilInterface) + && sAGpsRilInterface->update_network_availability) { + const char *c_apn = env->GetStringUTFChars(apn, NULL); + sAGpsRilInterface->update_network_availability(available, c_apn); + env->ReleaseStringUTFChars(apn, c_apn); + } + } +} + +static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* env, + jobject obj) { + if (sGpsGeofencingInterface != NULL) { + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* env, jobject obj, + jint geofence_id, jdouble latitude, jdouble longitude, jdouble radius, + jint last_transition, jint monitor_transition, jint notification_responsiveness, + jint unknown_timer) { + if (sGpsGeofencingInterface != NULL) { + sGpsGeofencingInterface->add_geofence_area(geofence_id, latitude, longitude, + radius, last_transition, monitor_transition, notification_responsiveness, + unknown_timer); + return JNI_TRUE; + } else { + ALOGE("Geofence interface not available"); + } + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_remove_geofence(JNIEnv* env, jobject obj, + jint geofence_id) { + if (sGpsGeofencingInterface != NULL) { + sGpsGeofencingInterface->remove_geofence_area(geofence_id); + return JNI_TRUE; + } else { + ALOGE("Geofence interface not available"); + } + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_pause_geofence(JNIEnv* env, jobject obj, + jint geofence_id) { + if (sGpsGeofencingInterface != NULL) { + sGpsGeofencingInterface->pause_geofence(geofence_id); + return JNI_TRUE; + } else { + ALOGE("Geofence interface not available"); + } + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_resume_geofence(JNIEnv* env, jobject obj, + jint geofence_id, jint monitor_transition) { + if (sGpsGeofencingInterface != NULL) { + sGpsGeofencingInterface->resume_geofence(geofence_id, monitor_transition); + return JNI_TRUE; + } else { + ALOGE("Geofence interface not available"); + } + return JNI_FALSE; +} + +static JNINativeMethod sMethods[] = { + /* name, signature, funcPtr */ + {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, + {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported}, + {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init}, + {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup}, + {"native_set_position_mode", "(IIIII)Z", (void*)android_location_GpsLocationProvider_set_position_mode}, + {"native_start", "()Z", (void*)android_location_GpsLocationProvider_start}, + {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop}, + {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data}, + {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, + {"native_read_nmea", "([BI)I", (void*)android_location_GpsLocationProvider_read_nmea}, + {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, + {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, + {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, + {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, + {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, + {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, + {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, + {"native_agps_set_id","(ILjava/lang/String;)V",(void*)android_location_GpsLocationProvider_agps_set_id}, + {"native_agps_set_ref_location_cellid","(IIIII)V",(void*)android_location_GpsLocationProvider_agps_set_reference_location_cellid}, + {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, + {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response}, + {"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message}, + {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state}, + {"native_update_network_state", "(ZIZZLjava/lang/String;Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state }, + {"native_is_geofence_supported", "()Z", (void*) android_location_GpsLocationProvider_is_geofence_supported}, + {"native_add_geofence", "(IDDDIIII)Z", (void *)android_location_GpsLocationProvider_add_geofence}, + {"native_remove_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_remove_geofence}, + {"native_pause_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_pause_geofence}, + {"native_resume_geofence", "(II)Z", (void *)android_location_GpsLocationProvider_resume_geofence} +}; + +int register_android_server_location_GpsLocationProvider(JNIEnv* env) +{ + return jniRegisterNativeMethods(env, "com/android/server/location/GpsLocationProvider", sMethods, NELEM(sMethods)); +} + +} /* namespace android */ diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp new file mode 100644 index 0000000..151e134 --- /dev/null +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2010 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_TAG "PowerManagerService-JNI" + +//#define LOG_NDEBUG 0 + +#include "JNIHelp.h" +#include "jni.h" + +#include <ScopedUtfChars.h> + +#include <limits.h> + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> +#include <utils/Timers.h> +#include <utils/misc.h> +#include <utils/String8.h> +#include <utils/Log.h> +#include <hardware/power.h> +#include <hardware_legacy/power.h> +#include <suspend/autosuspend.h> + +#include "com_android_server_power_PowerManagerService.h" + +namespace android { + +// ---------------------------------------------------------------------------- + +static struct { + jmethodID wakeUpFromNative; + jmethodID goToSleepFromNative; + jmethodID userActivityFromNative; +} gPowerManagerServiceClassInfo; + +// ---------------------------------------------------------------------------- + +static jobject gPowerManagerServiceObj; +static struct power_module* gPowerModule; + +static Mutex gPowerManagerLock; +static bool gScreenOn; +static bool gScreenBright; + +static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1]; + +// Throttling interval for user activity calls. +static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 500 * 1000000L; // 500ms + +// ---------------------------------------------------------------------------- + +static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { + if (env->ExceptionCheck()) { + ALOGE("An exception was thrown by callback '%s'.", methodName); + LOGE_EX(env); + env->ExceptionClear(); + return true; + } + return false; +} + +bool android_server_PowerManagerService_isScreenOn() { + AutoMutex _l(gPowerManagerLock); + return gScreenOn; +} + +bool android_server_PowerManagerService_isScreenBright() { + AutoMutex _l(gPowerManagerLock); + return gScreenBright; +} + +void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType) { + // Tell the power HAL when user activity occurs. + if (gPowerModule && gPowerModule->powerHint) { + gPowerModule->powerHint(gPowerModule, POWER_HINT_INTERACTION, NULL); + } + + if (gPowerManagerServiceObj) { + // Throttle calls into user activity by event type. + // We're a little conservative about argument checking here in case the caller + // passes in bad data which could corrupt system state. + if (eventType >= 0 && eventType <= USER_ACTIVITY_EVENT_LAST) { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (eventTime > now) { + eventTime = now; + } + + if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) { + return; + } + gLastEventTime[eventType] = eventTime; + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + env->CallVoidMethod(gPowerManagerServiceObj, + gPowerManagerServiceClassInfo.userActivityFromNative, + nanoseconds_to_milliseconds(eventTime), eventType, 0); + checkAndClearExceptionFromCallback(env, "userActivityFromNative"); + } +} + +void android_server_PowerManagerService_wakeUp(nsecs_t eventTime) { + if (gPowerManagerServiceObj) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + env->CallVoidMethod(gPowerManagerServiceObj, + gPowerManagerServiceClassInfo.wakeUpFromNative, + nanoseconds_to_milliseconds(eventTime)); + checkAndClearExceptionFromCallback(env, "wakeUpFromNative"); + } +} + +void android_server_PowerManagerService_goToSleep(nsecs_t eventTime) { + if (gPowerManagerServiceObj) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + env->CallVoidMethod(gPowerManagerServiceObj, + gPowerManagerServiceClassInfo.goToSleepFromNative, + nanoseconds_to_milliseconds(eventTime), 0); + checkAndClearExceptionFromCallback(env, "goToSleepFromNative"); + } +} + +// ---------------------------------------------------------------------------- + +static void nativeInit(JNIEnv* env, jobject obj) { + gPowerManagerServiceObj = env->NewGlobalRef(obj); + + status_t err = hw_get_module(POWER_HARDWARE_MODULE_ID, + (hw_module_t const**)&gPowerModule); + if (!err) { + gPowerModule->init(gPowerModule); + } else { + ALOGE("Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err)); + } +} + +static void nativeSetPowerState(JNIEnv* env, + jclass clazz, jboolean screenOn, jboolean screenBright) { + AutoMutex _l(gPowerManagerLock); + gScreenOn = screenOn; + gScreenBright = screenBright; +} + +static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) { + ScopedUtfChars name(env, nameStr); + acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str()); +} + +static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass clazz, jstring nameStr) { + ScopedUtfChars name(env, nameStr); + release_wake_lock(name.c_str()); +} + +static void nativeSetInteractive(JNIEnv *env, jclass clazz, jboolean enable) { + if (gPowerModule) { + if (enable) { + ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(true) while turning screen on"); + gPowerModule->setInteractive(gPowerModule, true); + } else { + ALOGD_IF_SLOW(20, "Excessive delay in setInteractive(false) while turning screen off"); + gPowerModule->setInteractive(gPowerModule, false); + } + } +} + +static void nativeSetAutoSuspend(JNIEnv *env, jclass clazz, jboolean enable) { + if (enable) { + ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_enable() while turning screen off"); + autosuspend_enable(); + } else { + ALOGD_IF_SLOW(100, "Excessive delay in autosuspend_disable() while turning screen on"); + autosuspend_disable(); + } +} + +// ---------------------------------------------------------------------------- + +static JNINativeMethod gPowerManagerServiceMethods[] = { + /* name, signature, funcPtr */ + { "nativeInit", "()V", + (void*) nativeInit }, + { "nativeSetPowerState", "(ZZ)V", + (void*) nativeSetPowerState }, + { "nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V", + (void*) nativeAcquireSuspendBlocker }, + { "nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V", + (void*) nativeReleaseSuspendBlocker }, + { "nativeSetInteractive", "(Z)V", + (void*) nativeSetInteractive }, + { "nativeSetAutoSuspend", "(Z)V", + (void*) nativeSetAutoSuspend }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); + +#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ + var = env->GetMethodID(clazz, methodName, methodDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method " methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_server_PowerManagerService(JNIEnv* env) { + int res = jniRegisterNativeMethods(env, "com/android/server/power/PowerManagerService", + gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods)); + LOG_FATAL_IF(res < 0, "Unable to register native methods."); + + // Callbacks + + jclass clazz; + FIND_CLASS(clazz, "com/android/server/power/PowerManagerService"); + + GET_METHOD_ID(gPowerManagerServiceClassInfo.wakeUpFromNative, clazz, + "wakeUpFromNative", "(J)V"); + + GET_METHOD_ID(gPowerManagerServiceClassInfo.goToSleepFromNative, clazz, + "goToSleepFromNative", "(JI)V"); + + GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz, + "userActivityFromNative", "(JII)V"); + + // Initialize + for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) { + gLastEventTime[i] = LLONG_MIN; + } + gScreenOn = true; + gScreenBright = true; + gPowerManagerServiceObj = NULL; + gPowerModule = NULL; + return 0; +} + +} /* namespace android */ diff --git a/services/core/jni/com_android_server_power_PowerManagerService.h b/services/core/jni/com_android_server_power_PowerManagerService.h new file mode 100644 index 0000000..0808b80 --- /dev/null +++ b/services/core/jni/com_android_server_power_PowerManagerService.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _ANDROID_SERVER_POWER_MANAGER_SERVICE_H +#define _ANDROID_SERVER_POWER_MANAGER_SERVICE_H + +#include "JNIHelp.h" +#include "jni.h" + +#include <androidfw/PowerManager.h> + +namespace android { + +extern bool android_server_PowerManagerService_isScreenOn(); +extern bool android_server_PowerManagerService_isScreenBright(); +extern void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType); +extern void android_server_PowerManagerService_wakeUp(nsecs_t eventTime); +extern void android_server_PowerManagerService_goToSleep(nsecs_t eventTime); + +} // namespace android + +#endif // _ANDROID_SERVER_POWER_MANAGER_SERVICE_H diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp new file mode 100644 index 0000000..efc34a2 --- /dev/null +++ b/services/core/jni/onload.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2009 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. + */ + +#include "JNIHelp.h" +#include "jni.h" +#include "utils/Log.h" +#include "utils/misc.h" + +namespace android { +int register_android_server_AlarmManagerService(JNIEnv* env); +int register_android_server_ConsumerIrService(JNIEnv *env); +int register_android_server_InputApplicationHandle(JNIEnv* env); +int register_android_server_InputWindowHandle(JNIEnv* env); +int register_android_server_InputManager(JNIEnv* env); +int register_android_server_LightsService(JNIEnv* env); +int register_android_server_PowerManagerService(JNIEnv* env); +int register_android_server_SerialService(JNIEnv* env); +int register_android_server_UsbDeviceManager(JNIEnv* env); +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_location_FlpHardwareProvider(JNIEnv* env); +int register_android_server_connectivity_Vpn(JNIEnv* env); +int register_android_server_AssetAtlasService(JNIEnv* env); +}; + +using namespace android; + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + JNIEnv* env = NULL; + jint result = -1; + + if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { + ALOGE("GetEnv failed!"); + return result; + } + ALOG_ASSERT(env, "Could not retrieve the env!"); + + register_android_server_PowerManagerService(env); + register_android_server_SerialService(env); + register_android_server_InputApplicationHandle(env); + register_android_server_InputWindowHandle(env); + register_android_server_InputManager(env); + register_android_server_LightsService(env); + register_android_server_AlarmManagerService(env); + register_android_server_UsbDeviceManager(env); + register_android_server_UsbHostManager(env); + register_android_server_VibratorService(env); + register_android_server_SystemServer(env); + register_android_server_location_GpsLocationProvider(env); + register_android_server_location_FlpHardwareProvider(env); + register_android_server_connectivity_Vpn(env); + register_android_server_AssetAtlasService(env); + register_android_server_ConsumerIrService(env); + + + return JNI_VERSION_1_4; +} |