系统需求
- Android内核须7.1.2或以上,并且已被root,root指通过“su”可以提升到管理员权限
- 板上CPU至少能硬编码屏幕尺寸的H264。
- 板上应有Wifi和蓝牙模块(查询、修改IP必须)。
以下说到具体操作时以Firefly AIO-3399J为实例(Android7.1 Industry固件)。
一、libkosapi.so
1.1 编译
- 从“Launcher+kdesktop”下载launcher-xxxx.rar,解压缩,取出目录:aosp-7.1.2。
- 复制aosp下的libs/kosapi到<aosp>/frameworks/native/libs/kosapi。
- 复制aosp下的include/kosapi到<aosp>/frameworks/native/include/kosapi。
- 编译libkosapi。mmm ./frameworks/native/libs/kosapi/。成功后<aosp>/out/target/product/rk3399_firfly_aio/system/lib下会有libkosapi.so。
目前Launcher只支持32位,因而只要能编译出32位libkosapi.so。kosapi下有多个*.cpp,具体用了哪些见Android.mk。存在其它cpp是因为这库本来是写给kos,当中函数将来可能会用到,就留着。以下是此版本libkosapi.so需要实现的API。
// 获取libkosapi.so版本,不要修改 void kosGetVersion(char* ver, int /*max_bytes*/) { strcpy(ver, "1.0.3-20220801"); }
更多libkosapi.so细节参考“libkosapi api”。
1.2 让kosapi.so成为系统共享库
需要两个步骤。1)修改<aosp>/build/target/product/base.mk,让把libkosapi.so放入设备的/system/lib。2)libkosapi.so虽然已放在设备的/system/lib,但身份不是可用的共享库,执行时Launcher还是会报找不到libkosapi.so。修改public.libraries.android.txt,即在调用android_init_namespaces时,public_libraries会有libkosapi.so。
<aosp>/system/core/rootdir/etc/public.libraries.android.txt ----------------- libandroid.so libc.so ... libkosapi.so
public.libraries.android.txt在设备上的位置是“/etc/public.libraries.txt”。
二、让com.kos.launcher拥有访问surfaceflinger权限
E/SurfaceFlinger: Permission Denial: can't access SurfaceFlinger pid=1750, uid=10055
com.kos.launcher没有“android.permission.ACCESS_SURFACE_FLINGER”权限,导致报上面这错误。
<aosp>/frameworks/native/services/surfaceflinger/SurfaceFlinger_hwc1.cpp ------ status_t SurfaceFlinger::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { ...... case GET_HDR_CAPABILITIES: { // codes that require permission check IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); if ((uid != AID_GRAPHICS && uid != AID_SYSTEM) && !PermissionCache::checkPermission(sAccessSurfaceFlinger, pid, uid)) { ALOGE("Permission Denial: " "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); // return PERMISSION_DENIED; } break; } case CAPTURE_SCREEN: ...... } }
因为com.kos.launcher没有“android.permission.ACCESS_SURFACE_FLINGER”权限,PermissionCache::checkPermission会返回false,导致onTransact返回失败PERMISSION_DENIED。注释掉“return PERMISSION_DENIED”,让忽略这权限检查。
三、让com.kos.launcher拥有读写/dev/uinput目录权限
要授权other能用/dev/uinput创建设备文件。
1750-1910/com.kos.launcher E/libkosapi-sendinput: Cannot open /dev/uinput: Permission denied. <aosp>/system/core/rootdir/ueventd.rc /dev/uinput 0660 system net_bt_stack 改为 /dev/uinput 0666 system net_bt_stack
四、IP处理相关
4.1 修改netd.rc,让other组可以读写netd这个socket
<aosp>/system/netd/server/netd.rc socket netd stream 0660 root system 改为 socket netd stream 0666 root system
这么做的原因是,libkosapi.so要打开netd这个socket,以便接收系统ip发生变化的广播,以及向/system/netd模块发命令。这个地方没改,或改错了,直观上很难查,但这个修改又很重要。可运行launcher,然后检查logcat输出,搜下面这条输出,最后“71”会有不同。
D/NetdListener: [NetdListener.cpp]kosRunNetdListener, socket_local_client(netd, ...), sock: 71
示例“sock: 71”,那就是libkosapi.so成功打开netd,修改成功。如果sock后的值“<= 0”,那这里没改正确。
github的netd.rc同级目录还有CommandListener.cpp、CommandListener.h,当中改动是早期为修改有线网IP,使用下来这种方法问题很多。后来修改IP使用WIfi后,这种方法就没必要用了。留着这两个文件只是将来可能会做参考。
4.2 位置信息
<aosp>/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java --- public class WifiServiceImpl extends IWifiManager.Stub { public List<ScanResult> getScanResults(String callingPackage) { ... try { if (!canReadPeerMacAddresses && !isActiveNetworkScorer && !isLocationEnabled(callingPackage)) { // return new ArrayList<ScanResult>(); } ... } }
注释掉isLocationEnabled==false时返回“new ArrayList<ScanResult>()”。这么做的原因是,android系统设置存在个叫”位置信息”的入口。有些设备为隐私,会关掉位置信息,这时isLocationEnabled就会返回false。后者会影响修改IP,那里要调用WifiServiceImpl.getScanResults,它将总得到空的Wifi列表。
五、让支持鼠标右键,垂直、水平滚动
怎么修改见“kOS(13):inputflinger—InputReader线程[1] ”中“让SingleTouchInputMapper支持鼠标右键、滚动”。Github有修改后的InputReader.cpp、InputReader.h。
六、存在远程连接时,不要让弹出软键盘
即使不改,依Android内在行为正常弹出,影响的也只是Client是PC场景,不追求极致的,可略过此处改动。注:Client是iOS、Android时,不能让此处改动生效,因为它们输入靠的是android软键盘。
远程桌面控制时 ,如果Client端是PC操作系统,像Windwos,用client端键盘会比android软键盘方便,android界面弹出软键盘就变成多余了。在launcher, 一旦存在连接,就会创键一个qwert(物理)键盘,连接没了,这键就会销毁。于是不要让弹出软键盘问题,等同存在qwert键盘时、如何让禁用软键盘。对这问题,有的内核可能是不需要改代码。这里描述相关细节。也可参考“Android中外接键盘的检测[2] ”
<aosp>/frameworks/base/core/java/android/inputmethodservice/InputMethodService.java public class InputMethodService extends AbstractInputMethodService { public boolean onEvaluateInputViewShown() { if (mSettingsObserver == null) { Log.w(TAG, "onEvaluateInputViewShown: mSettingsObserver must not be null here."); return false; } if (mSettingsObserver.shouldShowImeWithHardKeyboard()) { return true; } Configuration config = getResources().getConfiguration(); return config.keyboard == Configuration.KEYBOARD_NOKEYS || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES; } }
如果onEvaluateInputViewShown返回true,意味着此时可以弹出软键盘,否则禁止。launcher存在qwer键盘(存在远程连接)时,config.keyboard会是Configuration.KEYBOARD_QWERTY(2),但该函数要返回false,还须要同时满足两个条件,一是mSettingsObserver.shouldShowImeWithHardKeyboard()返回false,二是config.hardKeyboardHidden不是Configuration.HARDKEYBOARDHIDDEN_YES(2)。
第一个条件:mSettingsObserver.shouldShowImeWithHardKeyboard()。会调用Settings.Secure.getInt(mService.getContentResolver(), Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0),这个值依赖SHOW_IME_WITH_HARD_KEYBOARD。
<aosp>/frameworks/base/packages/SettingsProvider/res/values/defaults.xml <!-- Default for Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD --> <bool name="def_show_ime_with_hard_keyboard">true</bool> 改为 <bool name="def_show_ime_with_hard_keyboard">false</bool>
第二个条件:config.hardKeyboardHidden值不能是Configuration.HARDKEYBOARDHIDDEN_YES。computeScreenConfigurationLocked函数和这赋值相关。
<aosp>/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java void computeScreenConfigurationLocked(Configuration config) { .... // Let the policy update hidden states. config.keyboardHidden = Configuration.KEYBOARDHIDDEN_NO; // 有的aosp会把这里改成Configuration.HARDKEYBOARDHIDDEN_YES; config.hardKeyboardHidden = Configuration.HARDKEYBOARDHIDDEN_NO; config.navigationHidden = Configuration.NAVIGATIONHIDDEN_NO; mPolicy.adjustConfigurationLw(config, keyboardPresence, navigationPresence); }
七、防杀白名单
Launcher是个普通app,为防止Launcher在低内存时被系统杀死,建议把它加入防杀白名单,像即使Laucher在后台,oom_adj也能保证是0。
如何设置防杀白名单可参考“Android增加系统白名单,防止重要应用低内存时被误杀”。
八、让静默安装launcher
要是不修改,安装launcher时会有两次弹框确认,一是开始安装时“安装”,二是安装成功后“启动”。为什么要静默安装?——升级launcher时,要是不静默安装,安装成功后,老版本launcher已停止运行,新版本launcher却停留在第二次弹框确认界面。由于Luancher未运行,此时远程桌面无法连接,只有重启设备,有了静默安装就不必重启设备了。
<aosp>/packages/apps/PackageInstaller/src/com/android/packageinstaller目录存放了两个文件,那里提供了一种实现静默安装“com.kos.launcher”的方法,对要静默安装的packageName,mAutoInstall会置为true。
九、让可以加载外部存储中的libroseaplt.so
为什么要这么修改的原因参考“运行时加载动态链接库(Android、Windows)”。
9.1 修改linker.cpp,让libroseaplt.so进入灰名单
<aosp>/bionic/linker/linker.cpp ------ static bool is_greylisted(const char* name, const soinfo* needed_by) { static const char* const kLibraryGreyList[] = { "libandroid_runtime.so", "libbinder.so", "libcrypto.so", ... nullptr }; // 增加下以下这个if if (name[0] == '/') { // and reduce the path to basename const char* name2 = basename(name); if (strcmp(name2, "libroseaplt.so") == 0 || strcmp(name2, "libroseaplt2.so") == 0) { return true; } } // limit greylisting to apps targeting sdk version 23 and below if (get_application_target_sdk_version() > 23) { return false; } ... }
9.2 修改mmap.c,(vm_flags & VM_EXEC) == true时,不返回-EPERM
/kernel/mm/mmap.c ------ unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flags, vm_flags_t vm_flags, unsigned long pgoff, unsigned long *populate) { ... if (file) { struct inode *inode = file_inode(file); switch (flags & MAP_TYPE) { case MAP_SHARED: ... case MAP_PRIVATE: if (!(file->f_mode & FMODE_READ)) return -EACCES; if (path_noexec(&file->f_path)) { // 注释掉以下两条语句 // if (vm_flags & VM_EXEC) // return -EPERM; vm_flags &= ~VM_MAYEXEC; } ... default: return -EINVAL } } ... }
十、增加USB转串口驱动
底盘、雷达、机械臂虽然用的是串口协议,但往往会有USB转串口。为此内核需增加USB转串口驱动。
10.1 修改kernel编译配置,增加常用USB转串口驱动
配置是哪个文件,那要看“make”linux内核时用的哪参数。
$make ARCH=arm64 firefly_defconfig
以上是Firefly AIO-3399J编译kernel用的make命令,它对应的配置文件是<aosp>/kernel/arch/arm64/configs/firefly_defconfig。编辑firefly_defconfig,以下修改增加了三种驱动。
<aosp>/kernel/arch/arm64/configs/firefly_defconfig CONFIG_USB_SERIAL_CH341=y CONFIG_USB_SERIAL_CP210X=y CONFIG_USB_SERIAL_PL2303=y
10.2 ueventd.rockchip.rc,让other组可以读写USB转串口驱动的设备节点/dev/ttyUSBn
ttyUSBn权限由660改为666。
---/device/rockchip/common/ueventd.rockchip.rc /dev/ttyUSB0 0666 radio radio /dev/ttyUSB1 0666 radio radio /dev/ttyUSB2 0666 radio radio /dev/ttyUSB3 0666 radio radio /dev/ttyUSB4 0666 radio radio /dev/ttyUSB5 0666 radio radio /dev/ttyUSB6 0666 radio radio /dev/ttyUSB7 0666 radio radio /dev/ttyUSB8 0666 radio radio /dev/ttyUSB9 0666 radio radio #for 4G Gongle #quectel port /dev/ttyUSB* 0666 radio radio
如果有/dev/ttyUSB*,也要置为0666
参考
- kOS(13):inputflinger—InputReader线程 https://zhuanlan.zhihu.com/p/196635542
- Android中外接键盘的检测 https://segmentfault.com/a/1190000021080958