Android 13添加自定义native服务

这篇具有很好参考价值的文章主要介绍了Android 13添加自定义native服务。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

欢迎加入我的知识星球Android系统开发指南

欢迎关注微信公众号 无限无羡
欢迎关注知乎账号 无限无羡

native服务添加

native服务就是用c++写的系统服务,通过init进程启动,可以实现binder接口供client调用。
下面我们以实现一个beanserver的后台服务为例:

  1. 首先需要写一个rc文件
// 文件路径根据自己需求放置
// vendor/zzh/native-service/bean-server/beanserver.rc
service beanserver /system/bin/beanserver
    class main
  1. 写服务的main函数
// vendor/zzh/native-service/bean-server/main_beanserver.cpp
#define LOG_TAG "beanserver"
//#define LOG_NDEBUG 0

#include <hidl/HidlTransportSupport.h>
using namespace android;


int main(int argc __unused, char** argv __unused)
{
    ALOGD(" beamserver start......");
    return 0;
}

这个服务我们启动后只是打印了一行日志就退出了,具体可以根据自己的需求加入自己开机处理的业务。

  1. 编写Android.bp
cc_binary {
	// 最终会生成到/system/bin/beanserver
    name: "beanserver",

    srcs: ["main_beanserver.cpp"],

    header_libs: [
    ],

    shared_libs: [
        "liblog",
        "libutils",
        "libui",
        "libgui",
        "libbinder",
        "libhidlbase",
    ],
    compile_multilib: "first",
    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
        "-Wno-unused-parameter",
    ],
	// 最终会生成到/system/etc/init/beanserver.rc
	// init进程启动时会解析/system/etc/init/目录下的rc文件
    init_rc: ["beanserver.rc"],

    vintf_fragments: [
    ],
}

beanserver.rc在经过编译后会生成到/system/etc/init/beanserver.rc,然后init进程启动的时候就会解析该文件启动进程。

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

init进程解析/system/etc/init/的代码

// system/core/init/init.cpp
// 可以看到init会解析多个目录下的rc文件
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
    Parser parser = CreateParser(action_manager, service_list);

    std::string bootscript = GetProperty("ro.boot.init_rc", "");
    if (bootscript.empty()) {
        parser.ParseConfig("/system/etc/init/hw/init.rc");
        if (!parser.ParseConfig("/system/etc/init")) {
            late_import_paths.emplace_back("/system/etc/init");
        }
        // late_import is available only in Q and earlier release. As we don't
        // have system_ext in those versions, skip late_import for system_ext.
        parser.ParseConfig("/system_ext/etc/init");
        if (!parser.ParseConfig("/vendor/etc/init")) {
            late_import_paths.emplace_back("/vendor/etc/init");
        }
        if (!parser.ParseConfig("/odm/etc/init")) {
            late_import_paths.emplace_back("/odm/etc/init");
        }
        if (!parser.ParseConfig("/product/etc/init")) {
            late_import_paths.emplace_back("/product/etc/init");
        }
    } else {
        parser.ParseConfig(bootscript);
    }
}
  1. 加到编译镜像环境
// device/generic/car/emulator/aosp_car_emulator.mk
// 找到自己项目的mk文件加入
PRODUCT_PACKAGES += beanserver

这样才会将beanserver编译到镜像中。

  1. 编译镜像验证
    编译完启动时看到有如下报错日志:
03-29 07:13:38.364     0     0 I init    : Parsing file /system/etc/init/beanserver.rc...
03-29 07:13:41.706     0     0 E init    : Could not start service 'beanserver' as part of class 'core': File /system/bin/beanserver(labeled "u:object_r:system_file:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials. Note: this error shows up even in permissive mode in order to make auditing denials possible.

提示缺少selinux权限配置,我们可以在Google官网看到label_new_services_and_address_denials

下面我们配置一下selinux权限

selinux权限配置

  1. 编写自定义服务的te文件
// 我这里将新加的selinux配置放在了自己创建目录中
// 注意文件的首尾保留一行空行
// vendor/zzh/sepolicy/vendor/beanserver.te

# beanserver
type beanserver, domain, coredomain;
type beanserver_exec, exec_type, file_type, system_file_type;

init_daemon_domain(beanserver)

  1. 编写file_contexts文件
// vendor/zzh/sepolicy/vendor/file_contexts
// 注意文件的首尾保留一行空行

/system/bin/beanserver   u:object_r:beanserver_exec:s0

添加到sepolicy编译规则

// device/generic/car/emulator/aosp_car_emulator.mk
BOARD_VENDOR_SEPOLICY_DIRS += vendor/zzh/sepolicy/vendor
  1. 编译后启动模拟器
    这个时候能看到服务启动了,但是发现beanserver一直在重启,日志如下:
    native空间的的服务在哪里定义,Android系统开发,android,java,开发语言
    经过排查发生这个现象的原因有两个:
    (1)我们的进程启动后只是打印了一行日志就return 0退出了,通过查看init的代码发现,init进程会通过signal 9去kill掉退出的进程,这里总感觉有点矛盾,可能时init担心进程自己退出的不彻底,所以来个signal 9? init 里的代码如下:
// system/core/init/sigchld_handler.cpp
static pid_t ReapOneProcess() {
    siginfo_t siginfo = {};
    // This returns a zombie pid or informs us that there are no zombies left to be reaped.
    // It does NOT reap the pid; that is done below.
    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {
        PLOG(ERROR) << "waitid failed";
        return 0;
    }

    auto pid = siginfo.si_pid;
    if (pid == 0) return 0;

    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid
    // whenever the function returns from this point forward.
    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we
    // want the pid to remain valid throughout that (and potentially future) usages.
    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });

    std::string name;
    std::string wait_string;
    Service* service = nullptr;

    if (SubcontextChildReap(pid)) {
        name = "Subcontext";
    } else {
        service = ServiceList::GetInstance().FindService(pid, &Service::pid);

        if (service) {
            name = StringPrintf("Service '%s' (pid %d)", service->name().c_str(), pid);
            if (service->flags() & SVC_EXEC) {
                auto exec_duration = boot_clock::now() - service->time_started();
                auto exec_duration_ms =
                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();
                wait_string = StringPrintf(" waiting took %f seconds", exec_duration_ms / 1000.0f);
            } else if (service->flags() & SVC_ONESHOT) {
                auto exec_duration = boot_clock::now() - service->time_started();
                auto exec_duration_ms =
                        std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)
                                .count();
                wait_string = StringPrintf(" oneshot service took %f seconds in background",
                                           exec_duration_ms / 1000.0f);
            }
        } else {
            name = StringPrintf("Untracked pid %d", pid);
        }
    }

	// beanserver退出后,si_code为CLD_EXITED
    if (siginfo.si_code == CLD_EXITED) {
        LOG(INFO) << name << " exited with status " << siginfo.si_status << wait_string;
    } else {
        LOG(INFO) << name << " received signal " << siginfo.si_status << wait_string;
    }

    if (!service) {
        LOG(INFO) << name << " did not have an associated service entry and will not be reaped";
        return pid;
    }

	// 这里将siginfo传入Reap函数
    service->Reap(siginfo);

    if (service->flags() & SVC_TEMPORARY) {
        ServiceList::GetInstance().RemoveService(*service);
    }

    return pid;
}

void Service::Reap(const siginfo_t& siginfo) {
	// 如果没有oneshot属性或者设置了restart的属性,则会调用SIGKILL杀掉进程
	// 这里的oneshot和restart指的是rc文件里面的配置,我们的rc文件只声明了class main
	// 如果没有声明oneshot属性,则服务被杀后会重新启动,如果配置了oneshot则只会启动一次
    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
        KillProcessGroup(SIGKILL, false);
    } else {
        // Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not
        // kill the process group in this case.
        if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {
            // The new behavior in Android R is to kill these process groups in all cases.  The
            // 'true' parameter instructions KillProcessGroup() to report a warning message where it
            // detects a difference in behavior has occurred.
            KillProcessGroup(SIGKILL, true);
        }
    }
    ...
}

为了保证服务不退出,我们先在main函数里加个死循环看下效果

int main(int argc __unused, char** argv __unused)
{
    ALOGD(" beamserver start......");

    while (true) {
        sleep(3);
        ALOGD("beanserver_thread...........");
    }

    return 0;
}

编译后启动模拟器看下启动日志:

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

可以看到目前服务已经正常了,这个服务将一直在后台运行。
如果我们只想开机后执行一些操作后退出,那也可以,我们修改下看看效果。
修改beanserver.rc文件:

service beanserver /system/bin/beanserver
    class main
    oneshot //只启动一次

修改main_beanserver.cpp文件:

#define LOG_TAG "beanserver"
//#define LOG_NDEBUG 0

#include <hidl/HidlTransportSupport.h>
using namespace android;


void quickSort(int arr[], int left, int right) {
    int i = left, j = right;
    int tmp;
    int pivot = arr[(left + right) / 2];

    /* partition */
    while (i <= j) {
        while (arr[i] < pivot)
            i++;
        while (arr[j] > pivot)
            j--;
        if (i <= j) {
            tmp = arr[i];
            arr[i] = arr[j];
            arr[j] = tmp;
            i++;
            j--;
        }
    };

    /* recursion */
    if (left < j)
        quickSort(arr, left, j);
    if (i < right)
        quickSort(arr, i, right);
}

int main(int argc __unused, char** argv __unused)
{
    ALOGD(" beamserver start...");

    int data[] = {9, 21, 3, 66, 100, 8, 36};
    quickSort(data, 0, 7);
    for (int i = 0; i < 7; i++) {
        ALOGD("data[%d]=%d", i, data[i]);
    }

    return 0;
}

将一组数据用快速排序进行排序后输出,然后服务退出,通过日志可以看到,服务退出后init发送了signal 9,到此服务进程退出。

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

通过binder访问服务

先看下文件目录:

如果想要被别的进程调用,就需要实现binder接口,这样才算是一个完整的系统服务。

  1. 定义aidl接口
// vendor/zzh/native-service/bean-server/libbeanservice/aidl/com/zzh/IBeanService.aidl
package com.zzh;

interface IBeanService {
    void sayHello();
}
  1. 加入Android.bp中进行编译
// vendor/zzh/native-service/bean-server/libbeanservice/Android.bp
cc_library_shared {
    name: "libbeanservice_aidl",

    aidl: {
        export_aidl_headers: true,
        local_include_dirs: ["aidl"],
        include_dirs: [
        ],
    },

    srcs: [
        ":beanservice_aidl",
    ],

    shared_libs: [
        "libbase",
        "libcutils",
        "libutils",
        "liblog",
        "libbinder",
        "libgui",
    ],


    cflags: [
        "-Werror",
        "-Wall",
        "-Wextra",
    ],
}

filegroup {
    name: "beanservice_aidl",
    srcs: [
        "aidl/com/zzh/IBeanService.aidl",
    ],
    path: "aidl",
}

make libbeanservice_aidl 后可以看到out下生成的文件:
IBeanService.cpp
IBeanService.h
BpBeanService.h
BnBeanService.h

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

  1. 实现服务,继承BnBeanService
    BeanService.h
// vendor/zzh/native-service/bean-server/libbeanservice/BeanService.h

#ifndef BEANSERVICE_H
#define BEANSERVICE_H

#include <com/zzh/BnBeanService.h>
#include <binder/BinderService.h>

namespace android {

class BeanService:
    public BinderService<BeanService>,
    public virtual ::com::zzh::BnBeanService,
    public virtual IBinder::DeathRecipient
{
public:
    // Implementation of BinderService<T>
    static char const* getServiceName() { return "bean.like"; }

    // IBinder::DeathRecipient implementation
    virtual void        binderDied(const wp<IBinder> &who);
    
    BeanService();
    ~BeanService();
    virtual binder::Status  sayHello();
};

}

#endif

这里BeanService继承了BinderService,必须重写getServiceName函数用来返回服务的名称,后续注册服务时会用到这个名字。

BeanService.cpp

// vendor/zzh/native-service/bean-server/libbeanservice/BeanService.cpp
#define LOG_TAG "BeanService"
//#define LOG_NDEBUG 0

#include "BeanService.h"
#include <iostream>

namespace android {

using binder::Status;

BeanService::BeanService() {

}

BeanService::~BeanService() {

}

/*virtual*/void BeanService::binderDied(const wp<IBinder> &who) {
    ALOGE("%s: Java client's binder died, removing it from the list of active clients, who=%p",
            __FUNCTION__, &who);
}

Status BeanService::sayHello() {
    ALOGD(" BeanService::sayHello ");
    return Status::ok();
}

}

Android.bp

// vendor/zzh/native-service/bean-server/libbeanservice/Android.bp
cc_library_shared {
    name: "libbeanservice",

    srcs: [
        "BeanService.cpp",
    ],

    header_libs: [
    ],

    shared_libs: [
        "libbeanservice_aidl",
        "libbase",
        "libui",
        "liblog",
        "libutils",
        "libbinder",
        "libcutils",
    ],

    static_libs: [
    ],

    include_dirs: [
    ],

    export_shared_lib_headers: [
        "libbinder",
        "libbeanservice_aidl",
    ],

    export_include_dirs: ["."],

    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
        "-Wno-ignored-qualifiers",
    ],

}

cc_library_shared {
    name: "libbeanservice_aidl",

    aidl: {
        export_aidl_headers: true,
        local_include_dirs: ["aidl"],
        include_dirs: [
        ],
    },

    srcs: [
        ":beanservice_aidl",
    ],

    shared_libs: [
        "libbase",
        "libcutils",
        "libutils",
        "liblog",
        "libbinder",
        "libgui",
    ],


    cflags: [
        "-Werror",
        "-Wall",
        "-Wextra",
    ],
}

filegroup {
    name: "beanservice_aidl",
    srcs: [
        "aidl/com/zzh/IBeanService.aidl",
    ],
    path: "aidl",
}
  1. 注册服务
// vendor/zzh/native-service/bean-server/main_beanserver.cpp
#define LOG_TAG "beanserver"
//#define LOG_NDEBUG 0

#include "BeanService.h"
#include <hidl/HidlTransportSupport.h>
using namespace android;


int main(int argc __unused, char** argv __unused)
{
    ALOGD(" beamserver start...");
    signal(SIGPIPE, SIG_IGN);

    // Set 5 threads for HIDL calls. Now cameraserver will serve HIDL calls in
    // addition to consuming them from the Camera HAL as well.
    hardware::configureRpcThreadpool(5, /*willjoin*/ false);

    sp<ProcessState> proc(ProcessState::self());
    sp<IServiceManager> sm = defaultServiceManager();
    ALOGI("ServiceManager: %p", sm.get());
    BeanService::instantiate();
    ALOGI("ServiceManager: %p done instantiate", sm.get());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();
}

BeanService::instantiate()这个函数会调用父类BinderService的方法,最终会调用publish方法进行服务注册。

template<typename SERVICE>
class BinderService
{
public:
	// 进行服务注册的方法,被instantiate()调用
    static status_t publish(bool allowIsolated = false,
                            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
        sp<IServiceManager> sm(defaultServiceManager());
        // 这里进行服务注册,SERVICE::getServiceName()这个就是BeanService重写的方法,返回"bean.like"
        return sm->addService(String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated,
                              dumpFlags);
    }

    static void publishAndJoinThreadPool(
            bool allowIsolated = false,
            int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) {
        publish(allowIsolated, dumpFlags);
        joinThreadPool();
    }

	// 调用publish()方法
    static void instantiate() { publish(); }

    static status_t shutdown() { return NO_ERROR; }
   ...
}
  1. 编译后启动模拟器,看日志:

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

接下来根据提示配置selinux权限
adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy
输出信息如下:

#============= beanserver ==============
allow beanserver servicemanager:binder call;

#============= hal_audio_caremu ==============
allow hal_audio_caremu audioserver:fifo_file write;
allow hal_audio_caremu default_prop:file read;

#============= init ==============
allow init vendor_toolbox_exec:file execute_no_trans;

我们将规则加入beanserver.te即可:
selinux一般情况下并不能一次加完,每次都是添加了log中所报的权限后,再次运行时又会有新的权限错误,我们按照上面的方法逐一添加即可,最终的te文件如下:

// vendor/zzh/sepolicy/vendor/beanserver.te

# beanserver
type beanserver, domain, coredomain;
type beanserver_exec, exec_type, file_type, system_file_type;

init_daemon_domain(beanserver)

allow beanserver servicemanager:binder { call transfer };
allow beanserver beanserver_service:service_manager add;

除了上述修改外,还需要做如下修改:

// vendor/zzh/sepolicy/public/service.te
type beanserver_service,       service_manager_type;
// vendor/zzh/sepolicy/private/service_contexts
bean.like                             u:object_r:beanserver_service:s0
// device/generic/car/emulator/aosp_car_emulator.mk
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS += vendor/zzh/sepolicy/public
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS += vendor/zzh/sepolicy/private

简单解释一下,我们需要为定义的服务定义一个标签,ServiceManager执行addService操作时会进行检查,如果不定义标签的话会使用默认的default_android_service,但Selinux是不允许以这个标签add service的。

// system/sepolicy/public/domain.te
# Do not allow service_manager add for default service labels.
# Instead domains should use a more specific type such as
# system_app_service rather than the generic type.
# New service_types are defined in {,hw,vnd}service.te and new mappings
# from service name to service_type are defined in {,hw,vnd}service_contexts.
neverallow * default_android_service:service_manager *;
neverallow * default_android_vndservice:service_manager *;
neverallow * default_android_hwservice:hwservice_manager *;

再次编译启动模拟器:

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言
native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

到此,我们的服务已经成功的添加到ServiceManager中。

  1. Client通过binder访问服务

我们写一个demo进行访问服务
测试文件:

// vendor/zzh/native-service/bean-server/client/BeanServer_client_test.cpp
#define LOG_TAG "beanclient"
//#define LOG_NDEBUG 0
#include <com/zzh/IBeanService.h>
#include <binder/IServiceManager.h>

using namespace android;
using com::zzh::IBeanService;


int main(int argc __unused, char** argv __unused) {

    ALOGD(" beanclient start...");  
    // 先获取IServiceManager
    sp<IServiceManager> sm = defaultServiceManager(); 
    sp<IBinder> binder;
    do {
    	// 通过服务名称拿到代理对象
        binder = sm->getService(String16("bean.like"));
        if (binder != 0) {
            break;
        }
        usleep(500000); // 0.5s
    } while (true);
	// 转换成IBeanService
    sp<IBeanService>   beanService = interface_cast<IBeanService>(binder);
    // 通过代理调用服务端函数
    beanService->sayHello();
    
    return 0;
}

Android.bp

// vendor/zzh/native-service/bean-server/client/Android.bp
cc_binary {
    name: "beanserver_client",

    srcs: ["BeanServer_client_test.cpp"],

    cflags: [
        "-Wall",
        "-Wextra",
        "-Werror",
        "-Wno-unused-parameter",
    ],

    compile_multilib: "first",

    shared_libs: [
        "libbeanservice_aidl",
        "libbase",
        "libcutils",
        "libutils",
        "liblog",
        "libbinder",
        "libgui",
    ],

    include_dirs: ["."],
}

编译到系统目录

// device/generic/car/emulator/aosp_car_emulator.mk
PRODUCT_PACKAGES += beanserver_client

也可以不加上面的编译选项,直接make beanserver_client,然后将生成的产物adb push到系统目录也可以。

启动模拟器,在终端执行beanserver_client,查看日志:

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

可以看出是不允许shell访问beanserver服务,这里在实际业务中要对client端配置selinux,由于我们这里是测试,就不赘述了。直接setenforce 0进行测试

su
setenforce  0

然后再重新执行beanservere_client

native空间的的服务在哪里定义,Android系统开发,android,java,开发语言

调用成功!

为了方便大家清晰的理解代码结果,下面列出了本例中的代码目录:

zzh@ubuntu:~/work/android/aosp/android-13.0.0_r35/vendor/zzh$ tree
.
├── native-service
│   └── bean-server
│       ├── Android.bp
│       ├── beanserver.rc
│       ├── client
│       │   ├── Android.bp
│       │   └── BeanServer_client_test.cpp
│       ├── libbeanservice
│       │   ├── aidl
│       │   │   └── com
│       │   │       └── zzh
│       │   │           └── IBeanService.aidl
│       │   ├── Android.bp
│       │   ├── BeanService.cpp
│       │   └── BeanService.h
│       └── main_beanserver.cpp
└── sepolicy
    ├── private
    │   └── service_contexts
    ├── public
    │   └── service.te
    └── vendor
        ├── beanserver.te
        └── file_contexts

11 directories, 13 files

上述文件我已经整理好,大家想要的话可以关注我的微信公众号无限无羡,回复"nt" 即可获取,获取后只需修改部分名称,添加编译选项到自己项目的配置文件即可。文章来源地址https://www.toymoban.com/news/detail-652118.html

到了这里,关于Android 13添加自定义native服务的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Android 14.0 添加自定义服务,并生成jar给第三方app调用

    在14.0系统ROM产品定制化开发中,由于需要新增加自定义的功能,所以要增加自定义服务,而app上层通过调用自定义服务,来调用相应的功能,所以系统需要先生成jar,然后生成jar 给上层app调用,接下来就来分析实现的步骤,然后来实现相关的功能 从而来实现所需要的功能 在

    2024年04月10日
    浏览(54)
  • WebService 客户端增加Header头、并且指定命名空间、添加拦截器(日志拦截器,自定义拦截器)、soap:Envelope 添加命名空间

    1.增加Header头 生成XML结果如下 2.添加拦截器 3.soap:Envelope 添加命名空间 生成XML结果如下

    2024年02月10日
    浏览(47)
  • android 13.0 添加系统字体并且设置为默认字体

    在13.0系统定制化开发中,在产品定制中,有产品需求对于系统字体风格不太满意,所以想要更换系统的默认字体,对于系统字体的修改也是常有的功能,而系统默认也支持增加字体,所以就来添加楷体字体为系统字体,并替换为系统默认字体, 接下来就来分析下替换默认字

    2024年02月22日
    浏览(64)
  • Android13系统导航栏添加隐藏导航栏功能按钮

    最近有个项目,客户要求在底部导航栏中添加一个可以隐藏整个导航栏的功能按钮,效果如下图: 具体方法如下: 1. 在frameworks/base做如下修改: 2. 上面修改中有一行代码android:src=\\\"@drawable/ic_sysbar_hide\\\"引用了一个ic_sysbar_hide.xml文件,该文件内容如下:其实就是那个隐藏按钮的

    2024年04月23日
    浏览(67)
  • android 13.0 SystemUI状态栏下拉快捷添加截图快捷开关

    在13.0的系统产品rom定制化开发中,对SystemUI的定制需求也是挺多的,在下拉状态栏中 添加截图快捷开关,也是常有的开发功能,下面就以添加 截图功能为例功能的实现 在Systemui的下拉状态栏的相关源码中,在快捷设置区域QSPanel及点击事件流程 分析 SystemUI下拉之后的那些快捷

    2024年02月03日
    浏览(61)
  • Android 13(targetSdkVersion:33)必需添加com.google.android.gms.permission.AD_ID

    关于这个问题个人觉得Google真有点变态。 大概的意思是:你要适配Android 13,必须将targetSdkVersion升至33,这都很正常;你必须添加com.google.android.gms.permission.AD_ID的权限获取,OK,虽然不知道我的APP没有广告为何一定要我加这个,你要加就加呗!!!但变态的是在提交新版本审核

    2024年02月11日
    浏览(48)
  • 【Android13开发WIFI添加频段设置项(2.4G/5G/automatic)更改笔记】

    提示:这里简述项目相关背景: Android13中客户需求添加WIFI频段选项: 2.4G only,只显示链接2.4G的AP。 5G only,只能显示和链接5G的AP。 Automatic:自动显示,即全部正常显示。 提示:这里描述项目中遇到的问题: 作为初学者的我,刚开始接到这个bug,还是比较懵的,多亏了CSDN的前辈

    2024年02月05日
    浏览(42)
  • 【论文阅读】Vision Mamba:双向状态空间模型的的高效视觉表示学习

    论文地址: Vision Mamba 在这篇文章,我们展示对于视觉表示学习,不再依赖自注意力,提出一个新的泛用的视觉backbone基于双向Mamba块,通过位置嵌入来标示图片序列,通过双向状态空间模型压缩视觉表示。结果表示,Vim有能力解决在高分辨率图片上应用tranformer类型所导致计算

    2024年03月21日
    浏览(48)
  • 【Android Framework系列】第13章 SVG矢量图形自定义组件(绘制中国地图)

    本章节我们来了解下什么是 SVG 矢量图形,怎么通过 SVG 实现图形的绘制,通过 SVG 实现不规则的自定义控件,项目实现一个中国地图,实现每个省都能够点击,项目地址在文末请自取。 SVG 指可伸缩矢量图形 (Scalable Vector Graphics) SVG 用来定义用于网络的基于矢量的图形 SVG 使用

    2024年02月10日
    浏览(61)
  • Android10.0 hal层添加自定义hal模块功能实现

    在10.0的系统rom定制化开发中,在 对hal模块进行开发时,需要通过添加自定义的hal模块来实现某些 功能时,就需要添加hal模块的相关功能,接下来就来实现一个案例来供参考 HAL是硬件抽象层,它向下屏蔽了硬件的实现细节,向上提供了抽象接口, HAL是底层硬件和上层框架直

    2024年02月15日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包