Android语音通话状态监听

0 阅读4分钟

一、监听方案对比

Android 提供了 两套监听体系

方案API 层级权限精度推荐弃用
PhoneStateListenerandroid.telephonyREAD_PHONE_STATE✓ 已弃用
TelephonyCallbackandroid.telephonyREAD_PHONE_STATE现代方案
PreciseCallStateListener@SystemApiREAD_PRECISE_PHONE_STATE系统应用低层监听

二、通话状态层级

┌─────────────────────────────────────────────────┐
│ 通话状态的 3 个层级                              │
└─────────────────────────────────────────────────┘

1️⃣ 高层状态 (TelephonyCallback.CallStateListener)
   ├─ CALL_STATE_IDLE       (0)   无通话
   ├─ CALL_STATE_RINGING    (1)   来电响铃
   └─ CALL_STATE_OFFHOOK    (2)   活跃通话或通话中

2️⃣ 精确状态 (PreciseCallState - 系统级)
   ├─ PRECISE_CALL_STATE_IDLE           (0)   空闲
   ├─ PRECISE_CALL_STATE_ACTIVE         (1)   活跃通话
   ├─ PRECISE_CALL_STATE_HOLDING        (2)   保持通话
   ├─ PRECISE_CALL_STATE_DIALING        (3)   正在拨号
   ├─ PRECISE_CALL_STATE_ALERTING       (4)   远方响铃
   ├─ PRECISE_CALL_STATE_INCOMING       (5)   来电
   ├─ PRECISE_CALL_STATE_WAITING        (6)   来电等待
   ├─ PRECISE_CALL_STATE_DISCONNECTED   (7)   已断开
   └─ PRECISE_CALL_STATE_DISCONNECTING  (8)   正在断开

3️⃣ Call 内部状态 (frameworks_opt_telephony)
   ├─ Call.State.IDLE         (无连接)
   ├─ Call.State.ACTIVE       (活跃)
   ├─ Call.State.HOLDING      (保持)
   ├─ Call.State.DIALING      (拨号中 - MO)
   ├─ Call.State.ALERTING     (远方响铃 - MO)
   ├─ Call.State.INCOMING     (来电 - MT)
   ├─ Call.State.WAITING      (来电等待 - MT)
   ├─ Call.State.DISCONNECTED (已断开)
   └─ Call.State.DISCONNECTING (断开中)

三、现代方案:TelephonyCallback

3.1 基础用法

// 创建回调类
class MyTelephonyCallback extends TelephonyCallback implements
        TelephonyCallback.CallStateListener {
    
    @Override
    public void onCallStateChanged(@Annotation.CallState int state) {
        switch(state) {
            case TelephonyManager.CALL_STATE_IDLE:
                // 无通话
                break;
            case TelephonyManager.CALL_STATE_RINGING:
                // 来电或来电等待
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                // 活跃通话或正在拨号
                break;
        }
    }
}

// 注册监听
TelephonyManager tm = context.getSystemService(TelephonyManager.class);
MyTelephonyCallback callback = new MyTelephonyCallback();

tm.registerTelephonyCallback(
    executor,           // Executor (如 ConcurrentUtils.DIRECT_EXECUTOR)
    callback            // TelephonyCallback 实例
);

// 取消监听
tm.unregisterTelephonyCallback(callback);

3.2 权限配置

<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

权限获取 (Android 31+):

if (ContextCompat.checkSelfPermission(context, 
        Manifest.permission.READ_PHONE_STATE)
        != PackageManager.PERMISSION_GRANTED) {
    // 请求权限
    ActivityCompat.requestPermissions(activity,
            new String[]{Manifest.permission.READ_PHONE_STATE}, 
            REQUEST_CODE);
}

3.3 高级:PreciseCallStateListener (系统应用)

// 只有系统应用或运营商应用能使用
class MyPreciseCallback extends TelephonyCallback implements
        TelephonyCallback.PreciseCallStateListener {
    
    @Override
    public void onPreciseCallStateChanged(PreciseCallState callState) {
        // 获取三类电话的精确状态
        int ringingState = callState.getRingingCallState();
        int fgState = callState.getForegroundCallState();
        int bgState = callState.getBackgroundCallState();
        
        // 状态处理
        handleCallState(ringingState, fgState, bgState);
    }
}

// 注册
tm.registerTelephonyCallback(executor, preciseCallback);

权限 (系统级):

<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />

3.4 其他有用的回调

class FullTelephonyCallback extends TelephonyCallback implements
        TelephonyCallback.CallStateListener,
        TelephonyCallback.CallAttributesListener,
        TelephonyCallback.CallDisconnectCauseListener {
    
    // 基础通话状态
    @Override
    public void onCallStateChanged(int state) {
        Log.d(TAG, "Call state: " + state);
    }
    
    // 通话属性 (质量、网络类型等)
    @Override
    public void onCallAttributesChanged(CallAttributes attrs) {
        PreciseCallState callState = attrs.getPreciseCallState();
        int networkType = attrs.getNetworkType();
        CallQuality quality = attrs.getCallQuality();
    }
    
    // 断开原因
    @Override
    public void onCallDisconnectCauseChanged(int cause, int preciseCause) {
        // DisconnectCause 类中定义
        Log.d(TAG, "Disconnect: " + cause);
    }
}

四、旧方案:PhoneStateListener(已弃用)

// ⚠️ 不推荐使用,仅作参考

class MyPhoneStateListener extends PhoneStateListener {
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        // state: CALL_STATE_IDLE / RINGING / OFFHOOK
        // incomingNumber: 来电号码(可能为空)
    }
}

// 注册
TelephonyManager tm = context.getSystemService(TelephonyManager.class);
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);

// 取消
tm.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);

五、通话状态转移图

5.1 拨号流程 (Outgoing Call)

用户拨号
    │
    ├─→ CALL_STATE_OFFHOOK (开始拨号)
    │
    └─→ PreciseCallState:
        ├─ DIALING        (正在拨号)
        ├─ ALERTING       (远方响铃)
        └─ ACTIVE         (通话已接通)
        
        │
        └─→ 挂断
            ├─ DISCONNECTING
            └─ DISCONNECTED
            
        │
        └─→ CALL_STATE_IDLE (通话结束)

详细事件序列:

拨号
  │
  ├─ Event: onCallStateChanged(CALL_STATE_OFFHOOK)
  │
  ├─ Event: onPreciseCallStateChanged(
  │            ringing=IDLE,
  │            fg=DIALING,
  │            bg=IDLE)
  │
  ├─ RIL 上报远方响铃
  │  └─ Event: onPreciseCallStateChanged(
  │             fg=ALERTING, ...)
  │
  ├─ 远方接听
  │  └─ Event: onPreciseCallStateChanged(
  │             fg=ACTIVE, ...)
  │
  └─ 通话中 (保持 OFFHOOK 状态)

5.2 来电流程 (Incoming Call)

RIL 上报新来电
    │
    ├─→ CALL_STATE_RINGING (来电铃声)
    │
    ├─→ PreciseCallState:
    │   ├─ INCOMING  (MT 来电)
    │   └─ WAITING   (已有活跃呼叫,来电等待)
    │
    ├─ 用户接听
    │  ├─ CALL_STATE_OFFHOOK (通话中)
    │  └─ ACTIVE (已接通)
    │
    └─ 用户拒接
       └─ DISCONNECTED + CALL_STATE_IDLE

完整事件序列:

来电到达
  │
  ├─ Event: onCallStateChanged(CALL_STATE_RINGING)
  │
  ├─ Event: onPreciseCallStateChanged(
  │            ringing=INCOMING,
  │            fg=IDLE,
  │            bg=IDLE)
  │
  ├─ 用户接听
  │  ├─ Event: onCallStateChanged(CALL_STATE_OFFHOOK)
  │  └─ Event: onPreciseCallStateChanged(
  │             ringing=IDLE,
  │             fg=ACTIVE,
  │             bg=IDLE)
  │
  └─ 通话中

5.3 来电等待 (Call Waiting)

活跃通话中,收到新来电
    │
    ├─ 保持活跃通话
    │  └─ fg=ACTIVE
    │
    ├─ 新来电进入等待状态
    │  └─ ringing=WAITING
    │
    ├─ 用户切换到来电
    │  ├─ fg=HOLDING (活跃转为保持)
    │  └─ ringing=ACTIVE (等待转为活跃)
    │
    └─ 仍为 CALL_STATE_OFFHOOK

5.4 会议通话

多方通话中
    │
    ├─ fg=ACTIVE (前景通话)
    ├─ bg=ACTIVE (背景通话)
    │
    └─ 状态: CALL_STATE_OFFHOOK

六、核心数据结构

6.1 CallState (现代)

public final class CallState implements Parcelable {
    public int getCallState();              // PRECISE_CALL_STATE_*
    public String getCallClassification(); // FOREGROUND/BACKGROUND/RINGING
    public int getNetworkType();           // NETWORK_TYPE_*
    public CallQuality getCallQuality();   // 通话质量
}

6.2 PreciseCallState

public final class PreciseCallState implements Parcelable {
    public int getRingingCallState();      // 来电/来电等待
    public int getForegroundCallState();   // 活跃通话
    public int getBackgroundCallState();   // 保持通话
    
    // 断开原因
    public int getDisconnectCause();
    public int getPreciseDisconnectCause();
}

6.3 CallAttributes

public final class CallAttributes implements Parcelable {
    public PreciseCallState getPreciseCallState();
    public int getNetworkType();           // LTE, NR, WIFI 等
    public CallQuality getCallQuality();   // 延迟、丢包率等
}

七、实战示例

7.1 完整通话状态监听器

public class CallStateMonitor {
    private final Context mContext;
    private final TelephonyManager mTm;
    private MyCallback mCallback;
    
    public CallStateMonitor(Context context) {
        mContext = context;
        mTm = context.getSystemService(TelephonyManager.class);
    }
    
    public void startMonitoring(MyCallback callback) {
        mCallback = callback;
        
        // 检查权限
        if (ContextCompat.checkSelfPermission(mContext,
                Manifest.permission.READ_PHONE_STATE)
                == PackageManager.PERMISSION_GRANTED) {
            
            MyTelephonyCallback telCallback = new MyTelephonyCallback();
            mTm.registerTelephonyCallback(
                Executors.newSingleThreadExecutor(),
                telCallback
            );
        }
    }
    
    public void stopMonitoring() {
        if (mTelephonyCallback != null) {
            mTm.unregisterTelephonyCallback(mTelephonyCallback);
        }
    }
    
    private class MyTelephonyCallback extends TelephonyCallback implements
            TelephonyCallback.CallStateListener,
            TelephonyCallback.CallAttributesListener {
        
        @Override
        public void onCallStateChanged(int state) {
            String stateStr;
            switch (state) {
                case TelephonyManager.CALL_STATE_IDLE:
                    stateStr = "IDLE";
                    break;
                case TelephonyManager.CALL_STATE_RINGING:
                    stateStr = "RINGING";
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    stateStr = "OFFHOOK";
                    break;
                default:
                    stateStr = "UNKNOWN";
            }
            
            if (mCallback != null) {
                mCallback.onCallStateChanged(stateStr);
            }
        }
        
        @Override
        public void onCallAttributesChanged(CallAttributes attrs) {
            PreciseCallState ps = attrs.getPreciseCallState();
            int quality = attrs.getCallQuality().getAverageFlushedDelayMillis();
            
            if (mCallback != null) {
                mCallback.onCallAttributesChanged(ps, quality);
            }
        }
    }
    
    public interface MyCallback {
        void onCallStateChanged(String state);
        void onCallAttributesChanged(PreciseCallState state, int quality);
    }
}

7.2 来电检测

public class IncomingCallDetector {
    private final TelephonyManager mTm;
    
    public IncomingCallDetector(TelephonyManager tm) {
        mTm = tm;
    }
    
    public void monitorIncomingCalls(Executor executor) {
        mTm.registerTelephonyCallback(executor, new TelephonyCallback() {
            @Override
            public void onCallStateChanged(int state) {
                if (state == TelephonyManager.CALL_STATE_RINGING) {
                    // 来电到达
                    Log.d(TAG, "Incoming call detected!");
                    handleIncomingCall();
                }
            }
        });
    }
    
    private void handleIncomingCall() {
        // 可以执行:
        // - 记录通话
        // - 显示通知
        // - 获取来电信息
        // - 自动应答(需要高权限)
    }
}

八、常见问题

Q1: 如何获得来电号码?

// 旧 API (已弃用)
void onCallStateChanged(int state, String incomingNumber) {
    // incomingNumber 包含来电号码
}

// 新 API: 需要其他机制
// 通常通过 PHONE_STATE_CHANGED 广播 + 
// ContentResolver 查询通话记录

Q2: 通话中如何检测挂断?

// 监听通话状态变化
// OFFHOOK → IDLE (通话结束)
// 或 onCallDisconnectCauseChanged() 回调

Q3: 如何支持多SIM卡?

// 为每个订阅创建独立的 TelephonyManager
TelephonyManager tm = 
    context.getSystemService(TelephonyManager.class)
           .createForSubscriptionId(subscriptionId);
tm.registerTelephonyCallback(executor, callback);

九、性能优化

  1. 避免频繁注册/注销 - 应用启动时注册,销毁时取消
  2. 使用合适的 Executor - 不要在 UI 线程处理
  3. 权限检查 - 先检查权限再注册,避免崩溃
  4. 内存泄漏 - 确保在 Activity 销毁时取消监听

这就是 Android 通话状态监听的完整体系!