第二十期 在Android中修改GPS定位数据的完整方案《手机就是开发板》
https://blog.csdn.net/aggresss/article/details/54323034
现在很多的应用都是基于位置服务的,而且把位置服务作为重要的参考标准,其实这是很不安全的,位置信息的数据未经过任何加密,而且是从我们的手机中发送出去的,所以是可以修改的。这一期我们来探讨一下如何修改手机中的定位信息。太基础的原理我就不多说了,可以参考前几期文章。
先整理一下思路,Android在开发者模式下有一个"允许模拟位置选项",它是location service加载 MOCK location provider 实现的,通过这种线程注入的方式修改GPS信息是hacker们最喜欢的方式,但现在很多应用已经可以检测到这种注入方式而被屏蔽掉,也就是说如果我们只在APP层面上想解决方法总是有被检测出来的可能。那我们就得把问题往深了想,通过修改最底层的GPS数据来欺骗APP,在Framework层面上没有任何修改迹象,这样基于APP层面的检测机制就拿我们没有任何办法。
思路确定后我们来探讨实践路线,首先我们要建立一个管道,让我们想要定位的GPS数据提交到Android操作系统的最底层,也就是Linux Kernel层面;然后我们要修改 GPS的 location report 机制,让它从内核中提取到我们的数据,然后逐层上报到APP层。有点明修栈道暗度陈仓的感觉。
总体来说分成两部实现:1.建立到系统内核的数据管道;2.修改GPS上报机制。
这次实验使用的是闲置的小米3W手机,编译源码采用CyanogenMod-13,具体的编译环境搭建和编译方法请参考前几期文章。
因为Android系统从内核态到APP层要经过很多的层次,所以对于建立数据管道的步骤比较繁琐,我这里分成了5个步骤,对应5个层面来实现,每一步分别对应Android的 Kernel driver,HAL,JNI,Framework,Application。所有的代码我都已上传github中https://github.com/aggresss/PHDemo/ 这一期的代码在VirtualPosition 目录下。
下面描述一下实践步骤:
=============分割线1==============
第一步,修改Kernel driver
进入 kernel/xiaomi/cancro/drivers 目录下,新建vp.h文件
#ifndef _VP_ANDROID_H_
#define _VP_ANDROID_H_ #include <linux/cdev.h>
#include <linux/semaphore.h> #define VP_DEVICE_NODE_NAME "vp"
#define VP_DEVICE_FILE_NAME "vp"
#define VP_DEVICE_PROC_NAME "vp"
#define VP_DEVICE_CLASS_NAME "vp" typedef struct {int toggle;double virtual_latitude;double virtual_longitude;
} VirtualPosition;struct vp_android_dev { int lamp;VirtualPosition val;struct semaphore sem; struct cdev dev;
};
#endif
新建vp.c文件
/*******************************************
*include file and define functions
*******************************************/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/device.h>
#include <asm/uaccess.h> #include "vp.h" /*主设备和从设备号变量*/
static int vp_major = 0;
static int vp_minor = 0; /*设备类别和设备变量*/
static struct class* vp_class = NULL;
static struct vp_android_dev* vp_dev = NULL; /*传统的设备文件操作方法*/
static int vp_open(struct inode* inode, struct file* filp);
static int vp_release(struct inode* inode, struct file* filp);
static ssize_t vp_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos);
static ssize_t vp_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos); /*设备文件操作方法表*/
static struct file_operations vp_fops = { .owner = THIS_MODULE, .open = vp_open, .release = vp_release, .read = vp_read, .write = vp_write,
}; /*访问设置属性方法*/
static ssize_t vp_lamp_show(struct device* dev, struct device_attribute* attr, char* buf);
static ssize_t vp_lamp_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count); /*定义设备属性*/
static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, vp_lamp_show, vp_lamp_store);/*******************************************
*define traditional file access
*******************************************/
/*打开设备方法*/
static int vp_open(struct inode* inode, struct file* filp) { struct vp_android_dev* dev; /*将自定义设备结构体保存在文件指针的私有数据域中,以便访问设备时拿来用*/ dev = container_of(inode->i_cdev, struct vp_android_dev, dev); filp->private_data = dev; return 0;
} /*设备文件释放时调用,空实现*/
static int vp_release(struct inode* inode, struct file* filp) { return 0;
} /*读取设备的寄存器val的值*/
static ssize_t vp_read(struct file* filp, char __user *buf, size_t count, loff_t* f_pos) { ssize_t err = 0; struct vp_android_dev* dev = filp->private_data; /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count < sizeof(dev->val)) { goto out; } /*将寄存器val的值拷贝到用户提供的缓冲区*/ if(copy_to_user(buf, &(dev->val), sizeof(dev->val))) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err;
} /*写设备的寄存器值val*/
static ssize_t vp_write(struct file* filp, const char __user *buf, size_t count, loff_t* f_pos) { struct vp_android_dev* dev = filp->private_data; ssize_t err = 0; /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } if(count != sizeof(dev->val)) { goto out; } /*将用户提供的缓冲区的值写到设备寄存器去*/ if(copy_from_user(&(dev->val), buf, count)) { err = -EFAULT; goto out; } err = sizeof(dev->val); out: up(&(dev->sem)); return err;
} /*******************************************
*define devfs access
*******************************************/
/*读取寄存器lamp的值到缓冲区buf中,内部使用*/
static ssize_t __vp_get_lamp(struct vp_android_dev* dev, char* buf) { int lamp = 0; /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } lamp = dev->lamp; up(&(dev->sem)); return snprintf(buf, PAGE_SIZE, "%d\n", lamp);
} /*把缓冲区buf的值写到设备寄存器lamp中去,内部使用*/
static ssize_t __vp_set_lamp(struct vp_android_dev* dev, const char* buf, size_t count) { int lamp = 0; /*将字符串转换成数字*/ lamp = simple_strtol(buf, NULL, 10); /*同步访问*/ if(down_interruptible(&(dev->sem))) { return -ERESTARTSYS; } dev->lamp = lamp; up(&(dev->sem)); return count;
} /*读取设备属性lamp*/
static ssize_t vp_lamp_show(struct device* dev, struct device_attribute* attr, char* buf) { struct vp_android_dev* hdev = (struct vp_android_dev*)dev_get_drvdata(dev); return __vp_get_lamp(hdev, buf);
} /*写设备属性lamp*/
static ssize_t vp_lamp_store(struct device* dev, struct device_attribute* attr, const char* buf, size_t count) { struct vp_android_dev* hdev = (struct vp_android_dev*)dev_get_drvdata(dev); return __vp_set_lamp(hdev, buf, count);
} /*******************************************
*define proc access
*******************************************/
/*读取设备寄存器lamp的值,保存在page缓冲区中*/
static ssize_t vp_proc_read(char* page, char** start, off_t off, int count, int* eof, void* data) { if(off > 0) { *eof = 1; return 0; } return __vp_get_lamp(vp_dev, page);
} /*把缓冲区的值buff保存到设备寄存器lamp中去*/
static ssize_t vp_proc_write(struct file* filp, const char __user *buff, unsigned long len, void* data) { int err = 0; char* page = NULL; if(len > PAGE_SIZE) { printk(KERN_ALERT"The buff is too large: %lu.\n", len); return -EFAULT; } page = (char*)__get_free_page(GFP_KERNEL); if(!page) { printk(KERN_ALERT"Failed to alloc page.\n"); return -ENOMEM; } /*先把用户提供的缓冲区值拷贝到内核缓冲区中去*/ if(copy_from_user(page, buff, len)) { printk(KERN_ALERT"Failed to copy buff from user.\n"); err = -EFAULT; goto out; } err = __vp_set_lamp(vp_dev, page, len); out: free_page((unsigned long)page); return err;
} /*创建/proc/vp文件*/
static void vp_create_proc(void) {
struct proc_dir_entry *entry;
entry = create_proc_entry(VP_DEVICE_PROC_NAME, 0, NULL);
if(entry)
{
entry->read_proc = vp_proc_read;
entry->write_proc = vp_proc_write;
}
} /*删除/proc/vp文件*/
static void vp_remove_proc(void) { remove_proc_entry(VP_DEVICE_PROC_NAME, NULL);
} /*******************************************
*define load and remove function
*******************************************/
/*初始化设备*/
static int __vp_setup_dev(struct vp_android_dev* dev) { int err; dev_t devno = MKDEV(vp_major, vp_minor); memset(dev, 0, sizeof(struct vp_android_dev)); cdev_init(&(dev->dev), &vp_fops); dev->dev.owner = THIS_MODULE; dev->dev.ops = &vp_fops; /*注册字符设备*/ err = cdev_add(&(dev->dev),devno, 1); if(err) { return err; } /*初始化信号量和寄存器lamp, val的值*/ sema_init(&(dev->sem), 1); dev->lamp = 7777; dev->val.toggle = 1; dev->val.virtual_latitude = 45.104108; dev->val.virtual_longitude = 130.816878; return 0;
} /*模块加载方法*/
static int __init vp_init(void){ int err = -1; dev_t dev = 0; struct device* temp = NULL; printk(KERN_ALERT"Initializing vp device.\n"); /*动态分配主设备和从设备号*/ err = alloc_chrdev_region(&dev, 0, 1, VP_DEVICE_NODE_NAME); if(err < 0) { printk(KERN_ALERT"Failed to alloc char dev region.\n"); goto fail; } vp_major = MAJOR(dev); vp_minor = MINOR(dev); /*分配helo设备结构体变量*/ vp_dev = kmalloc(sizeof(struct vp_android_dev), GFP_KERNEL); if(!vp_dev) { err = -ENOMEM; printk(KERN_ALERT"Failed to alloc vp_dev.\n"); goto unregister; } /*初始化设备*/ err = __vp_setup_dev(vp_dev); if(err) { printk(KERN_ALERT"Failed to setup dev: %d.\n", err); goto cleanup; } /*在/sys/class/目录下创建设备类别目录vp*/ vp_class = class_create(THIS_MODULE, VP_DEVICE_CLASS_NAME); if(IS_ERR(vp_class)) { err = PTR_ERR(vp_class); printk(KERN_ALERT"Failed to create vp class.\n"); goto destroy_cdev; } /*在/dev/目录和/sys/class/vp目录下分别创建设备文件vp*/ temp = device_create(vp_class, NULL, dev, "%s", VP_DEVICE_FILE_NAME); if(IS_ERR(temp)) { err = PTR_ERR(temp); printk(KERN_ALERT"Failed to create vp device."); goto destroy_class; } /*在/sys/class/vp/vp目录下创建属性文件val*/ err = device_create_file(temp, &dev_attr_val); if(err < 0) { printk(KERN_ALERT"Failed to create attribute val."); goto destroy_device; } dev_set_drvdata(temp, vp_dev); /*创建/proc/vp文件*/ vp_create_proc(); printk(KERN_ALERT"Succedded to initialize vp device.\n"); return 0; destroy_device: device_destroy(vp_class, dev); destroy_class: class_destroy(vp_class); destroy_cdev: cdev_del(&(vp_dev->dev)); cleanup: kfree(vp_dev); unregister: unregister_chrdev_region(MKDEV(vp_major, vp_minor), 1); fail: return err;
} /*模块卸载方法*/
static void __exit vp_exit(void) { dev_t devno = MKDEV(vp_major, vp_minor); printk(KERN_ALERT"Destroy vp device.\n"); /*删除/proc/vp文件*/ vp_remove_proc(); /*销毁设备类别和设备*/ if(vp_class) { device_destroy(vp_class, MKDEV(vp_major, vp_minor)); class_destroy(vp_class); } /*删除字符设备和释放设备内存*/ if(vp_dev) { cdev_del(&(vp_dev->dev)); kfree(vp_dev); } /*释放设备号*/ unregister_chrdev_region(devno, 1);
} MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Virtualposition Driver"); module_init(vp_init);
module_exit(vp_exit);
添加 Kconfig 文件
config VPtristate "Virtual Position Driver"default nhelpThis is the virtual position driver.
添加 Makefile 文件
obj-$(CONFIG_VP) += vp.o
修改 drivers/Kconfig 文件 在menu "Device Drivers"和endmenu之间添加一行:
source "drivers/vp/Kconfig"
修改drivers/Makefile文件,添加一行:
obj-$(CONFIG_HELLO) += vp/
修改 arch/arm/configs目录下的cyanogen_cancro_defconfig 文件,在文件末尾加入
# CONFIG_VP
CONFIG_VP=y
=============分割线2==============
第二步,修改HAL
进入 ./hardware/libhardware/include/hardware 目录,新建 vp.h 文件
#ifndef ANDROID_VP_INTERFACE_H
#define ANDROID_VP_INTERFACE_H
#include <hardware/hardware.h>
__BEGIN_DECLS /*定义模块ID*/
#define VP_HARDWARE_MODULE_ID "vp" //typedef enum{false, true} bool;/*define virtual position structrue*/
typedef struct {int toggle;double virtual_latitude;double virtual_longitude;
} VirtualPosition;/*硬件模块结构体*/
struct vp_module_t { struct hw_module_t common;
}; /*硬件接口结构体*/
struct vp_device_t { struct hw_device_t common; int fd; int (*set_val)(struct vp_device_t* dev, VirtualPosition val); int (*get_val)(struct vp_device_t* dev, VirtualPosition* val);
}; __END_DECLS #endif
进入到 hardware/libhardware/modules 目录,新建vp目录,并添加vp.c文件
#define LOG_TAG "VpStub" #include <hardware/hardware.h>
#include <hardware/vp.h>
#include <fcntl.h>
#include <errno.h>
#include <cutils/log.h>
#include <cutils/atomic.h> #define DEVICE_NAME "/dev/vp"
#define MODULE_NAME "Vp"
#define MODULE_AUTHOR "aggresss@163.com" /*设备打开和关闭接口*/
static int vp_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device);
static int vp_device_close(struct hw_device_t* device); /*设备访问接口*/
static int vp_set_val(struct vp_device_t* dev, VirtualPosition val);
static int vp_get_val(struct vp_device_t* dev, VirtualPosition* val); /*模块方法表*/
static struct hw_module_methods_t vp_module_methods = { open: vp_device_open
}; /*模块实例变量*/
struct vp_module_t HAL_MODULE_INFO_SYM = { common: { tag: HARDWARE_MODULE_TAG, version_major: 1, version_minor: 0, id: VP_HARDWARE_MODULE_ID, name: MODULE_NAME, author: MODULE_AUTHOR, methods: &vp_module_methods, }
}; static int vp_device_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { struct vp_device_t* dev;dev = (struct vp_device_t*)malloc(sizeof(struct vp_device_t)); if(!dev) { ALOGE("Vp Stub: failed to alloc space"); return -EFAULT; } memset(dev, 0, sizeof(struct vp_device_t)); dev->common.tag = HARDWARE_DEVICE_TAG; dev->common.version = 0; dev->common.module = (hw_module_t*)module; dev->common.close = vp_device_close; dev->set_val = vp_set_val;dev->get_val = vp_get_val; if((dev->fd = open(DEVICE_NAME, O_RDWR)) == -1) { ALOGE("Vp Stub: failed to open /dev/vp -- %s.", strerror(errno));free(dev); return -EFAULT; } *device = &(dev->common); ALOGI("Vp Stub: open /dev/vp successfully."); return 0;
} static int vp_device_close(struct hw_device_t* device) { struct vp_device_t* vp_device = (struct vp_device_t*)device; if(vp_device) { close(vp_device->fd); free(vp_device); } return 0;
} static int vp_set_val(struct vp_device_t* dev, VirtualPosition val) { ALOGI("Vp Stub: set value %d to device.", val); write(dev->fd, &val, sizeof(val)); return 0;
} static int vp_get_val(struct vp_device_t* dev, VirtualPosition* val) { if(!val) { ALOGE("Vp Stub: error val pointer"); return -EFAULT; } read(dev->fd, val, sizeof(*val)); ALOGI("Vp Stub: get value %d from device", *val); return 0;
}
继续在vp目录下新建Android.mk文件:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
LOCAL_SHARED_LIBRARIES := liblog
LOCAL_SRC_FILES := vp.c
LOCAL_MODULE := vp.default
include $(BUILD_SHARED_LIBRARY)
=============分割线3==============
第三步,修改JNI
进入 frameworks/base/services/core/jni 目录,新建com_android_server_VirtualPositionService.cpp文件
#define LOG_TAG "VirtualPositionService"
#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/vp.h>
#include <stdio.h> namespace android
{ VirtualPosition virtual_position = {1, 0.0, 0.0};/*在硬件抽象层中定义的硬件访问结构体,参考<hardware/vp.h>*/ struct vp_device_t* vp_device = NULL; /*通过硬件抽象层定义的硬件访问接口设置硬件寄存器val的值*/ static void vp_setVal() { ALOGI("VirtualPosition JNI: set value to device."); if(!vp_device) { ALOGI("VirtualPosition JNI: device is not open."); return; } vp_device->set_val(vp_device, virtual_position); } /*通过硬件抽象层定义的硬件访问接口读取硬件寄存器val的值*/ static void vp_getVal() { if(!vp_device) { ALOGI("VirtualPosition JNI: device is not open."); } vp_device->get_val(vp_device, &virtual_position); ALOGI("VirtualPosition JNI: get value from device."); } /*通过硬件抽象层定义的硬件模块打开接口打开硬件设备*/ static inline int vp_device_open(const hw_module_t* module, struct vp_device_t** device) { return module->methods->open(module, VP_HARDWARE_MODULE_ID, (struct hw_device_t**)device); } /*通过硬件模块ID来加载指定的硬件抽象层模块并打开硬件*/ static jboolean vp_init(JNIEnv* env, jclass clazz) { vp_module_t* module; ALOGI("VirtualPosition JNI: initializing......"); if(hw_get_module(VP_HARDWARE_MODULE_ID, (const struct hw_module_t**)&module) == 0) { ALOGI("VirtualPosition JNI: vp Stub found."); if(vp_device_open(&(module->common), &vp_device) == 0) { ALOGI("VirtualPosition JNI: vp device is open."); return 0; } ALOGE("VirtualPosition JNI: failed to open vp device."); return -1; } ALOGE("VirtualPosition JNI: failed to get vp stub module."); return -1; } static void android_server_VirtualPositionService_set_virtual_toggle(JNIEnv* env, jclass clazz, jint tog){virtual_position.toggle = tog;vp_setVal();}static jint android_server_VirtualPositionService_get_virtual_toggle(JNIEnv* env, jclass clazz){vp_getVal();return virtual_position.toggle;}static void android_server_VirtualPositionService_set_virtual_latitude(JNIEnv* env, jclass clazz, jdouble vlat){virtual_position.virtual_latitude = vlat;vp_setVal();}static jdouble android_server_VirtualPositionService_get_virtual_latitude(JNIEnv* env, jclass clazz){vp_getVal();return virtual_position.virtual_latitude;}static void android_server_VirtualPositionService_set_virtual_longitude(JNIEnv* env, jclass clazz, jdouble vlon){virtual_position.virtual_longitude = vlon;vp_setVal();}static jdouble android_server_VirtualPositionService_get_virtual_longitude(JNIEnv* env, jclass clazz){vp_getVal();return virtual_position.virtual_longitude;}/*JNI方法表*/ static const JNINativeMethod method_table[] = { {"init_native","()Z", (void*)vp_init}, {"native_set_virtual_toggle","(I)V",(void*)android_server_VirtualPositionService_set_virtual_toggle},{"native_get_virtual_toggle","()I",(void*)android_server_VirtualPositionService_get_virtual_toggle},{"native_set_virtual_latitude","(D)V",(void*)android_server_VirtualPositionService_set_virtual_latitude},{"native_get_virtual_latitude","()D",(void*)android_server_VirtualPositionService_get_virtual_latitude},{"native_set_virtual_longitude","(D)V",(void*)android_server_VirtualPositionService_set_virtual_longitude},{"native_get_virtual_longitude","()D",(void*)android_server_VirtualPositionService_get_virtual_longitude},}; /*注册JNI方法*/ int register_android_server_VirtualPositionService(JNIEnv *env) { return jniRegisterNativeMethods(env, "com/android/server/VirtualPositionService", method_table, NELEM(method_table)); }
};
修改同目录下的onload.cpp文件,首先在namespace android增加com_android_server_VirtualPositionService函数声明:
namespace android { .............................................................................................. int register_android_server_VirtualPositionService(JNIEnv *env); };
在JNI_onLoad增加register_android_server_VirtualPositionService函数调用:
extern "C" jint JNI_onLoad(JavaVM* vm, void* reserved) { ................................................................................................. register_android_server_VirtualPositionService(env); ................................................................................................. }
修改同目录下的Android.mk文件,在LOCAL_SRC_FILES变量中增加一行:
LOCAL_SRC_FILES:= \ com_android_server_AlarmManagerService.cpp \ com_android_server_BatteryService.cpp \ com_android_server_InputManager.cpp \ com_android_server_LightsService.cpp \ com_android_server_PowerManagerService.cpp \ com_android_server_SystemServer.cpp \ com_android_server_UsbService.cpp \ com_android_server_VibratorService.cpp \ com_android_server_location_GpsLocationProvider.cpp \ com_android_server_VirtualPositionService.cpp \ onload.cpp
=============分割线4==============
第四步,修改Framework
进入到frameworks/base/core/java/android/os目录,新增VirtualPositionService.aidl接口定义文件
package android.os;interface IVirtualPositionService {void setVirtualToggle(int tog);int getVirtualToggle();void setVirtualLatitude(double vlat);double getVirtualLatitude();void setVirtualLongitude(double vlon);double getVirtualLongitude();
}
然后进入 frameworks/base目录,打开Android.mk文件,修改LOCAL_SRC_FILES变量的值,增加IVirtualPosition.aidl源文件:
LOCAL_SRC_FILES += /
....................................................................
core/java/android/os/IVibratorService.aidl /
core/java/android/os/IVirtualPosition.aidl /
core/java/android/service/urlrenderer/IUrlRendererService.aidl /
.....................................................................
进入到frameworks/base/services/java/com/android/server目录,新增VirtualPositionService.java文件
package com.android.server;
import android.content.Context;
import android.os.IVirtualPositionService;
import android.util.Slog; public class VirtualPositionService extends IVirtualPositionService.Stub { private static final String TAG = "VirtualPositionService"; VirtualPositionService() { init_native(); } public void setVirtualToggle(int tog) {native_set_virtual_toggle(tog);}public int getVirtualToggle(){return native_get_virtual_toggle();}public void setVirtualLatitude(double vlat) {native_set_virtual_latitude(vlat);}public double getVirtualLatitude(){return native_get_virtual_latitude();}public void setVirtualLongitude(double vlon) {native_set_virtual_longitude(vlon);}public double getVirtualLongitude() {return native_get_virtual_longitude();}private static native boolean init_native();private static native void native_set_virtual_toggle(int tog);private static native int native_get_virtual_toggle();private static native void native_set_virtual_latitude(double vlat);private static native double native_get_virtual_latitude();private static native void native_set_virtual_longitude(double vlon);private static native double native_get_virtual_longitude();};
修改同目录的SystemServer.java文件,在ServerThread::run函数中增加加载VirtualPositionService的代码:
@Override
public void run() {
....................................................................................
try {
Slog.i(TAG, "DiskStats Service");
ServiceManager.addService("diskstats", new DiskStatsService(context));
} catch (Throwable e) {
Slog.e(TAG, "Failure starting DiskStats Service", e);
}
try {
Slog.i(TAG, "VirtualPosition Service");
ServiceManager.addService("virtualposition", new VirtualPositionService());
} catch (Throwable e) {
Slog.e(TAG, "Failure starting VirtualPosition Service", e);
}
......................................................................................
}
然后需要修改sepolicy文件,具体的文件在github上,请下载使用。
=============分割线5==============
第五步,修改application
APP的文件比较多,请到github上下载,这里只贴具体的逻辑代码:
package com.example.phdemo.myapplication;import android.os.RemoteException;
import android.app.Activity;
import android.os.ServiceManager;
import android.os.Bundle;
import android.widget.CompoundButton;
import android.os.IVirtualPositionService;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ToggleButton;
import android.widget.CompoundButton.OnCheckedChangeListener;public class MainActivity extends Activity implements View.OnClickListener {private final static String LOG_TAG = "com.example.phdemo.virtualposition";private IVirtualPositionService virtualpositionService = null;
private ToggleButton toggleButton = null;
private EditText altitudeValueText = null;private EditText longitudeValueText = null;private Button getButton = null;private Button setButton = null;private Button clearButton = null;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);virtualpositionService = IVirtualPositionService.Stub.asInterface(ServiceManager.getService("virtualposition"));toggleButton=(ToggleButton)findViewById(R.id.toggleButton);
altitudeValueText = (EditText)findViewById(R.id.altitude_value);
longitudeValueText = (EditText)findViewById(R.id.longitude_value);getButton = (Button)findViewById(R.id.button_get);setButton = (Button)findViewById(R.id.button_set);clearButton = (Button)findViewById(R.id.button_clear);getButton.setOnClickListener(this);setButton.setOnClickListener(this);clearButton.setOnClickListener(this);try{ int val_tog = virtualpositionService.getVirtualToggle();if(val_tog == 1){toggleButton.setChecked(true);}else{toggleButton.setChecked(false);}} catch (Exception e) {}toggleButton.setOnCheckedChangeListener(new OnCheckedChangeListener(){
public void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {
toggleButton.setChecked(isChecked);
try{virtualpositionService.setVirtualToggle(isChecked?1:0);}catch(Exception e){}
}
});Log.i(LOG_TAG, "VirtualPosition Activity Created");}@Overridepublic void onClick(View v) {if(v.equals(getButton)) {try {double val_altitude = virtualpositionService.getVirtualLatitude();String text_altitude = String.valueOf(val_altitude);altitudeValueText.setText(text_altitude);
double val_longitude = virtualpositionService.getVirtualLongitude();String text_longitude = String.valueOf(val_longitude);longitudeValueText.setText(text_longitude);
int val_tog = virtualpositionService.getVirtualToggle();if(val_tog == 1){toggleButton.setChecked(true);}else{toggleButton.setChecked(false);}} catch (Exception e) {Log.e(LOG_TAG, "Remote Exception while reading value from GpsLocationProvider.");}}else if(v.equals(setButton)) {try {String text_altitude = altitudeValueText.getText().toString();
String text_longitude = longitudeValueText.getText().toString();double val_altitude = Double.parseDouble(text_altitude);
double val_longitude = Double.parseDouble(text_longitude);virtualpositionService.setVirtualLatitude(val_altitude);
virtualpositionService.setVirtualLongitude(val_longitude);} catch (Exception e) {Log.e(LOG_TAG, "Remote Exception while writing value to GpsLocationProvider.");}}else if(v.equals(clearButton)) {String text = "";altitudeValueText.setText(text);
longitudeValueText.setText(text);}}
}
=============分割线6==============
最后一步,在JNI层面修改location report 机制。
进入 frameworks/base/services/core/jni 目录,修改com_android_server_location_GpsLocationProvider.cpp文件:
在全局变量部分加入
// add by aggresss
static int vp_fd = open("/dev/vp", O_RDWR);
static VirtualPosition vp_val;
修改location_callback函数:
static void location_callback(GpsLocation* location)
{JNIEnv* env = AndroidRuntime::getJNIEnv();//add by aggresssread(vp_fd, &vp_val, sizeof(VirtualPosition));if(vp_val.toggle == 1){env->CallVoidMethod(mCallbacksObj, method_reportLocation, location->flags,(jdouble)vp_val.virtual_latitude, (jdouble)vp_val.virtual_longitude,(jdouble)location->altitude,(jfloat)location->speed, (jfloat)location->bearing,(jfloat)location->accuracy, (jlong)location->timestamp);checkAndClearExceptionFromCallback(env, __FUNCTION__);}else{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__);}
}
=============我是完成的分割线==============
完成后,重新编译固件,开机后启动VirtualPosition的APP,设置你想要的坐标,想在哪就在哪了。
第二十期 在Android中修改GPS定位数据的完整方案《手机就是开发板》相关推荐
- 三个activity之间跳转 数据传递_第二百四十二回:Android中Fragment之间的数据传递概述...
各位看官们大家好,上一回中咱们说的是Android中Activity之间数据传递的例子,这一回咱们说的例子是Fragment之间的数据传递.闲话休提,言归正转.让我们一起Talk Android吧! ...
- Android UI开发第二十九篇——Android中五种常用的menu(菜单)
Android Menu在手机的应用中起着导航的作用,作者总结了5种常用的Menu. 1.左右推出的Menu 前段时间比较流行,我最早是在海豚浏览器中看到的,当时耳目一新.最早使用左右推出菜单的,听说 ...
- android中实现GPS定位功能,Android中实现GPS定位的简单例子
今天弄了一个多小时,写了一个GPS获取地理位置代码的小例子,包括参考了网上的一些代码,并且对代码进行了一些修改,希望对大家的帮助.具体代码如下: 要实用Adnroid平台的GPS设备,首先需要添加上 ...
- 智能化软件开发微访谈·第二十期暨2022新年特辑:AI软件架构实践
CodeWisdom 智能化软件开发沙龙是复旦大学CodeWisdom团队参与组织的专注于代码大数据与智能化软件开发的学术和技术沙龙,面向相关领域的学术界研究者和工业界实践者,通过各种线上和线下交流活 ...
- 数据库管理-第二十期(20210304)
数据库管理 2021-03-04 第二十期 RWP 1 CPU占用率 2 统计信息比你想象中的更重要 3 干同一件事情应当批量处理 4 Arry和Inmemory 5 SQL Monitor 6 如何 ...
- 前端知识小报第二十期
前端知识小报第二十期 内容收藏自网络,本文只是聚合分享文章链接!侵删 本期标签 CSS / 骨架屏 / js / VUE 文章列表 CSS 奇思妙想边框动画 使用Chrome扩展程序生成网页骨架屏 [ ...
- android网络获取经纬,Android中透过GPS或NetWork获取当前位置的经纬度
Android中通过GPS或NetWork获取当前位置的经纬度 private double latitude=0.0; private double longitude =0.0; Location ...
- 开发者论坛一周精粹(第二十期) :晒往期云栖大会的照片或感想,赢2017杭州云栖大会门票...
第二十期(2017年8月21日-2017年8月27日 ) 2017杭州云栖大会即将震撼来袭,如果你参与过往期的云栖大会,不妨晒出你的参会照片和感想,我们将挑选10个优秀的分享送出2017杭州云栖大会的 ...
- Csdn视频第二十期 : 测试工具与流程讨论
Csdn视频第二十期 : 测试工具与流程讨论 http://live.csdn.net/Issue22/LivePlay.aspx
- 火云开发课堂 - 《Shader从入门到精通》系列 第二十节:在Shader中对3D模型进行多纹理混合
<Shader从入门到精通>系列在线课程 优惠链接:http://edu.csdn.net/combo/detail/90 第二十节:在Shader中对3D模型进行多纹理混合 视频地址: ...
最新文章
- 因特网的协议集称为TCP/IP协议集
- java delphi 三层_三层架构delphi+Java+Oracle模式的实现
- 计算机应用基础本模块一测试,广东开放大学远程教育专科2018年秋计算机应用基础Word模块测试...
- 路由器上的usb接口有什么用_路由器的USB接口,非常强大的功能,教您轻轻松松玩转,太实用了...
- ue4 android vulkan,在Android用vulkan完成蓝绿幕扣像
- GitHub提速方法大揭秘,10M速度使用无忧
- 如何更改应用路径_【电脑】实用技巧分享:如何更改电脑桌面路径?
- shell 字符串
- 怎么用eclipse编写python_python用eclipse开发配置
- IE报错,VS定位不到错误的常见原因
- Netty学习4—NIO服务端报错 远程主机强迫关闭了一个现有的连接
- nginx限制并发连接数和连接请求数
- 微信h5 支付,已经获得weixin://wap/pay?prepayid,但是无法调起微信客户端支付
- 学习分布式存储应该从哪几方面着手?
- C#使用iTextSharp打印PDF
- 第二周Java学习总结
- 如果取消Windows Ink后ps画笔没有压感
- java 使用 jacob 实现 将 freemarker 导出的 XML 格式的 excel 转 xls、xlsx 格式
- sql 无法启动错误代码0x7e
- Web 攻防之业务安全:密码找回安全案例总结.