平台:RK3399 KERNEL版本:kernel4.4 Android版本:android8.1
前言
RK3399虽然在设计上有预留4G(5G等)模组,但由于自身产品定义,多提供的是优秀的多显方案产品定制,且自身可同时集成千兆百兆网口,模组本身一般不适用于通话,仅提供数据网络支持。 由于项目对通话有硬性要求,固写下该文章对大体通话涉及的修改内容进行记录,仅提供参考,如有错误,还望指出,感谢!
一、为何需要自制回铃音?
在Android平台,移植模组通信主要要完成两件事,其一调通RIL服务,在SDK工作正常情况下(硬件设计不应该存在问题,包括模组供电,SIM卡外围电路设计),识别到SIM卡插入,此时短信,上网基本没有问题。其二语音通话需要单独添加模组的声卡,大多采用PCM数据传输。MH5000-31采用的为NAU88C10,具体应用可参考华为5G模组5000-31语音通话功能调试记录(NAU88C10).md 在完成上述步骤后,通话双方都有声音了,但问题接踵而至。不同运营商情况下,模组这边偶尔听不到振铃的声音,给别人打电话没有嘟嘟声…这还得了,简直没有灵魂了有没有。于是便有了自制一个嘟嘟回铃音的想法。
二、实现步骤
1.寻找铃音并选择合适的播放手段
铃音网上就能找到资源,最好格式为ogg。添加位置如下: frameworks/base/data/sounds/ringtones,也不局限于这里,可将找到的ogg放在上一级也行,这里更符合目录结构。在frameworks/base/data/sounds内AllAudio.mk添加,命名可自行选择,没有局限性,这里可以预制文件到system/media/audio/ringtones目录下,方便后续上层调用。
$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
+ $(LOCAL_PATH)/ringtones/ring_beep.ogg:system/media/audio/ringtones/ring_beep.ogg \
$(LOCAL_PATH)/ringtones/ring_beep.mp3:system/media/audio/ringtones/ring_beep.mp3 \
网上了解了Android音频文件播放方式,主流为MediaPlayer,SoundPool,AudioTrack,AsyncPlayer等,这篇博主写的文章十分不错[Android中播放音乐的几种方式],结合各种播放手段特点,这里选择了SoundPool来实现功能。(https://blog.csdn.net/u013366008/article/details/76577372)
2.定位拨出、接通前状态
上面准备工作做完了,那么接下来就是关键了,如何定位到拨出,振铃,接通这三个状态的代码位置,如何分析这个过程便成了重中之重。想详细了解可以看下这篇文章Android 8.0来电流程分析(一),也有一种别的办法就是看logcat,抓取这三种状态下的上层打印,从LOG中可以发现夹杂着大量的GsmCdmaConnection打印,并持续有GsmCdmaCallTracker,其中会有明确的OFFHOOK,DIALING,IDLE等状态,这部分还需自行百度了解,可以确认到整个通话过程是持续受上层监听的,这些状态值会在通话场景变化后跟随改变,很快找到frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaConnection.java,很快发现了hangup,onDisconnect,onConnectedInOrOut等接口,这不就是我们要的位置吗。
3.边实验边添加合适调用位置
终于到了添加功能的时候了,首先想的就是导包import android.media.SoundPool; 创建和添加如下:
public class GsmCdmaConnection extends Connection {
private static final String LOG_TAG = "GsmCdmaConnection";
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ SoundPool soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
+ int soundId = soundPool.load("/system/media/audio/ringtones/ring_beep.ogg", 0);
+ int streamID = 0;
GsmCdmaCallTracker mOwner;
GsmCdmaCall mParent;
boolean mDisconnected;
+ int play_flag = 0;
public boolean
update (DriverCall dc) {
GsmCdmaCall newParent;
boolean changed = false;
boolean wasConnectingInOrOut = isConnectingInOrOut();
boolean wasHolding = (getState() == GsmCdmaCall.State.HOLDING);
newParent = parentFromDCState(dc.state);
- if (Phone.DEBUG_PHONE) log("parent= " +mParent +", newParent= " + newParent);
+ if (Phone.DEBUG_PHONE) log("parent= " +mParent.getClass().getName().toString() +", newParent= " + newParent.getClass().getName().toString());
+ if(mParent.mState == GsmCdmaCall.State.ALERTING && newParent.mState == GsmCdmaCall.State.ALERTING && (play_flag == 0))
{
+ GsmCdmaPhone phone = mOwner.getPhone();
+ streamID = soundPool.play(soundId, 1, 1, 0, -1, 1);
+ Rlog.d(LOG_TAG, "Player: flag=" + play_flag);
+ Rlog.d(LOG_TAG, "Player: streamID1=" + streamID);
+ play_flag = 1;
}
播放完成后,必须在接通,挂断等情况下停止播放回铃音。
public boolean onDisconnect(int cause) {
boolean changed = false;
mCause = cause;
if (!mDisconnected) {
doDisconnect();
if (DBG) Rlog.d(LOG_TAG, "onDisconnect: cause=" + cause);
+ soundPool.stop(streamID);
...
}
public void hangup() throws CallStateException {
if (!mDisconnected) {
Rlog.e(RIL.RILJ_LOG_TAG,"hangup liuk 111");
+ soundPool.stop(streamID);
mOwner.hangup(this);
} else {
void
onConnectedInOrOut() {
mConnectTime = System.currentTimeMillis();
mConnectTimeReal = SystemClock.elapsedRealtime();
mDuration = 0;
...
+ play_flag = 0 ;
if (!mIsIncoming) {
processNextPostDialChar();
+ Rlog.d(LOG_TAG, "Player: streamID2=" + streamID);
+ soundPool.stop(streamID);
} else {
releaseWakeLock();
}
}
看代码就能理清大抵的思路,在结束,接通后等接口处停止播放,在状态更新中添加振铃判断来进行播放。
遇到的问题
1.播放音频失败,循环不止,异常等,需要检查soundPool.play里面的参数,理解各值代表的意思,一般采用AudioManager.STREAM_MUSIC。 2.接通后电话后回铃音并没有停止,这里涉及到soundPool.stop的规范,仔细排查发现产生了多个streamID,这里添加了play_flag作为标志,振铃中只允许播放一次音频文件,在接通或者挂断后即onConnectedInOrOut置0。 3.来电禁止播放,在onConnectedInOrOut有mIsIncoming判断,通常下stop。
总结
整体实现需要对通话过程有一定了解,准备工作比较多,实际思路不是很复杂。文中只提供一个参考思路,实际上还需要屏蔽网络侧回铃音,整体还是比较鸡肋的功能,如果能让模组解决这种问题是最好的,这种问题还是模组本身的问题。 适用性问题:项目上通话speaker和播放音频的speaker不同,在播放回铃音录音下不会切到通话speaker,所以网络侧不管有没有回铃音都没有影响,这也是前文提到的,模组通话一般是单独添加一个codec芯片,电路上会区分成两路。
|