PhoneFactory / Phone 启动流程架构设计


description: “PhoneFactory / Phone 启动流程深度架构设计——Telephony 框架的「大爆炸」初始化, 涵盖 makeDefaultPhone 的单实例保护、CommandsInterface/RIL 创建、UiccController 初始化、SubscriptionManagerService 启动、Phone 创建工厂、TelephonyComponentFactory 依赖注入。”

PhoneFactory 是 Telephony 框架的启动入口,它负责在 phone 进程启动时初始化整个 Telephony 世界。从 RIL 连接的建立、UiccController 的创建、Phone 实例的构造到 SubscriptionManagerService 的启动,所有核心组件都在 makeDefaultPhone() 中按严格的依赖顺序创建。

这个过程如同"创世"——它必须恰好执行一次(double-checked lock),必须按正确的依赖顺序创建所有单例,必须在失败时提供合理的错误处理。

设计理念: PhoneFactory 的设计核心是 严格顺序的初始化依赖链。每个组件的创建都依赖于前面的组件——RIL → UiccController → SubscriptionService → Phone。如果某个环节失败,整个 Telephony 栈将不可用,因此 Google 在这里把错误处理做到了极致。

1. 核心职责

  1. 单例保护:使用 LocalServerSocket 确保 phone 进程只有一个实例
  2. 组件创建:按依赖顺序创建 RIL、UiccController、SubscriptionService、Phone 等核心组件
  3. 资源配置:读取系统属性确定网络模式、Modem 数量等
  4. eSIM 初始化:如果设备支持 eSIM,初始化 EuiccController/EuiccCardController
  5. Phone 映射:维护 phoneId → Phone 的数组索引映射

2. 核心类与源码锚点

  • [PhoneFactory.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneFactory.java):启动入口。包含 makeDefaultPhone() 方法和所有静态组件引用。
  • [GsmCdmaPhone.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java):通用的 GSM/CDMA Phone 实现。创建后关联 ServiceStateTracker、DataNetworkController 等。
  • [TelephonyComponentFactory.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/TelephonyComponentFactory.java):依赖注入工厂。允许 OEM 通过 inject() 替换默认组件实现。
  • [ProxyController.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/ProxyController.java):多 SIM 代理控制器。
  • [PhoneSwitcher.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/data/PhoneSwitcher.java):数据连接的 Phone 切换器。

3. Google 设计技巧深入分析

3.1 LocalServerSocket 单实例保护

for(;;) {
   
   
    boolean hasException = false;
    retryCount++;
    try {
   
   
        new LocalServerSocket("com.android.internal.telephony");
    } catch (java.io.IOException ex) {
   
   
        hasException = true;
    }
    if (!hasException) {
   
   
        break;
    } else if (retryCount > SOCKET_OPEN_MAX_RETRY) {
   
   
        throw new RuntimeException("PhoneFactory probably already running");
    } else {
   
   
        try {
   
   
            Thread.sleep(SOCKET_OPEN_RETRY_MILLIS);  // 2 秒
        } catch (InterruptedException er) {
   
    }
    }
}

Google设计技巧 #1 — UNIX Domain Socket 的单实例保护
使用 LocalServerSocket("com.android.internal.telephony") 而非传统的文件锁或 PID 文件。这是因为 Android 中 phone 进程可能被 kill 后重启——LocalServerSocket 是内核管理的资源,当进程死亡时内核自动释放 socket,下一个 phone 实例可以成功创建。而如果 phone 进程已运行,创建同名 socket 会抛出 IOException,从而检测到重复启动。

同时使用 3 次重试 + 2 秒间隔的策略,处理进程重启时 socket 尚未完全释放的竞态窗口。

3.2 严格顺序的初始化依赖链

public static void makeDefaultPhone(Context context, @NonNull FeatureFlags featureFlags) {
   
   
    synchronized (sLockProxyPhones) {
   
   
        if (!sMadeDefaults) {
   
   
            sContext = context;
            
            // 1. 创建 TelephonyDevController
            TelephonyDevController.create();
            
            // 2. 获取 Modem 数量
            int numPhones = TelephonyManager.getDefault().getActiveModemCount();
            
            // 3. 创建 CommandsInterface (RIL) 数组
            sCommandsInterfaces = new RIL[numPhones];
            for (int i = 0; i < numPhones; i++) {
   
   
                sCommandsInterfaces[i] = new RIL(context, ...);
            }
            
            // 4. 创建 RadioConfig
            final RadioConfig radioConfig = RadioConfig.make(context, ...);
            
            // 5. 创建 UiccController (单例)
            sUiccController = UiccController.make(context<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值