audioplayer java_AudioPlayerStateMonitor.java

/*

* Copyright (C) 2017 The Android Open Source Project

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package com.android.server.media;

import android.annotation.NonNull;

import android.content.Context;

import android.media.AudioManager;

import android.media.AudioPlaybackConfiguration;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.os.UserHandle;

import android.util.ArrayMap;

import android.util.ArraySet;

import android.util.IntArray;

import android.util.Log;

import com.android.internal.annotations.GuardedBy;

import java.io.PrintWriter;

import java.util.List;

import java.util.Map;

import java.util.Set;

/**

* Monitors the state changes of audio players.

*/

class AudioPlayerStateMonitor {

private static boolean DEBUG = MediaSessionService.DEBUG;

private static String TAG = "AudioPlayerStateMonitor";

private static AudioPlayerStateMonitor sInstance;

/**

* Listener for handling the active state changes of audio players.

*/

interface OnAudioPlayerActiveStateChangedListener {

/**

* Called when the active state of audio player is changed.

*

* @param config The audio playback configuration for the audio player for which active

* state was changed. If {@param isRemoved} is {@code true}, this holds

* outdated information.

* @param isRemoved {@code true} if the audio player is removed.

*/

void onAudioPlayerActiveStateChanged(

@NonNull AudioPlaybackConfiguration config, boolean isRemoved);

}

private final static class MessageHandler extends Handler {

private static final int MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED = 1;

private final OnAudioPlayerActiveStateChangedListener mListener;

MessageHandler(Looper looper, OnAudioPlayerActiveStateChangedListener listener) {

super(looper);

mListener = listener;

}

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED:

mListener.onAudioPlayerActiveStateChanged((AudioPlaybackConfiguration) msg.obj,

msg.arg1 != 0);

break;

}

}

void sendAudioPlayerActiveStateChangedMessage(

final AudioPlaybackConfiguration config, final boolean isRemoved) {

obtainMessage(MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED,

isRemoved ? 1 : 0, 0 /* unused */, config).sendToTarget();

}

}

private final Object mLock = new Object();

@GuardedBy("mLock")

private final MapmListenerMap =

new ArrayMap<>();

@GuardedBy("mLock")

@SuppressWarnings("WeakerAccess") /* synthetic access */

final SetmActiveAudioUids = new ArraySet<>();

@GuardedBy("mLock")

@SuppressWarnings("WeakerAccess") /* synthetic access */

ArrayMapmPrevActiveAudioPlaybackConfigs =

new ArrayMap<>();

// Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)

// The UID whose audio playback becomes active at the last comes first.

// TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.

@GuardedBy("mLock")

@SuppressWarnings("WeakerAccess") /* synthetic access */

final IntArray mSortedAudioPlaybackClientUids = new IntArray();

static AudioPlayerStateMonitor getInstance(Context context) {

synchronized (AudioPlayerStateMonitor.class) {

if (sInstance == null) {

sInstance = new AudioPlayerStateMonitor(context);

}

return sInstance;

}

}

private AudioPlayerStateMonitor(Context context) {

AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

am.registerAudioPlaybackCallback(new AudioManagerPlaybackListener(), null);

}

/**

* Registers OnAudioPlayerActiveStateChangedListener.

*/

public void registerListener(

OnAudioPlayerActiveStateChangedListener listener, Handler handler) {

synchronized (mLock) {

mListenerMap.put(listener, new MessageHandler((handler == null) ?

Looper.myLooper() : handler.getLooper(), listener));

}

}

/**

* Unregisters OnAudioPlayerActiveStateChangedListener.

*/

public void unregisterListener(OnAudioPlayerActiveStateChangedListener listener) {

synchronized (mLock) {

mListenerMap.remove(listener);

}

}

/**

* Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an

* audio/video) The UID whose audio is currently playing comes first, then the UID whose audio

* playback becomes active at the last comes next.

*/

public IntArray getSortedAudioPlaybackClientUids() {

IntArray sortedAudioPlaybackClientUids = new IntArray();

synchronized (mLock) {

sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);

}

return sortedAudioPlaybackClientUids;

}

/**

* Returns if the audio playback is active for the uid.

*/

public boolean isPlaybackActive(int uid) {

synchronized (mLock) {

return mActiveAudioUids.contains(uid);

}

}

/**

* Cleans up the sorted list of audio playback client UIDs with given {@param

* mediaButtonSessionUid}.

*

UIDs whose audio playback are inactive and have started before the media button session's

* audio playback cannot be the lastly played media app. So they won't needed anymore.

*

* @param mediaButtonSessionUid UID of the media button session.

*/

public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) {

synchronized (mLock) {

int userId = UserHandle.getUserId(mediaButtonSessionUid);

for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) {

if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) {

break;

}

int uid = mSortedAudioPlaybackClientUids.get(i);

if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) {

// Clean up unnecessary UIDs.

// It doesn't need to be managed profile aware because it's just to prevent

// the list from increasing indefinitely. The media button session updating

// shouldn't be affected by cleaning up.

mSortedAudioPlaybackClientUids.remove(i);

}

}

}

}

/**

* Dumps {@link AudioPlayerStateMonitor}.

*/

public void dump(Context context, PrintWriter pw, String prefix) {

synchronized (mLock) {

pw.println(prefix + "Audio playback (lastly played comes first)");

String indent = prefix + " ";

for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) {

int uid = mSortedAudioPlaybackClientUids.get(i);

pw.print(indent + "uid=" + uid + " packages=");

String[] packages = context.getPackageManager().getPackagesForUid(uid);

if (packages != null && packages.length > 0) {

for (int j = 0; j < packages.length; j++) {

pw.print(packages[j] + " ");

}

}

pw.println();

}

}

}

@GuardedBy("mLock")

private void sendAudioPlayerActiveStateChangedMessageLocked(

final AudioPlaybackConfiguration config, final boolean isRemoved) {

for (MessageHandler messageHandler : mListenerMap.values()) {

messageHandler.sendAudioPlayerActiveStateChangedMessage(config, isRemoved);

}

}

private class AudioManagerPlaybackListener extends AudioManager.AudioPlaybackCallback {

@Override

public void onPlaybackConfigChanged(Listconfigs) {

synchronized (mLock) {

// Update mActiveAudioUids

mActiveAudioUids.clear();

ArrayMapactiveAudioPlaybackConfigs =

new ArrayMap<>();

for (AudioPlaybackConfiguration config : configs) {

if (config.isActive()) {

mActiveAudioUids.add(config.getClientUid());

activeAudioPlaybackConfigs.put(config.getPlayerInterfaceId(), config);

}

}

// Update mSortedAuioPlaybackClientUids.

for (int i = 0; i < activeAudioPlaybackConfigs.size(); ++i) {

AudioPlaybackConfiguration config = activeAudioPlaybackConfigs.valueAt(i);

final int uid = config.getClientUid();

if (!mPrevActiveAudioPlaybackConfigs.containsKey(

config.getPlayerInterfaceId())) {

if (DEBUG) {

Log.d(TAG, "Found a new active media playback. "

+ AudioPlaybackConfiguration.toLogFriendlyString(config));

}

// New active audio playback.

int index = mSortedAudioPlaybackClientUids.indexOf(uid);

if (index == 0) {

// It's the lastly played music app already. Skip updating.

continue;

} else if (index > 0) {

mSortedAudioPlaybackClientUids.remove(index);

}

mSortedAudioPlaybackClientUids.add(0, uid);

}

}

if (mActiveAudioUids.size() > 0

&& !mActiveAudioUids.contains(mSortedAudioPlaybackClientUids.get(0))) {

int firstActiveUid = -1;

int firatActiveUidIndex = -1;

for (int i = 1; i < mSortedAudioPlaybackClientUids.size(); ++i) {

int uid = mSortedAudioPlaybackClientUids.get(i);

if (mActiveAudioUids.contains(uid)) {

firatActiveUidIndex = i;

firstActiveUid = uid;

break;

}

}

for (int i = firatActiveUidIndex; i > 0; --i) {

mSortedAudioPlaybackClientUids.set(i,

mSortedAudioPlaybackClientUids.get(i - 1));

}

mSortedAudioPlaybackClientUids.set(0, firstActiveUid);

}

// Notify the active state change of audio players.

for (AudioPlaybackConfiguration config : configs) {

final int pii = config.getPlayerInterfaceId();

boolean wasActive = mPrevActiveAudioPlaybackConfigs.remove(pii) != null;

if (wasActive != config.isActive()) {

sendAudioPlayerActiveStateChangedMessageLocked(

config, /* isRemoved */ false);

}

}

for (AudioPlaybackConfiguration config : mPrevActiveAudioPlaybackConfigs.values()) {

sendAudioPlayerActiveStateChangedMessageLocked(config, /* isRemoved */ true);

}

// Update mPrevActiveAudioPlaybackConfigs

mPrevActiveAudioPlaybackConfigs = activeAudioPlaybackConfigs;

}

}

}

}

Java程序

|

296行

|

12.2 KB

/*

* Copyright (C) 2017 The Android Open Source Project

*

* Licensed under the Apache License, Version 2.0 (the "License");

* you may not use this file except in compliance with the License.

* You may obtain a copy of the License at

*

* http://www.apache.org/licenses/LICENSE-2.0

*

* Unless required by applicable law or agreed to in writing, software

* distributed under the License is distributed on an "AS IS" BASIS,

* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

* See the License for the specific language governing permissions and

* limitations under the License.

*/

package com.android.server.media;

import android.annotation.NonNull;

import android.content.Context;

import android.media.AudioManager;

import android.media.AudioPlaybackConfiguration;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.os.UserHandle;

import android.util.ArrayMap;

import android.util.ArraySet;

import android.util.IntArray;

import android.util.Log;

import com.android.internal.annotations.GuardedBy;

import java.io.PrintWriter;

import java.util.List;

import java.util.Map;

import java.util.Set;

/**

* Monitors the state changes of audio players.

*/

class AudioPlayerStateMonitor {

private static boolean DEBUG = MediaSessionService.DEBUG;

private static String TAG = "AudioPlayerStateMonitor";

private static AudioPlayerStateMonitor sInstance;

/**

* Listener for handling the active state changes of audio players.

*/

interface OnAudioPlayerActiveStateChangedListener {

/**

* Called when the active state of audio player is changed.

*

* @param config The audio playback configuration for the audio player for which active

* state was changed. If {@param isRemoved} is {@code true}, this holds

* outdated information.

* @param isRemoved {@code true} if the audio player is removed.

*/

void onAudioPlayerActiveStateChanged(

@NonNull AudioPlaybackConfiguration config, boolean isRemoved);

}

private final static class MessageHandler extends Handler {

private static final int MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED = 1;

private final OnAudioPlayerActiveStateChangedListener mListener;

MessageHandler(Looper looper, OnAudioPlayerActiveStateChangedListener listener) {

super(looper);

mListener = listener;

}

@Override

public void handleMessage(Message msg) {

switch (msg.what) {

case MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED:

mListener.onAudioPlayerActiveStateChanged((AudioPlaybackConfiguration) msg.obj,

msg.arg1 != 0);

break;

}

}

void sendAudioPlayerActiveStateChangedMessage(

final AudioPlaybackConfiguration config, final boolean isRemoved) {

obtainMessage(MSG_AUDIO_PLAYER_ACTIVE_STATE_CHANGED,

isRemoved ? 1 : 0, 0 /* unused */, config).sendToTarget();

}

}

private final Object mLock = new Object();

@GuardedBy("mLock")

private final Map mListenerMap =

new ArrayMap<>();

@GuardedBy("mLock")

@SuppressWarnings("WeakerAccess") /* synthetic access */

final Set mActiveAudioUids = new ArraySet<>();

@GuardedBy("mLock")

@SuppressWarnings("WeakerAccess") /* synthetic access */

ArrayMap mPrevActiveAudioPlaybackConfigs =

new ArrayMap<>();

// Sorted array of UIDs that had active audio playback. (i.e. playing an audio/video)

// The UID whose audio playback becomes active at the last comes first.

// TODO(b/35278867): Find and use unique identifier for apps because apps may share the UID.

@GuardedBy("mLock")

@SuppressWarnings("WeakerAccess") /* synthetic access */

final IntArray mSortedAudioPlaybackClientUids = new IntArray();

static AudioPlayerStateMonitor getInstance(Context context) {

synchronized (AudioPlayerStateMonitor.class) {

if (sInstance == null) {

sInstance = new AudioPlayerStateMonitor(context);

}

return sInstance;

}

}

private AudioPlayerStateMonitor(Context context) {

AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

am.registerAudioPlaybackCallback(new AudioManagerPlaybackListener(), null);

}

/**

* Registers OnAudioPlayerActiveStateChangedListener.

*/

public void registerListener(

OnAudioPlayerActiveStateChangedListener listener, Handler handler) {

synchronized (mLock) {

mListenerMap.put(listener, new MessageHandler((handler == null) ?

Looper.myLooper() : handler.getLooper(), listener));

}

}

/**

* Unregisters OnAudioPlayerActiveStateChangedListener.

*/

public void unregisterListener(OnAudioPlayerActiveStateChangedListener listener) {

synchronized (mLock) {

mListenerMap.remove(listener);

}

}

/**

* Returns the sorted list of UIDs that have had active audio playback. (i.e. playing an

* audio/video) The UID whose audio is currently playing comes first, then the UID whose audio

* playback becomes active at the last comes next.

*/

public IntArray getSortedAudioPlaybackClientUids() {

IntArray sortedAudioPlaybackClientUids = new IntArray();

synchronized (mLock) {

sortedAudioPlaybackClientUids.addAll(mSortedAudioPlaybackClientUids);

}

return sortedAudioPlaybackClientUids;

}

/**

* Returns if the audio playback is active for the uid.

*/

public boolean isPlaybackActive(int uid) {

synchronized (mLock) {

return mActiveAudioUids.contains(uid);

}

}

/**

* Cleans up the sorted list of audio playback client UIDs with given {@param

* mediaButtonSessionUid}.

*

UIDs whose audio playback are inactive and have started before the media button session's

* audio playback cannot be the lastly played media app. So they won't needed anymore.

*

* @param mediaButtonSessionUid UID of the media button session.

*/

public void cleanUpAudioPlaybackUids(int mediaButtonSessionUid) {

synchronized (mLock) {

int userId = UserHandle.getUserId(mediaButtonSessionUid);

for (int i = mSortedAudioPlaybackClientUids.size() - 1; i >= 0; i--) {

if (mSortedAudioPlaybackClientUids.get(i) == mediaButtonSessionUid) {

break;

}

int uid = mSortedAudioPlaybackClientUids.get(i);

if (userId == UserHandle.getUserId(uid) && !isPlaybackActive(uid)) {

// Clean up unnecessary UIDs.

// It doesn't need to be managed profile aware because it's just to prevent

// the list from increasing indefinitely. The media button session updating

// shouldn't be affected by cleaning up.

mSortedAudioPlaybackClientUids.remove(i);

}

}

}

}

/**

* Dumps {@link AudioPlayerStateMonitor}.

*/

public void dump(Context context, PrintWriter pw, String prefix) {

synchronized (mLock) {

pw.println(prefix + "Audio playback (lastly played comes first)");

String indent = prefix + " ";

for (int i = 0; i < mSortedAudioPlaybackClientUids.size(); i++) {

int uid = mSortedAudioPlaybackClientUids.get(i);

pw.print(indent + "uid=" + uid + " packages=");

String[] packages = context.getPackageManager().getPackagesForUid(uid);

if (packages != null && packages.length > 0) {

for (int j = 0; j < packages.length; j++) {

pw.print(packages[j] + " ");

}

}

pw.println();

}

}

}

@GuardedBy("mLock")

private void sendAudioPlayerActiveStateChangedMessageLocked(

final AudioPlaybackConfiguration config, final boolean isRemoved) {

for (MessageHandler messageHandler : mListenerMap.values()) {

messageHandler.sendAudioPlayerActiveStateChangedMessage(config, isRemoved);

}

}

private class AudioManagerPlaybackListener extends AudioManager.AudioPlaybackCallback {

@Override

public void onPlaybackConfigChanged(List configs) {

synchronized (mLock) {

// Update mActiveAudioUids

mActiveAudioUids.clear();

ArrayMap activeAudioPlaybackConfigs =

new ArrayMap<>();

for (AudioPlaybackConfiguration config : configs) {

if (config.isActive()) {

mActiveAudioUids.add(config.getClientUid());

activeAudioPlaybackConfigs.put(config.getPlayerInterfaceId(), config);

}

}

// Update mSortedAuioPlaybackClientUids.

for (int i = 0; i < activeAudioPlaybackConfigs.size(); ++i) {

AudioPlaybackConfiguration config = activeAudioPlaybackConfigs.valueAt(i);

final int uid = config.getClientUid();

if (!mPrevActiveAudioPlaybackConfigs.containsKey(

config.getPlayerInterfaceId())) {

if (DEBUG) {

Log.d(TAG, "Found a new active media playback. "

+ AudioPlaybackConfiguration.toLogFriendlyString(config));

}

// New active audio playback.

int index = mSortedAudioPlaybackClientUids.indexOf(uid);

if (index == 0) {

// It's the lastly played music app already. Skip updating.

continue;

} else if (index > 0) {

mSortedAudioPlaybackClientUids.remove(index);

}

mSortedAudioPlaybackClientUids.add(0, uid);

}

}

if (mActiveAudioUids.size() > 0

&& !mActiveAudioUids.contains(mSortedAudioPlaybackClientUids.get(0))) {

int firstActiveUid = -1;

int firatActiveUidIndex = -1;

for (int i = 1; i < mSortedAudioPlaybackClientUids.size(); ++i) {

int uid = mSortedAudioPlaybackClientUids.get(i);

if (mActiveAudioUids.contains(uid)) {

firatActiveUidIndex = i;

firstActiveUid = uid;

break;

}

}

for (int i = firatActiveUidIndex; i > 0; --i) {

mSortedAudioPlaybackClientUids.set(i,

mSortedAudioPlaybackClientUids.get(i - 1));

}

mSortedAudioPlaybackClientUids.set(0, firstActiveUid);

}

// Notify the active state change of audio players.

for (AudioPlaybackConfiguration config : configs) {

final int pii = config.getPlayerInterfaceId();

boolean wasActive = mPrevActiveAudioPlaybackConfigs.remove(pii) != null;

if (wasActive != config.isActive()) {

sendAudioPlayerActiveStateChangedMessageLocked(

config, /* isRemoved */ false);

}

}

for (AudioPlaybackConfiguration config : mPrevActiveAudioPlaybackConfigs.values()) {

sendAudioPlayerActiveStateChangedMessageLocked(config, /* isRemoved */ true);

}

// Update mPrevActiveAudioPlaybackConfigs

mPrevActiveAudioPlaybackConfigs = activeAudioPlaybackConfigs;

}

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值