最近产品的同事提出:如果用户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时都要遍历一次才不至于遗漏