Framework层定义aidl 接口,实现自定义系统服务


前言-需求场景

这里直接将客户需求拿过来吧,其实这种需求很常见,就是自定义一个服务,这个服务承担一定的功能。

系统源码需新增com.android.device.IDeviceCtrlService接口和com.android.device.IDeviceCtrlService.Stub实现类,提供"DeviceCtrlService"服务。	
应用层通过IDeviceCtrlService mService = Stub.asInterface(ServiceManager.getService("DeviceCtrlService"))获取到设备设置的服务。

com.android.device.IDeviceCtrlService 接口内容如下:
在这里插入图片描述
在这里插入图片描述

一、涉及到的知识点

自己总结如下:

  • aidl 相关知识点
  • Framework层 aidl 相关的服务、接口定义等概念和具体实践
  • 在Framework层自定义服务并在SystemServer 中添加自定义的服务
  • SELinux 权限相关知识,让自己自定义的服务可以被系统服务添加且具备权限
  • 应用层如何使用自己的服务

二、修改文件-新增文件-修改总览

修改文件

        device/mediatek/sepolicy/basic/non_plat/device.te
        device/mediatek/sepolicy/basic/non_plat/system_server.te
        device/mediatek/sepolicy/basic/plat_private/service_contexts
        frameworks/base/core/res/AndroidManifest.xml
        frameworks/base/services/java/com/android/server/SystemServer.java
        frameworks/base/services/api/current.txt

新增文件

frameworks/base/services/core/java/com/android/device/DeviceCtrlService.java
frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.aidl
frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.java

修改总览

 create mode 100755 alps-release-s0.mp1.rc/device/mediatek/sepolicy/basic/non_plat/device.te
 create mode 100755 alps-release-s0.mp1.rc/device/mediatek/sepolicy/basic/non_plat/system_server.te
 create mode 100755 alps-release-s0.mp1.rc/device/mediatek/sepolicy/basic/plat_private/service_contexts
 create mode 100755 alps-release-s0.mp1.rc/frameworks/base/core/res/AndroidManifest.xml
 create mode 100755 alps-release-s0.mp1.rc/frameworks/base/services/api/current.txt
 create mode 100755 alps-release-s0.mp1.rc/frameworks/base/services/core/java/com/android/device/DeviceCtrlService.java
 create mode 100755 alps-release-s0.mp1.rc/frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.aidl
 create mode 100755 alps-release-s0.mp1.rc/frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.java
 create mode 100755 alps-release-s0.mp1.rc/frameworks/base/services/java/com/android/server/SystemServer.java

在这里插入图片描述

三、修改方案实现

framework 功能接口实现

定义接口

路径: frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.aidl , 就是一个aidl 文件,用来定义接口用的

package com.android.device;
interface IDeviceCtrlService{
    String getAudioOutList();
    void setCurrentAudioOut(String key);
    String getCurrentAudioOut();
    void setAudioOutAuto(boolean value);
    void setCecStatus(String value);
    String getCecStatus();
    void setLedStatus(String value);
    String getLedStatus();
    void setStandbyStatus(String value);
    String getStandbyStatus();
    void setIpRouteInfos(in  String[] ipv4_infos,in  String[] ipv6_infos);
    void setPackageRouteInfos(String packageInfoJson);
    void setConnectToken(String token);
    String getConnectToken();
    String getGlobalAppInstallPermissions();
}

定义接口实现类

路径:frameworks/base/services/core/java/com/android/device/DeviceCtrlService.java , 就是自己定义的服务,实现了上面定义的接口,就是一个服务类,在aidl 体系中兼任的就是一个server 端,处理具体业务工作的。

/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.device;

import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Slog;

import com.android.device.IDeviceCtrlService;
import com.android.server.SystemService;
import com.android.server.pm.PackageManagerService;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 /**
 * Device Control Service implementation
 * Provides device settings and control functionalities
 */
public class DeviceCtrlService extends SystemService {




    private static final String TAG = "DeviceCtrlService";
    private static final boolean DEBUG = true;

        Context mContext;

    // Device state storage
    private String mCurrentAudioOut = "hdmi";
    private boolean mAudioOutAuto = true;
    private String mCecStatus = "enabled";
    private String mLedStatus = "auto";
    private String mStandbyStatus = "immediate";
    private String mConnectToken = "";

    // Routing information
    private List<String> mIpv4Infos = new ArrayList<>();
    private List<String> mIpv6Infos = new ArrayList<>();
    private String mPackageRouteInfo = "{}";

    // Audio output options
    private static final Map<String, String> AUDIO_OUTPUTS = new HashMap<>();

    static {
        AUDIO_OUTPUTS.put("hdmi", "HDMI Audio");
        AUDIO_OUTPUTS.put("spdif", "S/PDIF Audio");
        AUDIO_OUTPUTS.put("analog", "Analog Audio");
        AUDIO_OUTPUTS.put("bluetooth", "Bluetooth Audio");
    }
    
    private final IDeviceCtrlService.Stub mServiceStub = new IDeviceCtrlService.Stub() {



       
        @Override
        public String getAudioOutList() {
            final long token = Binder.clearCallingIdentity();
            try {
                StringBuilder sb = new StringBuilder();
                sb.append("[");
                boolean first = true;
                for (Map.Entry<String, String> entry : AUDIO_OUTPUTS.entrySet()) {
                    if (!first) {
                        sb.append(",");
                    }
                    sb.append("{\"key\":\"").append(entry.getKey())
                            .append("\",\"name\":\"").append(entry.getValue()).append("\"}");
                    first = false;
                }
                sb.append("]");
                return sb.toString();
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setCurrentAudioOut(String key) {
            final long token = Binder.clearCallingIdentity();
            try {
                if (AUDIO_OUTPUTS.containsKey(key)) {
                    mCurrentAudioOut = key;
                    Slog.i(TAG, "Audio output set to: " + key);

                    // Here you would typically call native methods to actually switch audio
                    // native_setAudioOutput(key);
                } else {
                    Slog.w(TAG, "Invalid audio output key: " + key);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public String getCurrentAudioOut() {
            final long token = Binder.clearCallingIdentity();
            try {
                return mCurrentAudioOut;
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setAudioOutAuto(boolean value) {
            final long token = Binder.clearCallingIdentity();
            try {
                mAudioOutAuto = value;
                Slog.i(TAG, "Audio auto switch set to: " + value);

                // Apply auto-switch setting
                // native_setAudioAutoSwitch(value);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setCecStatus(String value) {
            final long token = Binder.clearCallingIdentity();
            try {
                if (value.equals("enabled") || value.equals("disabled")) {
                    mCecStatus = value;
                    Slog.i(TAG, "CEC status set to: " + value);

                    // Apply CEC setting
                    // native_setCecStatus(value);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public String getCecStatus() {
            final long token = Binder.clearCallingIdentity();
            try {
                return mCecStatus;
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setLedStatus(String value) {
            final long token = Binder.clearCallingIdentity();
            try {
                if (value.equals("on") || value.equals("off") || value.equals("auto")) {
                    mLedStatus = value;
                    Slog.i(TAG, "LED status set to: " + value);

                    // Apply LED setting
                    // native_setLedStatus(value);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public String getLedStatus() {
            final long token = Binder.clearCallingIdentity();
            try {
                return mLedStatus;
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setStandbyStatus(String value) {
            final long token = Binder.clearCallingIdentity();
            try {
                if (value.equals("immediate") || value.equals("delay") || value.equals("never")) {
                    mStandbyStatus = value;
                    Slog.i(TAG, "Standby status set to: " + value);

                    // Apply standby setting
                    // native_setStandbyStatus(value);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public String getStandbyStatus() {
            final long token = Binder.clearCallingIdentity();
            try {
                return mStandbyStatus;
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setIpRouteInfos(String[] ipv4_infos, String[] ipv6_infos) {
            final long token = Binder.clearCallingIdentity();
            try {
                if (ipv4_infos != null) {
                    mIpv4Infos = Arrays.asList(ipv4_infos);
                }
                if (ipv6_infos != null) {
                    mIpv6Infos = Arrays.asList(ipv6_infos);
                }

                Slog.i(TAG, "IP routes updated - IPv4: " + mIpv4Infos.size() +
                        ", IPv6: " + mIpv6Infos.size());

                // Apply IP routing configuration
                // native_setIpRoutes(mIpv4Infos, mIpv6Infos);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setPackageRouteInfos(String packageInfoJson) {
            final long token = Binder.clearCallingIdentity();
            try {
                mPackageRouteInfo = packageInfoJson;
                Slog.i(TAG, "Package route info updated: " + packageInfoJson);

                // Apply package routing configuration
                // native_setPackageRoutes(packageInfoJson);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void setConnectToken(String token) {
            final long token_ = Binder.clearCallingIdentity();
            try {
                mConnectToken = token;
                Slog.i(TAG, "Connect token updated");

                // Store token securely
                // saveConnectToken(token);
            } finally {
                Binder.restoreCallingIdentity(token_);
            }
        }

        @Override
        public String getConnectToken() {
            final long token = Binder.clearCallingIdentity();
            try {
                // For security, token might be retrieved from secure storage
                return mConnectToken;
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public String getGlobalAppInstallPermissions() {
            final long token = Binder.clearCallingIdentity();
            try {
                PackageManagerService pm = (PackageManagerService)
                        mContext.getSystemService("package");
                // mContext.getSystemService(Context.PACKAGE_SERVICE);

                // Build permissions info
                StringBuilder sb = new StringBuilder();
                sb.append("{");
                sb.append("\"install_non_market_apps\":").append(isNonMarketAppsAllowed());
                sb.append(",\"verify_apps\":").append(isVerifyAppsEnabled());
                sb.append("}");
                return sb.toString();
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        private boolean isNonMarketAppsAllowed() {
            return android.provider.Settings.Global.getInt(
                    mContext.getContentResolver(),
                    android.provider.Settings.Global.INSTALL_NON_MARKET_APPS, 0) != 0;
        }

        private boolean isVerifyAppsEnabled() {
            return android.provider.Settings.Global.getInt(
                    mContext.getContentResolver(),
                    "package_verifier_enable", 1) != 0;
            //  android.provider.Settings.Global.PACKAGE_VERIFIER_ENABLE, 1) != 0;
        }
 

    };

     public DeviceCtrlService(Context context) {
        super(context);
        Slog.i(TAG, "DeviceCtrlService started");

    }

     @Override
    public void onStart() {
        Slog.i(TAG, "DeviceCtrlService onStart");

        publishBinderService("DeviceCtrlService", mServiceStub);
         android.util.Slog.i("DeviceCtrlService", "✅ DeviceCtrlService regist MTK SystemService success");
    }

     @Override
    public void onBootPhase(int phase) {
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
            // Service is ready
            Slog.i(TAG, "DeviceCtrlService boot phase: system services ready");
        } else if (phase == PHASE_BOOT_COMPLETED) {
            // Boot completed
            Slog.i(TAG, "DeviceCtrlService boot completed");
        }
    }
}

生成aidl编译后的Java文件-桥梁

路径:frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.java ,就是一个中间类,通信桥梁,代理类。
这里就是接口和服务端之间的代理类,本身是动态生成的,但是为了源码好编译,这里是事先通过命令编译好然后放到这里,不然 源码编译的时候找不到 类。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.android.device;
public interface IDeviceCtrlService extends android.os.IInterface
{
  /** Default implementation for IDeviceCtrlService. */
  public static class Default implements com.android.device.IDeviceCtrlService
  {
  ...................
  public static final java.lang.String DESCRIPTOR = "com.android.device.IDeviceCtrlService";
  public java.lang.String getAudioOutList() throws android.os.RemoteException;
  public void setCurrentAudioOut(java.lang.String key) throws android.os.RemoteException;
  public java.lang.String getCurrentAudioOut() throws android.os.RemoteException;
  public void setAudioOutAuto(boolean value) throws android.os.RemoteException;
  public void setCecStatus(java.lang.String value) throws android.os.RemoteException;
  public java.lang.String getCecStatus() throws android.os.RemoteException;
  public void setLedStatus(java.lang.String value) throws android.os.RemoteException;
  public java.lang.String getLedStatus() throws android.os.RemoteException;
  public void setStandbyStatus(java.lang.String value) throws android.os.RemoteException;
  public java.lang.String getStandbyStatus() throws android.os.RemoteException;
  public void setIpRouteInfos(java.lang.String[] ipv4_infos, java.lang.String[] ipv6_infos) throws android.os.RemoteException;
  public void setPackageRouteInfos(java.lang.String packageInfoJson) throws android.os.RemoteException;
  public void setConnectToken(java.lang.String token) throws android.os.RemoteException;
  public java.lang.String getConnectToken() throws android.os.RemoteException;
  public java.lang.String getGlobalAppInstallPermissions() throws android.os.RemoteException;
}
.....................

将自定义服务添加到系统中释放接口供访问

基本常识是自定义服务需要扔给交给SystemServer 类,注册服务启动的。 这样应用端才能够使用。
路径: frameworks/base/services/java/com/android/server/SystemServer.java

// modify by fangchen start
import com.android.device.DeviceCtrlService; 
// modify by fangchen end 

在合适的位置,启动自定义服务:
			// modify by fangchen start 
          t.traceBegin("StartDeviceCtrlService");
          try {
             Slog.i(TAG, "Device Control Service");
				 mSystemServiceManager.startService(DeviceCtrlService.class);
                 } catch (Throwable e) {
			       Slog.i(TAG, "Device Control Service  error");
                 e.printStackTrace();
                 reportWtf("starting DeviceCtrlService", e);
             } 
           t.traceEnd();
			// modify by fangchen end  

修改 SELinux 规则,允许 system_server 添加自定义服务(核心)

新增服务名的 SELinux 类型(定义客体标签)

路径: device/mediatek/sepolicy/basic/non_plat/device.te

# modify by fangchen start  custom Service
#type DeviceCtrlService, service_manager_type;
type device_ctrl_service, service_manager_type; 
# modify by fangchen end   custom Service 

在这里插入图片描述

设置允许规则-允许SystemManager 具备add 权限

路径: device/mediatek/sepolicy/basic/non_plat/system_server.te

# modify by fangchen start  custom Service
#type DeviceCtrlService, service_manager_type;
allow system_server device_ctrl_service:service_manager add;
# modify by fangchen end   custom Service 

在这里插入图片描述

权限服务映射-Framework层添加的服务映射到SELinux 权限名映射

路径: device/mediatek/sepolicy/basic/plat_private/service_contexts

# modify by fangchen start custom service
DeviceCtrlService    u:object_r:device_ctrl_service:s0
# modify by fangchen end custom service

在这里插入图片描述

配置自己定义的服务

路径:frameworks/base/core/res/AndroidManifest.xml
将自己定义的服务,新增到配置文件中,如下:


	<!-- modify by fangchen start  -->	
	<service
    android:name="com.android.device.DeviceCtrlService"
    android:exported="true"/>
    <!-- modify by fangchen end   -->	
		

新增更新的api

路径: frameworks/base/services/api/current.txt
更新了服务相关接口,那么就要更新自己的api , 这里 是更新之后的,一般都放到分支代码中,这样就可以直接编译了。

四、延伸知识点-技能点【特别特别重要】

必备知识点

如上已经总结了需求涉及到的知识点;需求的视线步骤。最基本必须掌握的。这里再次罗列下:

aidl 相关

aidl 文件接口自己写 -> 自己写实现类 aidl 的实现类,也就是一个服务 -> aidl 一定要生成一个aidl 代理的服务通信中间件。 在系统里面不建议编译时候自动生成,而是先用命令自己生成一次 -> 集成到SystemServer 服务管理里面去-> 编译更新api 让aidl 被系统识别 更新系统api ->编译打包固件

系统SeLinux 权限相关

系统服务SystemServer 还有好多系统机制每隔Android 平台的机制一样规则不一样,说白了就是Linux 权限 系统权限一说,规定了 不是你随便改就像改想添加就添加到系统里面去的。这个时候就有SeLinux 权限一说了,就是要把自己定制的服务添加到底层系统里面去,底层系统才能够监听到。 自定义服务 添加SeLinux 权限 保证三点:

  • 定义自定义服务名的SELinux类型(推荐用小写,和服务名一致),案例:type device_ctrl_service, service_manager_type;
  • 允许system_server添加该服务(核心规则),案例:allow system_server device_ctrl_service:service_manager add;
  • 关联服务名到 SELinux 类型(关键),案例:DeviceCtrlService u:object_r:device_ctrl_service:s0
    当然,针对不同的Android 平台芯片平台、不同Android版本,配置的方式 文件路径不一致,适当修改即可。

必备技能点

增删改接口方法后如何生成aidl代理文件

package com.android.device;
import java.util.List;
interface IDeviceCtrlService{
    String getAudioOutList();
    void setCurrentAudioOut(String key);
    String getCurrentAudioOut();
    void setAudioOutAuto(boolean value);
    void setCecStatus(String value);
    String getCecStatus();
    void setLedStatus(String value);
    String getLedStatus();
    void setStandbyStatus(String value);
    String getStandbyStatus();
    void setIpRouteInfos(in  String[] ipv4_infos,in  String[] ipv6_infos);
    void setPackageRouteInfos(String packageInfoJson);
    void setConnectToken(String token);
    String getConnectToken();
    String getGlobalAppInstallPermissions();
	void setGlobalAppInstallPermissions(String status);
    List<String> getPreInstallPackages();
}

在这里插入图片描述

在源码根目录编写脚本,生成新的aidl 中间文件,如下:
脚本如下: aidl_create.sh

mkdir -p frameworks/base/services/core/java/com/android/device/gen/


out_sys/soong/host/linux-x86/bin/aidl  \
  -o frameworks/base/services/core/java/com/android/device/gen/ \
  -I frameworks/base/core/aidl/ \
  -I frameworks/base/services/core/java/ \
  --lang=java \
  frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.aidl
  
  
cp frameworks/base/services/core/java/com/android/device/gen/com/android/device/IDeviceCtrlService.java frameworks/base/services/core/java/com/android/device/

然后执行脚本生成中间文件:

./aidl_create.sh

看一下实际结果:
在这里插入图片描述
查看 IDeviceCtrlService.java 有了新的方法了
在这里插入图片描述

更新系统api

以下三个命令很重要, source 准备当前环境,lunch 当前自己的平台项目分支【一般自己的配置文件、编译文件有的】;最后更新api ,如果这个不知道命令,在编译阶段会报错提醒您执行哪个命令的 m services-non-updatable-stubs-update-current-api


source build/envsetup.sh 
lunch full_k69v1_64_k419-userdebug
m services-non-updatable-stubs-update-current-api

相关命令

aidl 命令

如上我们生成根据aidl 生成中间代理文件时候,用到了这个命令:out_sys/soong/host/linux-x86/bin/aidl ,所以在你编译源码之后会生成这个文件 这个环境。 然后再进行 生成工作

mkdir -p frameworks/base/services/core/java/com/android/device/gen/


out_sys/soong/host/linux-x86/bin/aidl  \
  -o frameworks/base/services/core/java/com/android/device/gen/ \
  -I frameworks/base/core/aidl/ \
  -I frameworks/base/services/core/java/ \
  --lang=java \
  frameworks/base/services/core/java/com/android/device/IDeviceCtrlService.aidl
  
  
cp frameworks/base/services/core/java/com/android/device/gen/com/android/device/IDeviceCtrlService.java frameworks/base/services/core/java/com/android/device/
 
adb 相关命令
service list | grep device_ctrl
# 输出:device_ctrl: [com.android.device.IDeviceCtrlService] → 成功


 列出所有已注册的系统服务(关键)
service list | grep -i device
# 或直接搜索你注册的服务名
service list | grep -i DeviceCtrlService
service list | grep -i device_ctrl



# 1. 查看系统中所有已注册的服务(关键:找你的服务名)
adb shell service list | grep -i device
# 正确输出示例:device_ctrl: [com.android.device.IDeviceCtrlService]
# 若无输出 → 服务未注册;若有输出但名称不是"DeviceCtrlService" → 服务名不匹配

# 2. 查看SystemServer日志,确认服务是否注册成功
adb logcat -s SystemServer | grep -i DeviceCtrlService
# 正确输出:I SystemServer:DeviceCtrlService 注册成功!
# 若输出错误栈 → 注册代码执行失败;若无输出 → 注册代码未执行

# 3. 检查应用是否有系统签名(MTK必查)
adb shell dumpsys package 你的应用包名 | grep -i signature
# 正确输出:signatures=PackageSignatures{xxx [AndroidPlatformSigner]}
# 若不是系统签名 → 权限校验失败,无法访问系统服务

五、应用层验证

部分代码调用如下:


       private IDeviceCtrlService mDeviceCtrlService;


   @Override
    protected void onCreate(Bundle savedInstanceState) {
        ..........
       // 1. 获取系统服务
        getDeviceCtrlService();

        // 2. 调用服务方法
        if (mDeviceCtrlService != null) {
            callServiceMethods();
        }
    .........................
   }


 

```java
 /**
     * 获取 DeviceCtrlService 服务实例
     */
    private void getDeviceCtrlService() {
        try {
            // 通过 ServiceManager 获取服务,参数为注册的服务名
            mDeviceCtrlService = IDeviceCtrlService.Stub.asInterface(ServiceManager.getService("DeviceCtrlService"));
            if (mDeviceCtrlService == null) {
                Log.e(TAG, "获取 DeviceCtrlService 失败:服务未注册或不存在");

                try {
                    Class<?> smClass = Class.forName("android.os.ServiceManager");
                    Method listServices = smClass.getMethod("listServices");
                    String[] services = (String[]) listServices.invoke(null);
                    Log.d(TAG, "系统所有服务:" + Arrays.toString(services));
                } catch (Exception e) {
                    e.printStackTrace();
                }

            } else {
                Log.d(TAG, "获取 DeviceCtrlService 成功");
            }
        } catch (Exception e) {
            Log.e(TAG, "获取 DeviceCtrlService 异常:", e);
        }
    }

    /**
     * 调用服务的具体方法
     */
    private void callServiceMethods() {
        try {
            // 示例 1:获取音频输出列表
            String audioOutList = mDeviceCtrlService.getAudioOutList();
            Log.d(TAG, "音频输出列表:" + audioOutList);

            // 示例 2:设置当前音频输出为 HDMI
            mDeviceCtrlService.setCurrentAudioOut("hdmi");
            // 获取设置后的音频输出
            String currentAudioOut = mDeviceCtrlService.getCurrentAudioOut();
            Log.d(TAG, "当前音频输出:" + currentAudioOut);

            // 示例 3:设置 CEC 状态
            mDeviceCtrlService.setCecStatus("enabled");
            String cecStatus = mDeviceCtrlService.getCecStatus();
            Log.d(TAG, "CEC 状态:" + cecStatus);

            // 示例 4:设置 IP 路由信息
            String[] ipv4Infos = new String[]{"192.168.1.1/24", "10.0.0.1/8"};
            String[] ipv6Infos = new String[]{"2001:0db8:85a3:0000:0000:8a2e:0370:7334/64"};
            mDeviceCtrlService.setIpRouteInfos(ipv4Infos, ipv6Infos);

            // 示例 5:设置连接 Token
            mDeviceCtrlService.setConnectToken("abc123456789");
            String token = mDeviceCtrlService.getConnectToken();
            Log.d(TAG, "连接 Token:" + token);

        } catch (RemoteException e) {
            // 跨进程调用必须捕获 RemoteException(服务崩溃、断开等场景会抛出)
            Log.e(TAG, "调用服务方法异常(RemoteException):", e);
        } catch (SecurityException e) {
            // 权限不足时抛出
            Log.e(TAG, "调用服务方法无权限:", e);
        } catch (Exception e) {

            Log.e(TAG, "调用服务方法未知异常:", e);
        }
    }
实验结果如下:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/1d0d33c91da34c82a2a69d99009fc086.png)


![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/ddec9fe7e4eb4a1e9cf16c73d4a49a03.png)


# 总结
 这里的需求其实就是新增一个服务供本地App调用,涉及到知识点两点:
 - aidl 一套环境配置、Framework层的接口实现
 - SeLinux 权限机制

这个过程中各个步骤异常痛苦,各种报错,耐心解决每一个问题,然后得到最优解决方案至实现需求即可。 
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐