定制Android内核(Android-7.1.2)

相关文章:定制Android内核(Android-12)

系统需求

  • Android内核须7.1.2或以上,并且已被root,root指通过“su”可以提升到管理员权限
  • 板上CPU至少能硬编码屏幕尺寸的H264。
  • 板上应有Wifi和蓝牙模块(查询、修改IP必须)。

以下说到具体操作时以Firefly AIO-3399J为实例(Android7.1 Industry固件)。

 

一、libkosapi.so

1.1 编译

  1. 从“Launcher+kdesktop”下载launcher-xxxx.rar,解压缩,取出目录:aosp-7.1.2。
  2. 复制aosp下的libs/kosapi到<aosp>/frameworks/native/libs/kosapi。
  3. 复制aosp下的include/kosapi到<aosp>/frameworks/native/include/kosapi。
  4. 编译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

参考

  1. kOS(13):inputflinger—InputReader线程 https://zhuanlan.zhihu.com/p/196635542
  2. Android中外接键盘的检测 https://segmentfault.com/a/1190000021080958

全部评论: 0

    写评论: