系统需求
- 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