还是之前公司的那个需求,为满足客户需要,必须能够远程控制手下手机的相机和录音功能,之前能够使用设备管理器来禁用摄像头,但是毕竟需要手机使用者来手动点击确定激活的选项,这点就不是很美丽,虽然那个点击的操作可以使用自动化服务来实现,但是有点大题小做并且效果还是不理想,所以我使用的禁用android底层相机和录音的api,通过修改android底层代码实现 功能失效,而通过反编译手机自带的相机录音的apk虽然也能够实现失效的效果,但是毕竟这只是一个apk,人家在下载个其他的照相或录音的app,你还是拿人家没有办法,所有相关的应用都会使用Camera、AudioRecord或是MediaRecorder类实现的照相 和 录制语音的功能,让我们查询相关底层代码,修改部分代码就能够一举多得的让所有调用这三个类的所有app都失效啦~
话不多说直接上代码~ 关于xposed模块配置方面我就不介绍了...不会的同学可以看我的其他帖子或是去google一下,关于那三个类可以在AS上引出这三个包,然后ctri + 左键 点击进去查看源码,里面API介绍的很全面,对于是英文看不懂的同学可以再AS里面装一个自动翻译的插件,我可以推荐一个AS翻译的插件你们可以试试~无网络无法使用哦~网址如下所示点击就能进入(安装教程也讲解的很详细):
点击打开链接
private void hookCamera(XC_LoadPackage.LoadPackageParam loadPackageParam) {
try {
XposedHelpers.findAndHookMethod("android.hardware.Camera", loadPackageParam.classLoader, "open", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
if (ModelHelper.getGZipModel("Audio.qwer.com","disable").equals("disable")){
param.setResult(null);
Log.e("pptppt", "试试吧");
}
}
});
XposedHelpers.findAndHookMethod("android.hardware.Camera", loadPackageParam.classLoader, "open",
int.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
if (ModelHelper.getGZipModel("Audio.qwer.com","disable").equals("disable")){
param.setResult(null);
Log.e("pptppt", "试试吧 啊哈哈");
}
}
});
} catch (Exception e) {
}
}
通过阅读Camera 的API 只要禁用掉open方法的返回值就能够实现无法开启摄像头操作,接下来是禁用语音录音功能:
private void hookRecord(XC_LoadPackage.LoadPackageParam loadPackageParam) {
try {
Class> aClass = XposedHelpers.findClass("android.media.MediaRecorder", loadPackageParam.classLoader);
XposedBridge.hookAllMethods(aClass, "setOutputFile", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
if (ModelHelper.getGZipModel("Audio.qwer.com","disable").equals("disable")){
param.args[0] = null;
Log.e("pptppt", "lalala1");
}
}
});
/*********************************************************************************/
XposedHelpers.findAndHookMethod("android.media.AudioRecord", loadPackageParam.classLoader, "getState", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
if (ModelHelper.getGZipModel("Audio.qwer.com","disable").equals("disable")){
param.setResult(0);
Log.e("pptppt", "getState");
}
}
});
}catch (Exception e){
}
}
不知道你们观察出来了没有,在修改参数或返回值的时候,我在外侧都会加一个if判断,读取一个压缩文件中的string字符串,不满足条件的时候在禁止,这样做是为了能够更灵活的实现禁用效果,当不需要禁用的时候就重新写入压缩文件中的内容就可以了~~ 如下是ModelHelper类的代码:
import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public final class ModelHelper {
public static final String DIR = "/sdcard/";
/**
* by kevin 07-03
* 解压文件 获得string数据
* @param modelName 文件名
* @param value 取不到或解压出错时 用来替代的默认数据
* @return 返回数据 成功为modelName文件的内容 失败为value默认内容
*/
public static String getGZipModel(String modelName, String value) {
try {
File file = new File(DIR + modelName);
if (!file.isFile() || !file.exists()) {
return value;
}
byte [] buffer = new byte[10240];
GZIPInputStream gis = new GZIPInputStream(new FileInputStream(file));
int cnt = gis.read(buffer);
gis.close();
return new String(buffer, 0, cnt, "utf-8");
} catch (Exception e) {
e.printStackTrace();
return value;
}
}
/**
* by kevin 07-03
* set cloud.sv 设置数据内容
* @param fileName 压缩文件名字
* @param value 写入的内容
*/
public static void setGZipModel(String fileName,String value){
try {
FileOutputStream fos = new FileOutputStream(DIR + fileName);
GZIPOutputStream gos = new GZIPOutputStream(fos);
gos.write(value.getBytes());
gos.flush();
gos.finish();
}catch (Exception e){
Log.e("qswx", "写入文件内容出错或不存在");
}
}
/**
* by kevin 06-20
* 读文件的方法
* @param modelName 文件名
* @param value 没有这个文件或这不是文件的时候 返回的String类型信息
* @return 返回的是文件中的数据信息 为String类型
*/
public static String getModel(String modelName, String value) {
try {
File file = new File(DIR + modelName);
if (!file.isFile() || !file.exists()) {
return value;
}
Reader inputStreamReader = new InputStreamReader(new FileInputStream(file), "UTF-8");
String str3 = new BufferedReader(inputStreamReader).readLine();
inputStreamReader.close();
return str3;
} catch (Exception e) {
e.printStackTrace();
return value;
}
}
/**
* by kevin 06-20
* 写入String字符串 写入到指定文件中
* @param modelName 指定文件的文件名字
* @param value 写入到文件的String字符串
*
* BufferedWriter 写入到缓存区
* .flush()清空提交 不要忘记写
*/
public static void setModel(String modelName, String value) {
BufferedWriter bufferedWriter = null;
try {
bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(DIR + modelName), false), "UTF-8"));
bufferedWriter.write(value);
bufferedWriter.flush();
bufferedWriter.close();
} catch (Exception e3) {
e3.printStackTrace();
try {
if(bufferedWriter != null) {
bufferedWriter.close();
}
} catch (Exception e5) {
}
}
}
}