① 各位大神们,速来。。 小弟用android官方的api能够对U盘等存储设备进行识别,但是对于hid的设备却不能识别
可能是驱动问题
② Android之隐藏api介绍
..
Android P 引入了针对非 SDK 接口(俗称为隐藏API)的使用限制。这是继 Android N上针对 NDK 中私有库的链接限制之后的又一次重大调整。
从今以后,不论是native层的NDK还是 java层的SDK,我们只能使用Google提供的、公开的标准接口。这对开发者以及用户乃至整个Android生态,当然是一件好事。
但这也同时意味着Android上的各种黑科技有可能会逐渐走向消亡。
公共 SDK 接口是在 Android 框架软件包索引 中记录的那些接口
从 Android Pie 开始,对某些隐藏类、方法和字段的访问受到限制在 Pie 之前,通过简单地使用反射来使用这些隐藏的非 SDK 组件非常容易。
但是,现在当尝试访问时,面向 API 28 (Pie) 或更高版本的应用程序将遇到 ClassNotFoundException、NoSuchMethodError 或 NoSuchFieldException Activity#createDialog() 。
先来看看系统是如何实现这个限制的。
通过反射或者JNI访问非公开接口时会触发警告/异常等,那么不妨跟踪一下反射的流程,看看系统到底在哪一步做的限制。
先来看一下 java.lang.Class.getDeclaredMethod(String) :
其中看一下 ShouldBlockAccessToMember 的调用,如果它返回false,那么直接返回 nullptr ,上层就会抛 NoSuchMethodXXX 异常;也就触发系统的限制了。
源码如下:
继续跟踪下 GetMemberAction方法 :
继续跟踪GetMemberActionImpl方法:
其中调用到了
只要 IsExempted 方法返回 true,就算这个方法在黑名单中,依然会被放行然后允许被调用。
IsExempted 方法:
继续跟踪传递进来的参数 runtime->GetHiddenApiExemptions() 发现也是 runtime 里面的一个参数.
这样就可以直接修改 hidden_api_exemptions_ 绕过去限制。
Java 层的,有一个对应的 VMRuntime.setHiddenApiExemptions 方法,通过 VMRuntime.setHiddenApiExemptions 设置下豁免条件,就能愉快滴使用反射了。
IsExempted 方法里面调用 DoesPrefixMatch 方法。DoesPrefixMatch是对方法签名进行前缀匹配。所有Java方法类的签名都是以 L 开头,这样就可以直接传个 L 进去,所有的隐藏API全部被赦免了!
另一种绕过 Android P以上非公开API限制的办法
③ android hid是什么意思
机器人藏起来了,Android 除了安卓系统的意思,还有机器人的意思
④ android怎样调用@hide和internal API
Android有两种类型的API是不能经由SDK访问的。
第一种是位于com.android.internal包中的API。我将称之为internal API。第二种API类型是一系列被标记为@hide属性的类和方法。从严格意义上来讲,这不是一个单一的API,而是一组小的被隐藏的API,但我仍将其假设为一种API,并称之为hidden API。
Hidden API 例子
你可以查看一下android的源码,并能找到一些变量、函数和类等,都被@hide属性标记了。
下面的例子就是在WifiManager(API 10源码)中隐藏的变量。
另一个例子是在WifiManager(API 10源码)中隐藏了setWifiApEnabled函数。
因此,只要你看到@hide属性,那你看到的就是hidden API。
Internal和hidden API的区别
Hidden API之所以被隐藏,是想阻止开发者使用SDK中那些未完成或不稳定的部分(接口或架构)。举个例子,Bluetooth API在API 5(Android 2.0)上才开放;在API 3 和4上都是用@hide属性隐藏了。当这些API被验证和清理后,Google的开发者会移除@hide属性,并让其在API 5官方化。很多地方在API 4 和5之间发生了变化。如果你的程序依赖某些隐藏的API,当其部署到新的平台上时,就有可能陷入困境。
对于internal API来说,从来都没有计划将其开放出来。它就是Android的“内部厨房”,对开发者来说,应该将其视作黑盒。凡事都会有变化的。如果你依赖某些internal API,也有可能在新的Android release上,这些internal API发生变化,从而令你失望。
总结一下区别:
Hidden API = 进行中的工作;
Internal API = 黑盒;
Internal和hidden API的编译时 vs. 运行时
当你使用Android SDK进行开发的时候,你引用了一个非常重要的jar文件——android.jar。它位于Android SDK平台的文件夹中(SDK_DIR/platforms/platform-X/android.jar,其中,X表示API等级)。这个android.jar移掉了com.android.internal包中所有的类,也移掉了所有标记有@hide的类,枚举,字段和方法。
但当你在设备上启动应用程序时,它将加载framework.jar(简单来说,它和android.jar等同),而其未移掉internal API和hidden API。(但它对开发者来说,并不能友好地访问,因此,我将向大家展示不通过反射如何使用这些API)。
关于internal API,还有一件事需要说明。Eclipse的ADT插件增加了一个额外的规则,那就是禁止使用com.android.internal包中的任何东西。所以,即便是我们可以拿到最原始的android.jar(未删减版),也没有轻松的办法通过Eclipse使用这些internal API。
你可以亲自检查一下。创建一个新的Android工程(或者使用已有的)。查看一下它引用的类库(右击project Properties –> Java Build Path –> Libraries)。
重要的总结:internal和hidden API在SDK中是按照一样的方式处理的(都从android.jar中移除了),但internal API更惨的是,还被Eclipse的ADT插件显式禁止了。
不通过反射使用internal和hidden API
这些文章的终极目标是让开发者能够不通过反射使用Internal和Hidden API。如果你完成了接下来部分中描述的步骤,你将能使用这些Internal和Hidden API,如同公开的API。你不再需要使用反射。
注:如果你正在使用这些非公开的API,你必须知道,你的程序有着极大的风险。基本上,无法保证在下一次的Android OS更新时,这些API不被破坏,也无法保证不同的运营商有着一致的行为。你自己决定吧。
接下来有三个场景:
1. Internal 和hidden API都可用(场景A)
2. 只Hidden API可用(场景B)
3. 只Internal API可用(场景C)
场景A是B、C的总和。场景B是最简单的一个(不需要对Eclipse的ADT修改)。
场景A:阅读Part1, 2, 3, 4, 5
场景B:阅读Part1, 2, 3, 5
场景C:阅读Part1, 2, 3, 4, 5
我解释了为什么我们不通过反射就会很难使用internal和hidden API。这是因为android.jar中就没包含这些API,因此,没人能够在编译时引用这些类。
这篇文章将描述如何还原最初的android.jar。这将允许我们像使用公开的API那样使用internal和hidden API。
如何得到原版android.jar?
我们需要修改android.jar,这样它才能包含所有的*.class文件(包括internal和hidden API类)。有两种办法:
1) Android是一个开源工程。我们可以下载源码并搭建编译环境,这样它就不能移除那些internal和hidden的类了。这个办法比较困难;
2) 每个模拟器或真机在运行时都会有一个等同android.jar的东西。我们可以从这里拿到jar文件,提取出原始的.class文件,并拷贝到Android SDK的android.jar中。
我将采用方案2。它易于开始,还不需要搭建Linux环境及编译环境等。
从设备上获取framework.jar
你可以使用命令行(adb pull)从模拟器或设备上下载文件,或者使用DDMS(借助Eclipse或SDK中的应用)。
注意:模拟器通常在.dex文件中包含代码,而真机一般在优化版的dex文件中包含代码——odex文件。操作odex文件比较困难,这也是为什么我选择模拟器的原因。
与Android SDK中的android.jar等同的文件是framework.jar。这个文件位于设备的:/system/framework/framework.jar
adb pull /system/framework/framework.jar
当framework.jar从设备上下下来之后,重命名为framework.zip并解压到独立的文件夹中,看起来是这个样子的:
classes.dex正是我们需要的。
创建framework-classes.zip
首先,我们需要把.dex文件转换成.jar格式。你可以使用通用的工具dex2jar。只需要运行:
dev2jar classes.dex
当转换结束时,你应该得到了classes.dex.dex2jar.jar文件。重命名为framework-classes.zip。使用zip查看器,进入到framework-classes.zip/com/android/internal/:
恭喜你,你已经拥有了所有的.class文件,包括internal和hidden API(尽管截图只确认了internal部分)。
创建original-android.jar
Android SDK的android.jar位于ANDROID_SDK/platforms/android-X/android.jar(X表示API等级)。
拷贝android.jar成custom-android.jar。解压至custom-android文件夹。将framework-classes.zip中所有的.class文件拷贝到custom-android文件夹中(你需要覆盖所有已经存在的.class文件)。
然后,压缩custom-android文件成original-android.zip。重命名为original-android.jar。
步骤总结
1. 选择你的目标平台X
2. 创建目标平台X的模拟器
3. 启动模拟器,下载/system/framework/framework.jar
4. 重命名framework.jar -> framework.zip
5. 从framework.zip中抽取classes.dex
6. 使用dex2jar工具,将其转换成classes.jar
7. 重命名classes.jar -> framework-classes.zip
8. 拷贝android.jar –> custom-android.zip
9. 解压custom-android.zip至custom-android文件夹
10. 将framework-classes.zip中所有文件拷贝至custom-android文件夹(覆盖存在的文件)
11. 压缩custom-android文件夹成original-android.zip
12. 重命名original-android.zip -> original-android.jar
打完收功。
⑤ 如何调用Android隐藏API
一、如何隐藏API的? 1) 在正常情况下,即不加@hide的时候,所有的public的类或者方法,在编译时都会编译生成到stub library的jar文件中。这个stub library其实是个空实现,但是它包含了所有的public方法。所以用这个stub library,应用开发者就可以在eclipse中进行开发了。 2) 但一旦加了@hide,那么在编译生成stublibrary时,凡是被@hide标记的类或者方法都被移除了。所以应用开发者就无法通过这个stub library“直接”调用被隐藏的类或者方法。如果强行在eclipse里调用隐藏的类或者方法,则eclipse会报错。典型的例子就是Android SDK中的android.jar,这个stub library所包含的就是已经被移除的的API。上图中,ActivityManager的forceStopPackage()方法在Android SDK中是无法找到的。 二、隐藏的API可以调用吗? 1) 那么应用开发者可以使用这些隐藏的API吗? 答案是肯定的。在真实的运行环境中,所有的API都是存在的并且是被实现的。那么很容易就会想到用“反射”。如果我们已经知道目标类的类名和方法名,以及参数列表,那么这样的方法即使被@hide了,我们依然可以通过反射来调用它。对于Java反射,这里我们不准备,不是很了解的同学请自行学习研究。下面是一个反射的例子。 IActivitManageram = ActivityManagerNative.getDefault(); Method forceStopPackage = am.getClass().getDeclaredMethod("forceStopPackage", String.class); forceStopPackage.setAccessible(true); forceStopPackage.invoke(am, yourpkgname); 2) 反射虽好,不过写起来真麻烦,本来一个调用代码,一行就可以搞定,但是现在用发射写的话,需要好多行。有没有其他更加方便的办法呢,其实是有的。上面我们提到在Android SDK中的android.jar是一个阉割版,如果我们能生成一个完整版,这个问题就迎刃而解了。方法是现在整套Android源代码,然后做一次完整的编译。在out/target/common/obj/JAVA_LIBRARIES/目录下可以根据需要提取自己所要的stublibrary。以framework为例:将out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar复制到eclipse开发环境中,用userlibrary的方式挂载,使其的优先级比android.jar要高即可。 以下是在eclipse中的设置步骤: <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op> <ignore_js_op> 三、需要注意的一些问题 1) 无论是反射还是使用自编译的stublibrary,只能解决调用隐藏API的问题,而无法越过权限检查。 2) Google之所以将有些API隐藏,有些原因可能是因为这些API属于内部逻辑,不想对外暴露,也有可能是API接口还未最终确定下来。所以在低版本Android上的隐藏API不一定能在高版本的Android上使用。这点是一定要注意的。也就说隐藏API的兼容性比较差。因此利用反射调用隐藏API时,一定要注意根据Android的版本采用不同的方式去反射。
⑥ Android可以实现跟HID设备的通讯吗Java/Linux
可以的Android SDK提供类跟方法可以实现跟HID设备通信,例如
UsbDeviceConnection 类的controlTransfer接口就可以实现。
⑦ Android可以实现跟HID设备的通讯吗
HID设备是USB接口的,Android支持USB的读取和操作。所以可以。
⑧ Android 蓝牙HID模式怎么向HID蓝牙设备发送数据
要想实现这个目标,首先要想一想android设备和pc设备之间的通讯基础是什么?这个通讯技术必须是android和pc同时支持的,目前看来也就是wifi,蓝牙。首先说一下蓝牙,蓝牙是一个提供个人局域网的安全无线电通讯技术,相对于wifi而言,蓝牙的功耗相对较低,尤其是BLE技术使得蓝牙的功耗可以和zigbee媲美了,并且android也支持了基于蓝牙的socket操作。但是pc上的java部分对于蓝牙的socket支持就不是很好了,实现起来比较麻烦。但是wifi虽然功耗相对蓝牙而言比较高了点,但是实现起来非常容易,就是socket就好了!所以在第一版本中,可以先使用wifi作为传输技术。
⑨ android bluetooth hid协议的开发求助
Android Bluetooth HID实现详解
Android 关于蓝牙的部分使用的是BlueZ协议栈。但是直到目前2.3.3都没有扩展HID的profile,只是实现了最基本的Handset和d2dp的profile,所以我们的工作涉及到从应用到jni三层的修改,具体修改文件如图所示,绿色表示新建的类,橙色表示修改的类。
一. 本地层
路径:framework/base/core/jni/
参照android_server_BluetoothA2dpService.cpp新建android_server_bluetoothHidServer.cpp。该类中主要是通过dbus对bluez协议栈的访问,dbus 的通用方法都在android_bluetooth_common.cpp中实现,我们做的仅仅是通过dbus_func_args_async调用到bluez提供的input接口。
主要实现以下两个方法函数:
static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
c_path, "org.bluez.Input", "Connect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
return ret ? JNI_TRUE : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
jstring path) {
#ifdef HAVE_BLUETOOTH
LOGV(__FUNCTION__);
if (nat) {
const char *c_path = env->GetStringUTFChars(path, NULL);
bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
c_path, "org.bluez.Input", "Disconnect",
DBUS_TYPE_INVALID);
env->ReleaseStringUTFChars(path, c_path);
return ret ? JNI_TRUE : JNI_FALSE;
}
#endif
return JNI_FALSE;
}
这里要注意将该文件添加到AndroidRuntime.cpp和Android.mk中,否则不会编译到动态库中。
此部分编译后最终生成libandroid_runtime.so并替换到system/libs下
二.Framework的java部分
路径framework/base/java/android/server/中添加BluetoothHidService.java文件
路径framework/base/java/android/bluetooth/中添加BluetoothHid.java和IBluetoothHid.aidl文件。
interface IBluetoothHid {
boolean connect(in BluetoothDevice device);
boolean disconnect(in BluetoothDevice device);
int getState(in BluetoothDevice device);
boolean setPriority(in BluetoothDevice device, int priority);
int getPriority(in BluetoothDevice device);
}
BluetoothHid.java中主要的两个方法connect和disconnect间接地通过aidl访问BluetoothHidService。这里主要是实现跨进程并为上层提供可直接访问的方法。
由此framework的主要部分打包生成framework.Jar并最终部署到system/framework里。
三.应用(Settings.apk)
最后需要修改应用部分,应用部分的修改点比较分散,不想框架层那样整块模仿A2DP的样子那么方便,但也不是说jni部分有多么容易。反而对于我这种对C语言不熟悉的人来说,修改jni是最头疼得事了。好在蓝牙HID这部分框架层的修改都是整块进行的,理解上还算比价容易。
总的来说在Settings.apk中要修改的文件主要是这么几个:
LocalBluetoothProfileManager.java这里主要提供一个HID的profile以便应用层访问。建一个HIDProfile的class调用framework中的BluetoothHID。实际上就是通过bender机制调用了BluetoothHidService。
CashedBluetoothDevice中添加显示蓝牙键盘的图标,BluetoothPairingDialog中则需要添加一段蓝牙配对验证处理的代码,我是参照i9000中先弹出一个随机数,然后在键盘中敲入相同的随机数即配对成功,具体实现如下:
// HID
if (isDeviceKeyboard(mDevice)) {
String pin = String.format("%06d", Long.valueOf(Math
.abs(new Random().nextLong() % 1000000L)));
mPairingView.setVisibility(View.GONE);
messageView.setText(getString(
R.string.bluetooth_enter_keyboard_pin_msg, pin, name));
byte[] bytePin = BluetoothDevice.convertPinToBytes(pin);
if (bytePin != null) {
mDevice.setPin(bytePin);
}
}
……
}
转载