SystemServer vs ServiceManager

本文详细阐述了Android系统服务的组成与启动流程,包括system service的浏览、SystemServer核心进程、ServiceManager进程的启动与管理,以及系统服务的分类与启动方式。深入理解Android系统服务有助于开发者更好地集成和利用系统功能。

在Android系统中有很多内置的软件,例如,当手机接到来电时,会显示对方的电话号。也可以根据周围的环境将手机设置成震动或静音。如果想把这些功能加到自己的软件中应该怎么办呢?答案就是“系统服务”。在Android系统中提供了很多这种服务,通过这些服务,就可以像Android系统的内置软件一样随心所欲地控制Android系统了。系统服务(System Services)并非Android开发应用程序时,所涉及的Service(后台应用服务程序,位于Android系统应用层)的概念。而是Android操作系统Java应用程序下层的,伴随操作系统启动而运行的系统后台服务程序(位于Android系统的FrameWork层)。它是Android系统运行的基石,它配合binder(Android多进程通讯方法)、dalvik虚拟机和Android应用程序构成了一个多进程交互通讯,交互服务的Android系统。

Android体系架构中四种意义上服务:Native服务、Android服务、Init空间的服务、应用层空间的服务。这里的system service属于android服务这一块。

1.浏览一下Android的system service

在命令行启动shell

adb shell

执行下面指令

#service list

Found 47 services:
0    phone: [com.Android.internal.telephony.ITelephony]
1    iphonesubinfo: [com.Android.internal.telephony.IPhoneSubInfo]
2    simphonebook: [com.Android.internal.telephony.IIccPhoneBook]
3    isms: [com.Android.internal.telephony.ISms]
4    appwidget: [com.Android.internal.appwidget.IAppWidgetService]
42    SurfaceFlinger: [Android.ui.ISurfaceComposer]
43    media.audio_policy: [Android.media.IAudioPolicyService]
46    media.audio_flinger: [Android.media.IAudioFlinger]

从结果看来Android后台有很多的system service,他们是分散在不同进程中的线程实体。

2.SystemServer

SystemServer是Android系统的一个核心进程,它是由zygote进程创建的,因此在android的启动过程中位于zygote之后。android的所有服务循环都是建立在 SystemServer之上的。在SystemServer中,将可以看到它建立了android中的大部分服务,并通过ServerManager的add_service方法把这些服务加入到了ServiceManager的svclist中。从而完成ServcieManager对服务的管理。

3.Service Manager

Service manager是管理以上services的一个进程,可以在adb shell中运行ps看看进程列表就知道了。

源代码位于:

frameworks/base/cmds/servicemanager

执行方式:

他是用c和c++语言编写的natvie可以执行文件。在Android中称之为EXECUTABLE,这个名称很重要因为Android.mk文件中用这个名字来确定他是可以执行的二进制文件。

 4.Service Manager的启动过程和方法

开始有点复杂了,也该开始进入真正的Linux了。众所周知Linux的启动和文件系统的加载需要一个ramdisk,ramdisk负责让Linux kernel加载第一个进程init进程。那么这一切是怎么发生的呢?

Android的ramdisk中就有这样一个可执行文件init,我们可以去看一下

system/core/init/init.c

int main(int argc, char **argv)
{
    。。。。。。
    parse_config_file("/init.rc");

    。。。。。。

这个文件会编译出一个init的二进制可执行文件,并且去读init.rc文件。我们称init.rc文件为Android启动配置脚本。

 

现在我们打开init.rc文件,(如果您不知道init.rc,请参考google吧,了解下它的语法)

## Daemon processes to be run by init.
##
service servicemanager /system/bin/servicemanager
    user system
    critical
    onrestart restart zygote 
    onrestart restart media

从这里可以看出servicemanager 是init通过init.rc加载的第一个进程,接下来启动了zygote和media。

 

继续阅读init.rc

servicemanager进程运行起来以后,我们就可以应用binder来应用servicemanager提供的服务函数去创建

system-server和mediaserver了,下面是init.rc中的代码

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

#system-server的创建是通过app_process这个二进制程序去加载的
    socket zygote stream 666
    onrestart write /sys/Android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart media

service media /system/bin/mediaserver   #mediaserver的启动代码比较简单,看看就知道了不用参数就创建了
    user media
    group system audio camera graphics inet net_bt net_bt_admin

5.回过头再看系统的进程列表

# ps
USER     PID   PPID  VSIZE  RSS     WCHAN    PC         NAME
root      1     0     296    204   c009a694 0000c93c S /init 
root      2     0     0      0     c004dea0 00000000 S kthreadd
root      25    1     728    316   c003d444 afe0d6ac S /system/bin/sh
system    26    1     796    256   c019a810 afe0ca7c S /system/bin/servicemanager 
root      30    1     82860  26580 c009a694 afe0cba4 S zygote 
media     31    1     20944  3184  ffffffff afe0ca7c S /system/bin/mediaserver 
root      32    1     784    280   c0209468 afe0c7dc S /system/bin/installd
keystore  33    1     1616   396   c01a65a4 afe0d40c S /system/bin/keystore
root      34    1     728    272   c003d444 afe0d6ac S /system/bin/sh
root      35    1     824    332   c00b7dd0 afe0d7fc S /system/bin/qemud
root      37    1     1308   152   ffffffff 0000eca4 S /sbin/adbd
root      44    34    780    304   c0209468 afe0c7dc S /system/bin/qemu-props
system    52    30    158356 37804 ffffffff afe0ca7c S system_server 
app_1     92    30    108640 20580 ffffffff afe0da04 S com.Android.inputmethod.pinyin
radio     93    30    122852 23340 ffffffff afe0da04 S com.Android.phone
app_1     98    30    143244 34888 ffffffff afe0da04 S Android.process.acore

我们注意观察进程列表的PID和PPID,我们要通过实际的列表去理清他们的亲缘关系。

servicemanager是init的子进程

mediaserver是init的子进程

zygote是init的子进程,管理所有虚拟机实例

system_server和所有的java应用程序是zygote的子进程。system_server负责管理系统服务。



SystemServer进程

SystemServer进程在Android的运行环境中扮演了"神经中枢"的作用,APK应用中能够直接交互的大部分系统服务都在该进程中运 行,常见的比如WindowManagerServer(Wms)、ActivityManagerSystemService(AmS)、 PackageManagerServer(PmS)等,这些系统服务都是以一个线程的方式存在于SystemServer进程中。下面就来介绍到底都有 哪些服务线程,及其启动的顺序。

SystemServer的main()函数首先调用的是init1()函数,这是一个native函数,内部会进行一些与Dalvik虚拟机相关 的初始化工作。该函数执行完毕后,其内部会调用Java端的init2()函数,这就是为什么Java源码中没有引用init2()的地方,主要的系统服 务都是在init2()函数中完成的。

该函数首先创建了一个ServerThread对象,该对象是一个线程,然后直接运行该线程,如以下代码所示:

  

于是,从ServerThread的run()方法内部开始真正启动各种服务线程。

基本上每个服务都有对应的Java类,从编码规范的角度来看,启动这些服务的模式可归类为三种,如图9-3所示。

  
图9-3  不同服务的启动方式

模式一是指直接使用构造函数构造一个服务,由于大多数服务都对应一个线程,因此,在构造函数内部就会创建一个线程并自动运行。

模式二是指服务类会提供一个getInstance()方法,通过该方法获取该服务对象,这样的好处是保证系统中仅包含一个该服务对象。

模式三是指从服务类的main()函数中开始执行。

无论以上何种模式,当创建了服务对象后,有时可能还需要调用该服务类的init()或者systemReady()函数以完成该对象的启动,当然这 些都是服务类内部自定义的。为了区分以上启动的不同,以下采用一种新的方式描述该启动过程。比如当一个服务对象是通过模式一创建,并调用init()完成 该服务的启动,我们就用模式1.2表示;如果构造函数返回后就已经启动,而无须任何其他调用,即什么都不做(nothing),我们就用模式1.1表示。

表9-2列出了SystemServer中所启动的所有服务,以及这些服务的启动模式。

表9-2  SystemServer中启动服务列表

服务类名称

作用描述

启动模式

EntropyService

提供伪随机数

1.0

PowerManagerService

电源管理服务

1.2/3

ActivityManagerService

最核心的服务之一,管理 Activity

自定义

TelephonyRegistry

通过该服务注册电话模块的事件响应,比如重启、关闭、启动等

1.0

PackageManagerService

程序包管理服务

3.3

AccountManagerService

账户管理服务,是指联系人账户,而不是 Linux 系统的账户

1.0

ContentService

ContentProvider 服务,提供跨进程数据交换

3.0

BatteryService

电池管理服务

1.0

LightsService

自然光强度感应传感器服务

1.0

VibratorService

震动器服务

1.0

AlarmManagerService

定时器管理服务,提供定时提醒服务

1.0

WindowManagerService

Framework 最核心的服务之一,负责窗口管理

3.3

BluetoothService

蓝牙服务

1.0 +

DevicePolicyManagerService

提供一些系统级别的设置及属性

1.3

StatusBarManagerService

状态栏管理服务

1.3

ClipboardService

系统剪切板服务

1.0

InputMethodManagerService

输入法管理服务

1.0

NetStatService

网络状态服务

1.0

NetworkManagementService

网络管理服务

NMS.create()

ConnectivityService

网络连接管理服务

2.3

ThrottleService

暂不清楚其作用

1.3

(续表)

服务类名称

作用描述

启动模式

AccessibilityManagerService

辅助管理程序截获所有的用户输入,并根据这

些输入给用户一些额外的反馈,起到辅助的效果

1.0

MountService

挂载服务,可通过该服务调用 Linux 层面的 mount 程序

1.0

NotificationManagerService

通知栏管理服务, Android 中的通知栏和状

态栏在一起,只是界面上前者在左边,后者在右边

1.3

DeviceStorageMonitorService

磁盘空间状态检测服务

1.0

LocationManagerService

地理位置服务

1.3

SearchManagerService

搜索管理服务

1.0

DropBoxManagerService

通过该服务访问 Linux 层面的 Dropbox 程序

1.0

WallpaperManagerService

墙纸管理服务,墙纸不等同于桌面背景,

在 View 系统内部,墙纸可以作为任何窗口的背景

1.3

AudioService

音频管理服务

1.0

BackupManagerService

系统备份服务

1.0

AppWidgetService

Widget 服务

1.3

RecognitionManagerService

身份识别服务

1.3

DiskStatsService

磁盘统计服务

1.0

AmS的启动模式如下:

调用main()函数,返回一个Context对象,而不是AmS服务本身。

调用AmS.setSystemProcess()。

调用AmS.installProviders()。

调用systemReady(),当AmS执行完systemReady()后,会相继启动相关联服务的systemReady()函数,完成整体初始化。

关于具体某个服务的内部启动过程,请参照源码,这些过程一般都比较简单。


<think>我们被要求在SystemServer中添加代码,但之前的问题要求是“除去systemserver”,但现在问题变了,要求改为在SystemServer中添加。所以我们需要调整方案。 在SystemServer中添加代码的考虑: 1. 启动时机:SystemServer是系统核心服务,启动非常早,但要注意电话服务(Telephony)的初始化时机。 2. 权限:SystemServer本身具有很高的权限,可以直接读取IMEI。 3. 位置:我们需要在电话服务初始化之后,但在发送开机广播之前执行。 具体步骤: - 在SystemServer的`startOtherServices`方法中,找到电话服务(如TelecomLoaderService, TelephonyRegistry等)初始化之后的位置。 - 由于电话服务的初始化可能依赖于其他服务,我们需要确保在TelephonyManager能够正常使用的时候读取IMEI。 代码实现: 1. 在`SystemServer.java`的`startOtherServices`方法中,添加一个异步任务来读取IMEI,因为读取IMEI可能需要等待电话服务完全就绪。 2. 使用重试机制,因为电话服务可能不会立即就绪。 注意:在SystemServer中,我们不应该在主线程(即SystemServer的启动线程)中执行可能耗时的操作(如等待电话服务),所以应该启动一个新线程。 代码示例(基于Android 10+,路径:frameworks/base/services/java/com/android/server/SystemServer.java): ```java private void startOtherServices(@NonNull SystemServiceManager ssm, @NonNull Context context) { // ... 原有代码 // 在电话服务初始化之后的位置,例如在启动TelecomLoaderService和TelephonyRegistry之后 try { // 启动电话相关服务 t.traceBegin("StartTelecomLoaderService"); telecomLoader = new TelecomLoaderService(context); ServiceManager.addService(Context.TELECOM_SERVICE, telecomLoader); t.traceEnd(); t.traceBegin("StartTelephonyRegistry"); telephonyRegistry = new TelephonyRegistry(context); ServiceManager.addService("telephony.registry", telephonyRegistry); t.traceEnd(); // ... 其他服务 // 在电话服务启动后,启动一个线程来读取IMEI new Thread(() -> { readAndStoreImei(context); }, "ImeiReaderThread").start(); } catch (Throwable e) { // 异常处理 } // ... 继续启动其他服务,直到最终发送BOOT_COMPLETED广播 } private void readAndStoreImei(Context context) { TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class); // 如果telephonyManager为null,则重试 int retryCount = 0; while (telephonyManager == null && retryCount < 10) { SystemClock.sleep(1000); telephonyManager = context.getSystemService(TelephonyManager.class); retryCount++; } if (telephonyManager == null) { Slog.e(TAG, "Failed to get TelephonyManager after 10 retries"); return; } int slotCount = telephonyManager.getActiveModemCount(); for (int slotIndex = 0; slotIndex < slotCount; slotIndex++) { String imei = null; retryCount = 0; while (imei == null && retryCount < 10) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { imei = telephonyManager.getImei(slotIndex); } else { // 对于低于O的设备,使用反射 imei = (String) TelephonyManager.class .getMethod("getDeviceId", int.class) .invoke(telephonyManager, slotIndex); } } catch (Exception e) { Slog.w(TAG, "Exception reading IMEI for slot " + slotIndex, e); } if (imei == null) { SystemClock.sleep(1000); retryCount++; } } if (imei != null) { // 存储到Settings.Global Settings.Global.putString(context.getContentResolver(), "persist.sys.imei_slot" + slotIndex, imei); Slog.i(TAG, "Stored IMEI for slot " + slotIndex); } else { Slog.e(TAG, "Failed to read IMEI for slot " + slotIndex); } } } ``` 注意事项: 1. 权限:SystemServer本身具有系统权限,不需要额外声明。 2. 版本兼容性:对于低于Android O(API 26)的版本,使用反射调用`getDeviceId(int)`。 3. 线程安全:在SystemServer中启动新线程是安全的,但要注意不要阻塞主线程。 4. 重试机制:由于电话服务启动可能需要时间,我们通过重试来等待。 替代存储位置:如果不想使用`Settings.Global`,也可以考虑存储到系统属性(`SystemProperties`)中,但注意系统属性有长度限制(最大92个字符),而IMEI通常为15位,所以可以。但系统属性重启后不会持久化(除非以`persist.`开头)。所以我们可以存储为`persist.sys.imei_slotX`,但是注意,`Settings.Global`是持久化的,而系统属性也可以持久化,但使用系统属性需要写入权限。 使用系统属性示例: ```java // 在存储IMEI的地方,替换Settings.Global为: SystemProperties.set("persist.sys.imei_slot" + slotIndex, imei); ``` 但是,使用系统属性需要注意: - 系统属性是全局可见的,任何有权限的进程都可以读取(`getprop`命令也可以读取)。 - 持久化属性会写入到`/data/property`目录下,所以设备恢复出厂设置会被清除。 根据需求选择存储方式。 如果选择使用系统属性,则代码修改为: ```java // 替换Settings.Global.putString(...)为: SystemProperties.set("persist.sys.imei_slot" + slotIndex, imei); ``` 但请注意,在SystemServer中,使用`SystemProperties.set`是安全的。 最后,我们还需要考虑双卡和多卡的情况,使用`getActiveModemCount`获取卡槽数。 总结:在SystemServer的`startOtherServices`方法中,在电话服务初始化后启动一个线程,该线程负责读取IMEI并存储到持久化位置(如Settings.Global或系统属性)。 相关问题:</think>在 Android 的 `SystemServer` 中添加代码读取 IMEI 并存储到数据库,需要选择正确的初始化和执行时机。以下是详细实现方案: ### 最佳位置:`SystemServer` 的 `startOtherServices()` 方法 #### 原因: 1. **启动顺序控制**:`startOtherServices()` 在核心服务(如 ActivityManager)启动后执行,但仍在 `BOOT_COMPLETED` 广播之前 2. **服务可用性**:此时 `TelephonyManager` 服务已初始化完成 3. **系统权限**:`SystemServer` 具有 `READ_PRIVILEGED_PHONE_STATE` 权限 --- ### 实现代码(基于 AOSP): ```java // 文件路径:frameworks/base/services/java/com/android/server/SystemServer.java public final class SystemServer { private void startOtherServices() { // ... 原有代码 // 在电话服务初始化后添加(通常在 TelephonyRegistry 后) t.traceBegin("StartImeiReader"); try { // 启动 IMEI 读取服务 mSystemServiceManager.startService(new ImeiReaderService(mSystemContext)); } finally { t.traceEnd(); } // ... 其他服务初始化 } } // 新建 IMEI 读取服务 class ImeiReaderService extends SystemService { private static final String TAG = "ImeiReaderService"; private final Context mContext; public ImeiReaderService(Context context) { super(context); mContext = context; } @Override public void onStart() { // 在后台线程执行 IMEI 读取 new Thread(() -> { readAndStoreImei(); }, "ImeiReaderThread").start(); } private void readAndStoreImei() { TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); if (tm == null) { Slog.e(TAG, "TelephonyManager not available"); return; } int slotCount = tm.getActiveModemCount(); for (int slotIndex = 0; slotIndex < slotCount; slotIndex++) { String imei = null; int retry = 0; // 重试机制(最多 10 次,每次间隔 1 秒) while (imei == null && retry < 10) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { imei = tm.getImei(slotIndex); } else { imei = (String) TelephonyManager.class .getMethod("getDeviceId", int.class) .invoke(tm, slotIndex); } } catch (Exception e) { Slog.w(TAG, "IMEI read failed for slot " + slotIndex, e); } if (imei == null) { SystemClock.sleep(1000); retry++; } } if (imei != null) { // 存储到数据库(示例使用 SettingsProvider) String key = "sys.imei_slot" + slotIndex; Settings.Global.putString(mContext.getContentResolver(), key, imei); Slog.i(TAG, "IMEI stored for slot " + slotIndex + ": " + imei); } else { Slog.e(TAG, "Failed to read IMEI for slot " + slotIndex); } } } } ``` --- ### 关键实现细节: 1. **服务启动时机**: ```java mSystemServiceManager.startService(new ImeiReaderService(mSystemContext)); ``` - 放置在 `TelephonyRegistry` 初始化之后 - 确保在 `ActivityManagerService.onBootPhase()` 的 `PHASE_THIRD_PARTY_APPS_CAN_START` 阶段之前 2. **异步处理**: ```java new Thread(() -> { readAndStoreImei(); }, "ImeiReaderThread").start(); ``` - 避免阻塞系统服务启动主线程 - 使用独立线程处理可能耗时的 IMEI 读取操作 3. **多 SIM 卡支持**: ```java int slotCount = tm.getActiveModemCount(); for (int slotIndex = 0; slotIndex < slotCount; slotIndex++) { // 处理每个卡槽 } ``` - 动态适配不同 SIM 卡槽数量的设备 4. **存储位置选择**: ```java Settings.Global.putString(mContext.getContentResolver(), key, imei); ``` - 使用 `SettingsProvider` 持久化存储 - 替代方案:系统属性(需添加 `persist.` 前缀) ```java SystemProperties.set("persist.sys.imei_slot" + slotIndex, imei); ``` --- ### 启动时序验证: ```mermaid sequenceDiagram participant Zygote participant SystemServer participant TelephonyService participant ImeiReader Zygote->>SystemServer: 启动 SystemServer->>TelephonyService: 初始化电话服务 SystemServer->>ImeiReader: 启动IMEI读取服务 Note right of ImeiReader: 在后台线程执行 ImeiReader->>TelephonyService: 获取IMEI TelephonyService-->>ImeiReader: 返回IMEI ImeiReader->>SettingsProvider: 存储数据 SystemServer->>Broadcast: 发送BOOT_COMPLETED ``` --- ### 注意事项: 1. **权限声明**: 在 `AndroidManifest.xml` 添加: ```xml <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/> ``` 2. **系统签名**: - 修改代码需使用平台签名编译(`platform` 密钥) 3. **厂商适配**: - 双卡设备:检查 `getImei(int slotIndex)` 在不同芯片平台的实现 - CDMA 设备:考虑 `getMeid()` 的调用 4. **安全合规**: - IMEI 属于 PII 数据,存储位置需符合 GDPR/CCPA 要求 - 恢复出厂设置时清除:重写 `SettingsProvider` 的 `resetToDefaults()` ---
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值