车载设备上音视频应用续播功能的实现

最近产品的同事提出:如果用户ACC Off前正在使用音视频类应用收听/观看音视频节目,那么要求下次ACC On时能继续收听/观看

仔细阅读Audio部分的源码,发现在AF端处理音频的子线程PlaybackThread中有一个成员容器变量mActiveTracks,该变量表示当前子线程中所有活跃的音频流列表,基于这个逻辑,可以在ACC Off前统计各个子线程中活跃的音频流列表,然后获取音频流Track所属的pid,最后根据pid查询所属的包名,然后把包名信息保存到文件,当下次ACC On时根据包名拉起这些音视频类应用即可。

具体实现步骤如下:

1.在Framework层添加Property属性,并赋予audioserver进程读写的权限

/device/qcom/msm8953_64/msm8953_64.mk

...
PRODUCT_COPY_FILES += device/qcom/msm8953_64/whitelistedapps.xml:system/etc/whitelistedapps.xml \
                      device/qcom/msm8953_64/gamedwhitelist.xml:system/etc/gamedwhitelist.xml
#add by xpzhi 增加persist属性服务
PRODUCT_PROPERTY_OVERRIDES += \
persist.audio.mogo=mogo

PRODUCT_PROPERTY_OVERRIDES += \
           dalvik.vm.heapminfree=4m \
           dalvik.vm.heapstartsize=16m
$(call inherit-product, frameworks/native/build/phone-xhdpi-2048-dalvik-heap.mk)
$(call inherit-product, device/qcom/common/common64.mk)
...

我司使用的是高通8953的方案,网友注意根据自己的实际情况变换路径

system/sepolicy/audioserver.te

...
# Grant access to audio files to audioserver
allow audioserver audio_data_file:dir ra_dir_perms;
allow audioserver audio_data_file:file create_file_perms;

# Grant access to property_set add by xpzhi 允许AudioServer进程读写该属性
set_prop(audioserver, audio_prop)
allow audioserver audio_prop:property_service set;

# Needed on some devices for playing audio on paired BT device,
# but seems appropriate for all devices.
unix_socket_connect(audioserver, bluetooth, bluetooth)

...

这里添加的权限其实是SeLinux权限,忘记加的话也可以利用adb shell命令把Android设备的SeLinux权限临时关闭,

set enforce 0 设置为宽容模式 (1是强制执行模式 ;get enforce 获取当前SeLinux状态)


2.在Native层的Track对象上添加pid属性,在Track的构造函数中保存Track所属的pid信息

frameworks/av/services/audioflinger/PlaybackTracks.h

...
public:
    void triggerEvents(AudioSystem::sync_event_t type);
    void invalidate();
    void disable();

    bool isInvalid() const { return mIsInvalid; }
    int fastIndex() const { return mFastIndex; }
    pid_t               mPid;//******add by xpzhi 添加Track所属的pid************
...

frameworks/av/services/audioflinger/Tracks.cpp

...
// ----------------------------------------------------------------------------

// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
AudioFlinger::PlaybackThread::Track::Track(
            PlaybackThread *thread,
            const sp<Client>& client,
            audio_stream_type_t streamType,
            uint32_t sampleRate,
            audio_format_t format,
            audio_channel_mask_t channelMask,
            size_t frameCount,
            void *buffer,
            const sp<IMemory>& sharedBuffer,
            audio_session_t sessionId,
            int uid,
            audio_output_flags_t flags,
            track_type type)
    :   TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
                  (sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,
                  sessionId, uid, true /*isOut*/,
                  (type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
                  type),
    mFillingUpStatus(FS_INVALID),
    // mRetryCount initialized later when needed
    mSharedBuffer(sharedBuffer),
    mStreamType(streamType),
    mName(-1),  // see note below
    mMainBuffer(thread->mixBuffer()),
    mAuxBuffer(NULL),
    mAuxEffectId(0), mHasVolumeController(false),
    mPresentationCompleteFrames(0),
    mFrameMap(16 /* sink-frame-to-track-frame map memory */),
    // mSinkTimestamp
    mFastIndex(-1),
    mCachedVolume(1.0),
    mIsInvalid(false),
    mAudioTrackServerProxy(NULL),
    mResumeToStopping(false),
    mFlushHwPending(false),
    mFlags(flags)
{
    // client == 0 implies sharedBuffer == 0
    ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));

    ALOGV_IF(sharedBuffer != 0, "sharedBuffer: %p, size: %zu", sharedBuffer->pointer(),
            sharedBuffer->size());

    if (mCblk == NULL) {
        return;
    }

    mPid=client->pid(); //******add by xpzhi 保存Track所属的pid信息************

    if (sharedBuffer == 0) {
        mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
                mFrameSize, !isExternalTrack(), sampleRate);
    } else {
        ....
    }
}
...


3.在混音的时候(准确来说是更新活跃的Track列表)遍历活跃的mActiveTracks,将这些Track的pid拼接起来,存储到prop属性里,供应用程序层读取

frameworks/av/services/audioflinger/AudioFlinger.h

...
// no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held
float streamVolume_l(audio_stream_type_t stream) const
                                { return mStreamTypes[stream].volume; }
void ioConfigChanged(audio_io_config_event event,
                                   const sp<AudioIoDescriptor>& ioDesc,
                                   pid_t pid = 0);
void updatePid();//******add by xpzhi 遍历音频应用并保存pid信息************
...


frameworks/av/services/audioflinger/AudioFlinger.cpp

...
void AudioFlinger::ioConfigChanged(audio_io_config_event event,
                                   const sp<AudioIoDescriptor>& ioDesc,
                                   pid_t pid)
{
    Mutex::Autolock _l(mClientLock);
    size_t size = mNotificationClients.size();
    for (size_t i = 0; i < size; i++) {
        if ((pid == 0) || (mNotificationClients.keyAt(i) == pid)) {
            mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioDesc);
        }
    }
}


//******add by xpzhi 遍历所有的子线程并保存Track对应的pid信息************
void AudioFlinger::updatePid(){
   char prop[50];       
   property_get("persist.audio.mogo",prop,"");    
   std::string tempStr="";
   for (size_t i = 0; i < mPlaybackThreads.size(); i++) {                 
       PlaybackThread * thread = mPlaybackThreads.valueAt(i).get();                 
       for(size_t j = 0; j < thread->mActiveTracks.size(); j++) {
           const sp<PlaybackThread::Track> t = thread->mActiveTracks[j].promote();
           if (t == 0) {
               continue;
           }
           PlaybackThread::Track* const track = t.get();
           if(track->mPid == 0) {
               continue;
           }           
           tempStr = tempStr+std::to_string(track->mPid)+",";
       }
   }
   if(tempStr.length() > 1) {
       tempStr = tempStr.substr(0, tempStr.length()-1);           
       if(strcmp(prop, (char *)tempStr.c_str()) != 0) {            
          int mogo = property_set("persist.audio.mogo",(char *)tempStr.c_str());
       }             
   } else { 
       if(strcmp(prop,(char *)"")!=0) {
           int mogo=property_set("persist.audio.mogo",(char *)"");
       }           
   }     
}
...


frameworks/av/services/audioflinger/Threads.cpp

...
// removeTracks_l() must be called with ThreadBase::mLock held
void AudioFlinger::PlaybackThread::removeTracks_l(const Vector< sp<Track> >& tracksToRemove)
{
    size_t count = tracksToRemove.size();
    if (count > 0) {
        for (size_t i=0 ; i<count ; i++) {
            const sp<Track>& track = tracksToRemove.itemAt(i);
            mActiveTracks.remove(track);
            mWakeLockUids.remove(track->uid());
            mActiveTracksGeneration++;
            ALOGV("removeTracks_l removing track on session %d", track->sessionId());            
            sp<EffectChain> chain = getEffectChain_l(track->sessionId());
            if (chain != 0) {
                ALOGV("stopping track on chain %p for session Id: %d", chain.get(),
                        track->sessionId());
                chain->decActiveTrackCnt();
            }
            if (track->isTerminated()) {
                removeTrack_l(track);
            }

          if(track->streamType() == AUDIO_STREAM_ALARM) {
           mAudioFlinger->updateStreamVolume(-1);  // add by xpzhi MGOS-462
          }
    
        }
    }    
    
    mAudioFlinger->updatePid();//******add by xpzhi 更新活跃的Track列表时通知AF去遍历活跃的Track列表并保存Track对应的pid信息************
}
...


4.系统应用程序层读取prop属性后,获取系统中当前运行的应用程序列表,根据pid查询应用程序对应的包名,最后将这些包名写入文件保存,下次ACC On时读取包名信息并拉起来恢复播放(实现比较简单网友可以自己实现)

 

注意实际测试中发现的问题:

车机上酷我音乐使用的AudioTrack对象播放的音频;QQ音乐、懒人听书等应用都是使用Mediaplayer播放的音频。

1.获取音频流Track对象所属的pid信息时,函数IPCThreadState::self()->getCallingPid()获取得并不精确,如果是用Mediaplayer播放音频的话,获取到的pid是Media_Server进程的pid(其实MPS请求构建Track对象时会携带自己的pid信息,参数就直接可以使用)


2.使用AudioTrack对象播放音频时,是由MixerThread子线程去混音的;使用Mediaplayer播放音频时,是由OffloadThread(硬解码)线程去处理的,两个子线程均有mActiveTracks成员变量,因此在统计pid时都要遍历一次才不至于遗漏


 

上一篇:安装Octopress的记录


下一篇:夜里盘算千条路,清早起来磨豆腐