android9:Radio模块-服务启动

一、目的
都是因为工作的原因,现在正在调试本地收音机功能。开发环境背景,android9 基于imx8 ndk 开发本地收音机功能。我是做app功能,所以调试过程从应用层开始往下调试,在android linux 开发同事说 收音机芯片已经调试OK之后,我使用android 自带的收音机功能进行测试,打开收音机闪退同时报错,收音机服务失败。具体log如下:

06-15 06:54:09.607 I/ActivityManager( 2390): START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.android.car.radio cmp=com.android.car.radio/.CarRadioActivity} from uid 10014
06-15 06:54:09.663 I/ActivityManager( 2390): Start proc 4059:com.android.car.radio/u0a42 for activity com.android.car.radio/.CarRadioActivity
06-15 06:54:09.663 I/Zygote  ( 4059): seccomp disabled by setenforce 0
06-15 06:54:09.684 I/CAR.AM  ( 2579): onForegroundActivitiesChanged uid 10042 pid 4059 fg true
06-15 06:54:09.689 I/CAR.AM  ( 2579): New top task: TaskInfoContainer [topActivity=ComponentInfo{com.android.car.radio/com.android.car.radio.CarRadioActivity}, taskId=6, stackId=2, userId=0
06-15 06:54:09.690 I/CAR.AM  ( 2579): activity launched:TaskInfoContainer [topActivity=ComponentInfo{com.android.car.radio/com.android.car.radio.CarRadioActivity}, taskId=6, stackId=2, userId=0
06-15 06:54:09.698 D/PermissionCache( 1572): checking android.permission.READ_FRAME_BUFFER for uid=1000 => granted (764 us)
06-15 06:54:09.702 I/droid.car.radi( 4059): The ClassLoaderContext is a special shared library.
06-15 06:54:09.727 I/display ( 2579): open gpu gralloc module!
06-15 06:54:09.745 I/CAR.AM  ( 2579): onTaskStackChanged
06-15 06:54:10.016 D/OpenGLRenderer( 4059): Skia GL Pipeline
06-15 06:54:10.064 W/SystemServiceRegistry( 4059): No service published for: broadcastradio
06-15 06:54:10.064 D/AndroidRuntime( 4059): Shutting down VM
--------- beginning of crash
06-15 06:54:10.065 E/AndroidRuntime( 4059): FATAL EXCEPTION: main
06-15 06:54:10.065 E/AndroidRuntime( 4059): Process: com.android.car.radio, PID: 4059
06-15 06:54:10.065 E/AndroidRuntime( 4059): java.lang.RuntimeException: Unable to create service com.android.car.radio.RadioService: java.lang.NullPointerException: RadioManager could not be loaded
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at android.app.ActivityThread.handleCreateService(ActivityThread.java:3582)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at android.app.ActivityThread.access$1300(ActivityThread.java:200)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1672)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at android.os.Handler.dispatchMessage(Handler.java:106)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at android.os.Looper.loop(Looper.java:193)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at android.app.ActivityThread.main(ActivityThread.java:6718)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at java.lang.reflect.Method.invoke(Native Method)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:917)
06-15 06:54:10.065 E/AndroidRuntime( 4059): Caused by: java.lang.NullPointerException: RadioManager could not be loaded
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at java.util.Objects.requireNonNull(Objects.java:228)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at com.android.car.radio.platform.RadioManagerExt.<init>(RadioManagerExt.java:62)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at com.android.car.radio.RadioService.onCreate(RadioService.java:127)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     at android.app.ActivityThread.handleCreateService(ActivityThread.java:3570)
06-15 06:54:10.065 E/AndroidRuntime( 4059):     ... 8 more
06-15 06:54:10.067 W/ActivityManager( 2390):   Force finishing activity com.android.car.radio/.CarRadioActivity
06-15 06:54:10.073 I/Process ( 4059): Sending signal. PID: 4059 SIG: 9
06-15 06:54:10.127 W/InputDispatcher( 2390): channel 'bcf9e50 com.android.car.radio/com.android.car.radio.CarRadioActivity (server)' ~ Consumer closed input channel or an error occurred.  events=0x9
06-15 06:54:10.127 E/InputDispatcher( 2390): channel 'bcf9e50 com.android.car.radio/com.android.car.radio.CarRadioActivity (server)' ~ Channel is unrecoverably broken and will be disposed!
06-15 06:54:10.128 I/ActivityManager( 2390): Process com.android.car.radio (pid 4059) has died: fore TOP 
06-15 06:54:10.128 I/WindowManager( 2390): WIN DEATH: Window{bcf9e50 u0 com.android.car.radio/com.android.car.radio.CarRadioActivity}
06-15 06:54:10.128 W/InputDispatcher( 2390): Attempted to unregister already unregistered input channel 'bcf9e50 com.android.car.radio/com.android.car.radio.CarRadioActivity (server)'
06-15 06:54:10.128 W/libprocessgroup( 2390): kill(-4059, 9) failed: No such process
06-15 06:54:10.129 I/Zygote  ( 2296): Process 4059 exited due to signal (9)
06-15 06:54:10.155 W/ActivityManager( 2390): setHasOverlayUi called on unknown pid: 4059
06-15 06:54:10.168 I/CAR.AM  ( 2579): onTaskStackChanged
06-15 06:54:10.171 I/CAR.AM  ( 2579): New top task: TaskInfoContainer [topActivity=ComponentInfo{com.android.car.carlauncher/com.android.car.carlauncher.AppGridActivity}, taskId=5, stackId=1, userId=0

然后咨询imx8 ndk 技术支持,在他的指导下,收音机服务打开成功。但是原因不懂。下面先把解决方案粘出来。

在device/fsl/目录下根据补丁位置打上补丁:
device_fsl_imx8q_manifest_car.patch
diff --git a/imx8q/mek_8q/manifest_car.xml b/imx8q/mek_8q/manifest_car.xml
index eabe20f4..32f5d415 100644
--- a/imx8q/mek_8q/manifest_car.xml
+++ b/imx8q/mek_8q/manifest_car.xml
@@ -264,4 +264,13 @@
             <instance>default</instance>
         </interface>
     </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.broadcastradio</name>
+        <transport>hwbinder</transport>
+        <version>2.0</version>
+        <interface>
+            <name>IBroadcastRadio</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
 </manifest>

device_fsl_imx8q_mek_8q.patch
diff --git a/imx8q/mek_8q/mek_8q_car.mk b/imx8q/mek_8q/mek_8q_car.mk
index c8259cbf..9319cefd 100644
--- a/imx8q/mek_8q/mek_8q_car.mk
+++ b/imx8q/mek_8q/mek_8q_car.mk
@@ -34,7 +34,8 @@ PRODUCT_COPY_FILES += \
 PRODUCT_COPY_FILES += \
     frameworks/native/data/etc/android.hardware.screen.landscape.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.screen.landscape.xml \
     frameworks/native/data/etc/android.hardware.type.automotive.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.type.automotive.xml \
-    frameworks/native/data/etc/android.software.freeform_window_management.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.freeform_window_management.xml
+    frameworks/native/data/etc/android.software.freeform_window_management.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.freeform_window_management.xml \
+    frameworks/native/data/etc/android.hardware.broadcastradio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.broadcastradio.xml
 
 # Add Google prebuilt services
 PRODUCT_PACKAGES += \
@@ -54,7 +55,8 @@ PRODUCT_PACKAGES += \
 PRODUCT_PACKAGES += \
     libion \
     vehicle.default \
-    android.hardware.automotive.vehicle@2.0-service
+    android.hardware.automotive.vehicle@2.0-service \
+    android.hardware.broadcastradio@2.0-service
 
 # Add Trusty OS backed gatekeeper and secure storage proxy
 PRODUCT_PACKAGES += \

注:实际上就是编译android.hardware.broadcastradio@2.0-service、拷贝android.hardware.broadcastradio.xml文件以及将HAL加入清单manifest_car.xml,可根据自己习惯修改位置。
编译完成后会在out/target/product/mek_8q/vendor/bin/hw/目录下生成android.hardware.broadcastradio@2.0-service,在out/target/product/mek_8q/vendor/etc/permissions/目录下生成android.hardware.broadcastradio.xml,
在out/target/product/mek_8q/vendor/etc/init/目录下生成android.hardware.broadcastradio@2.0-service.rc

编译完成重新烧写系统后,因为SELinux的原因,在命令行输入
setenforce 0
使SELinux处于permissive状态,避开Selinux,用于调试,重启失效。


后面的SELinux的关闭方法可以查看我前面的文章:androidP:SElinux权限

为啥加上上边那些,收音机服务就可以启动了。后面还要研究一下。如果有大神可以指导,不胜感激。下面就应用能开始启动收音机服务的源码调用流程 做个记录,以方便后续学习。

二、收音机服务的启动流程
https://www.jianshu.com/p/4a20fcfcb1be (Radio Bringup(App-Framework-Hal))
根据该文档介绍,在android启动启动之后,init进程会启动Zygote进程,Zygote进程进一步启动SystemServer进程。(上述启动流程比较复杂,后面参考 Android 9§之init进程启动源码分析指南之一:https://blog.csdn.net/tkwxty/article/details/106020050 这个链接进一步学习)
从SystemServer进程启动开始分析。
参考链接:https://www.jianshu.com/p/4a20fcfcb1be (Radio Bringup(App-Framework-Hal))
源码路径:
1、SystemServer.java
/frameworks/base/services/java/com/android/server/SystemServer.java
2、ApplicationPackageManager.java
/frameworks/base/core/java/android/app/ApplicationPackageManager.java
3、PackageManagerService.java
/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
4、SystemConfig.java
/frameworks/base/core/java/com/android/server/SystemConfig.java
5、Environment.java
/frameworks/base/core/java/android/os/Environment.java
6、PackageManager.java
/frameworks/base/core/java/android/content/pm/PackageManager.java

在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/img_convert/0c872894f2f6ed57d60cfb31760ddebf.png

备注1:

  startOtherServices() {
     ...
     if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BROADCAST_RADIO)) {
                traceBeginAndSlog("StartBroadcastRadioService");
                mSystemServiceManager.startService(BroadcastRadioService.class);
                traceEnd();
            }
     ...
  }

没有添加第一部分的解决方案之前,该服务是不能启动的,也就是if条件判断不匹配。hasSystemFeature函数用来判断该设备(硬件)是否支持相应功能(收音机)。下面分析,函数后面的调用流程。其中:public static final String FEATURE_BROADCAST_RADIO = “android.hardware.broadcastradio”;

备注2:

@Override
    public boolean hasSystemFeature(String name, int version) {
        try {
            return mPM.hasSystemFeature(name, version);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

备注3:

 SystemConfig systemConfig = SystemConfig.getInstance();
 final ArrayMap<String, FeatureInfo> mAvailableFeatures;
 mAvailableFeatures = systemConfig.getAvailableFeatures();

@Override
    public boolean hasSystemFeature(String name, int version) {
        // allow instant applications
        synchronized (mAvailableFeatures) {
            final FeatureInfo feat = mAvailableFeatures.get(name);
            if (feat == null) {
                return false;
            } else {
                return feat.version >= version;
            }
        }
    }

备注4:
在备注3中,定义systemConfig变量获取SystemConfig实例,

public static SystemConfig getInstance() {
        synchronized (SystemConfig.class) {
            if (sInstance == null) {
                sInstance = new SystemConfig();
            }
            return sInstance;
        }
    }

//SystemConfig构造函数中实现方法
SystemConfig() {
    ...
   // Read configuration from the old permissions dir
        readPermissions(Environment.buildPath(
                Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
    ...
}

 void readPermissions(File libraryDir, int permissionFlag) {
        // Read permissions from given directory.
        if (!libraryDir.exists() || !libraryDir.isDirectory()) {
            if (permissionFlag == ALLOW_ALL) {
                Slog.w(TAG, "No directory " + libraryDir + ", skipping");
            }
            return;
        }
        if (!libraryDir.canRead()) {
            Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
            return;
        }

        // Iterate over the files in the directory and scan .xml files
        File platformFile = null;
        for (File f : libraryDir.listFiles()) {
            //这里打印日志,截图1
            // We'll read platform.xml last
            if (f.getPath().endsWith("etc/permissions/platform.xml")) {
                platformFile = f;
                continue;
            }

            if (!f.getPath().endsWith(".xml")) {
                Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
                continue;
            }
            if (!f.canRead()) {
                Slog.w(TAG, "Permissions library file " + f + " cannot be read");
                continue;
            }

            readPermissionsFromXml(f, permissionFlag);
        }

        // Read platform permissions last so it will take precedence
        if (platformFile != null) {
            readPermissionsFromXml(platformFile, permissionFlag);
        }
    }

    private void readPermissionsFromXml(File permFile, int permissionFlag) {
       ...
       else if ("feature".equals(name) && allowFeatures) {
                    String fname = parser.getAttributeValue(null, "name");
                    int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
                    boolean allowed;
                    if (!lowRam) {
                        allowed = true;
                    } else {
                        String notLowRam = parser.getAttributeValue(null, "notLowRam");
                        allowed = !"true".equals(notLowRam);
                    }
                    if (fname == null) {
                        Slog.w(TAG, "<feature> without name in " + fname + " at "
                                + parser.getPositionDescription());
                    } else if (allowed) {
                        //这里打印fname = android.hardware.broadcastradio,fversion = 0;
                        addFeature(fname, fversion);
                    }
                    XmlUtils.skipCurrentTag(parser);
                    continue;

                }
       ...
    }
  
  //终于到了addFeature方法,这里只有把name 进去,前面mAvailableFeatures.get(name)才能取到值
   private void addFeature(String name, int version) {
        FeatureInfo fi = mAvailableFeatures.get(name);
        if (fi == null) {
            fi = new FeatureInfo();
            fi.name = name;
            fi.version = version;
            mAvailableFeatures.put(name, fi);
        } else {
            fi.version = Math.max(fi.version, version);
        }
    }

截图1:
在这里插入图片描述

备注5:

 private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
 public static File getRootDirectory() {
        return DIR_ANDROID_ROOT;
    }

根据上述备注4和备注5流程,想要mAvailableFeatures.get(name)方法取到对应值,首先需要addFeature方法,将对应的name添加到FeatureInfo中。下面分析addFeature是在哪里获取的。直接将链接https://www.jianshu.com/p/4a20fcfcb1be的结论放到这里来。readPermissions(Environment.buildPath(
Environment.getRootDirectory(), “etc”, “permissions”), ALLOW_ALL);}
可以看出来,该权限是在system/etc/permissions下边的xml配置文件中进行的配置。(我使用的是androidP 恩智浦imx8平台 android.hardware.broadcastradio.xml在板子里的位置为 vendor/etc/permissions,从上述日志截图1可以看出,这里文件路径两种都有)。按照第一部分解决问题的方法,添加对应补丁可以生成 android.hardware.broadcastradio.xml文件并将该文件拷贝到vendor/etc/permissions路径下,android系统跑起来后实现收音机服务的启动。

另外我的android.hardware.broadcastradio.xml文件的内容是:

<permissions>
    <feature name="android.hardware.broadcastradio" />
</permissions>

三、收音机服务调用流程
从打开RadioActivity开始。
在这里插入图片描述
源码路径
1、CarRadioActivity.java
/packages/apps/Car/Radio/src/com/android/car/radio/CarRadioActivity.java
2、RadioController.java
/packages/apps/Car/Radio/src/com/android/car/radio/RadioController.java
3、IRadioManager.aidl
/packages/apps/Car/Radio/src/com/android/car/radio/service/IRadioManager.aidl
IRadioManager.aidl实现类RadioService.java
/packages/apps/Car/Radio/src/com/android/car/radio/RadioService.java
4、RadioManagerExt.java
/packages/apps/Car/Radio/src/com/android/car/radio/platform/RadioManagerExt.java
5、RadioManager.java
/frameworks/base/core/java/android/hardware/radio/RadioManager.java
6、IRadioService.aidl
/frameworks/base/core/java/android/hardware/radio/IRadioService.aidl
IRadioService.aidl实现类在 BroadcastRadioService.java中的内部类ServiceImpl
/frameworks/base/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java

备注1:mRadioManager = (RadioManager)ctx.getSystemService(Context.RADIO_SERVICE);
备注2:@NonNull private final IRadioService mService;
mService = IRadioService.Stub.asInterface(ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE));
备注3:内部类ServiceImpl中两个功能
1)判断收音机服务权限,

 private void enforcePolicyAccess() {
            if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission(
                    Manifest.permission.ACCESS_BROADCAST_RADIO)) {
                throw new SecurityException("ACCESS_BROADCAST_RADIO permission not granted");
            }
        }

2)实现三个重载方法:listModules、 openTuner 、addAnnouncementListener

以上为收音机服务调用的流程,后面慢慢深入到hal层,与linux层通信,应该收音机功能就可以了。不知道我想的是不是对的。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值