blob: e730ee831616633bddcea2f34bf30281c945592f [file] [log] [blame]
/*
* Copyright (C) 2015 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.car;
import android.content.Context;
import android.media.AudioFocusInfo;
import android.media.AudioManager;
import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.android.car.hal.AudioHalService;
import com.android.car.hal.AudioHalService.AudioHalListener;
import com.android.car.hal.VehicleHal;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
public class CarAudioService implements CarServiceBase, AudioHalListener {
// support only two streams, default and media for now.
private static final int NUMBER_OF_STREAMS = 2;
private static final int FOCUS_STACK_DEPTH_TO_MONITOR = 2;
private final AudioHalService mAudioHal;
private final Context mContext;
private final HandlerThread mHandlerThread;
private final CarAudioChangeHandler mHandler;
private final SystemFocusListener mSystemFocusListener;
private AudioPolicy mAudioPolicy;
private final Object mLock = new Object();
@GuardedBy("mLock")
private int mCurrentFocusState;
@GuardedBy("mLock")
private int mAllowedStreams;
@GuardedBy("mLock")
private int mLastFocusRequest;
@GuardedBy("mLock")
private int mLastFocusRequestStreams;
@GuardedBy("mLock")
private final AudioFocusInfo[] mFocusInfos = new AudioFocusInfo[FOCUS_STACK_DEPTH_TO_MONITOR];
public CarAudioService(Context context) {
mAudioHal = VehicleHal.getInstance().getAudioHal();
mContext = context;
mHandlerThread = new HandlerThread(CarLog.TAG_AUDIO);
mSystemFocusListener = new SystemFocusListener();
mHandlerThread.start();
AudioPolicy.Builder builder = new AudioPolicy.Builder(mContext);
builder.setLooper(mHandlerThread.getLooper()).
setAudioPolicyFocusListener(mSystemFocusListener);
mAudioPolicy = builder.build();
mHandler = new CarAudioChangeHandler(mHandlerThread.getLooper());
}
@Override
public void init() {
AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
int r = am.registerAudioPolicy(mAudioPolicy);
if (r != 0) {
throw new RuntimeException("registerAudioPolicy failed " + r);
}
mAudioHal.setListener(this);
}
@Override
public void release() {
AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
am.unregisterAudioPolicyAsync(mAudioPolicy);
}
@Override
public void dump(PrintWriter writer) {
// TODO Auto-generated method stub
}
@Override
public void onFocusChange(int focusState, int streams) {
mHandler.handleFocusChange(focusState, streams);
}
@Override
public void onVolumeChange(int volume, int streamNumber) {
mHandler.handleVolumeChange(volume, streamNumber);
}
@Override
public void onStreamStatusChange(int state, int streamNumber) {
mHandler.handleStreamStateChange(state, streamNumber);
}
private void doHandleCarFocusChange(int focusState, int streams) {
mAllowedStreams = streams;
switch (focusState) {
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN:
doHandleFocusGainFromCar();
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_GAIN_TRANSIENT:
doHandleFocusGainTransientFromCar();
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS:
doHandleFocusLossFromCar();
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT:
doHandleFocusLossTransientFromCar();
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_CAN_DUCK:
doHandleFocusLossTransientCanDuckFromCar();
break;
case AudioHalService.VEHICLE_AUDIO_FOCUS_STATE_LOSS_TRANSIENT_EXLCUSIVE:
doHandleFocusLossTransientExclusiveFromCar();
break;
}
}
private void doHandleFocusGainFromCar() {
//TODO
}
private void doHandleFocusGainTransientFromCar() {
//TODO
}
private void doHandleFocusLossFromCar() {
//TODO
}
private void doHandleFocusLossTransientFromCar() {
//TODO
}
private void doHandleFocusLossTransientCanDuckFromCar() {
//TODO
}
private void doHandleFocusLossTransientExclusiveFromCar() {
//TODO
}
private void doHandleVolumeChange(int volume, int streamNumber) {
//TODO
}
private void doHandleStreamStatusChange(int state, int streamNumber) {
//TODO
}
private void lockSystemAudioFocus() {
//TODO use AUDIOFOCUS_FLAG_LOCK
}
private void unlockSystemAudioFocus() {
//TODO
}
private void sendCarAudioFocusRequestIfNecessary() {
//TODO
}
private void doHandleSystemAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
if (requestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
mFocusInfos[0] = afi;
sendCarAudioFocusRequestIfNecessary();
}
}
private void doHandleSystemAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
if (!wasNotified) {
// app released focus by itself. Remove from stack if it is there.
boolean mayNeedsFocusChange = false;
for (int i = 0; i < mFocusInfos.length; i++) {
AudioFocusInfo info = mFocusInfos[i];
if (info == null) {
continue;
}
if (info.getClientId().equals(afi.getClientId())) {
if (i == 0) { // this is top component releasing focus
// clear bottom one as well. This can lead into sending focus request
// if there is a focus holder other than this one.
// But that cannot be distinguished. So release it now, and request
// again if necessary.
mFocusInfos[1] = null;
break;
}
mFocusInfos[i] = null;
mayNeedsFocusChange = true;
}
}
if (mayNeedsFocusChange) {
sendCarAudioFocusRequestIfNecessary();
}
} else { // there will be a separate grant soon
mFocusInfos[1] = afi;
}
}
private class SystemFocusListener extends AudioPolicyFocusListener {
@Override
public void onAudioFocusGrant(AudioFocusInfo afi, int requestResult) {
Log.i(CarLog.TAG_AUDIO, "onAudioFocusGrant " + afi + " result:" + requestResult +
" clientId:" + afi.getClientId() + " loss received:" + afi.getLossReceived());
doHandleSystemAudioFocusGrant(afi, requestResult);
}
@Override
public void onAudioFocusLoss(AudioFocusInfo afi, boolean wasNotified) {
Log.i(CarLog.TAG_AUDIO, "onAudioFocusLoss " + afi + " notified:" + wasNotified +
" clientId:" + afi.getClientId() + " loss received:" + afi.getLossReceived());
doHandleSystemAudioFocusLoss(afi, wasNotified);
}
}
private class CarAudioChangeHandler extends Handler {
private static final int MSG_FOCUS_CHANGE = 0;
private static final int MSG_STREAM_STATE_CHANGE = 1;
private static final int MSG_VOLUME_CHANGE = 2;
private CarAudioChangeHandler(Looper looper) {
super(looper);
}
private void handleFocusChange(int focusState, int streams) {
Message msg = obtainMessage(MSG_FOCUS_CHANGE, focusState, streams);
sendMessage(msg);
}
private void handleStreamStateChange(int state, int streamNumber) {
Message msg = obtainMessage(MSG_STREAM_STATE_CHANGE, state, streamNumber);
sendMessage(msg);
}
private void handleVolumeChange(int volume, int streamNumber) {
Message msg = obtainMessage(MSG_VOLUME_CHANGE, volume, streamNumber);
sendMessage(msg);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_FOCUS_CHANGE:
doHandleCarFocusChange(msg.arg1, msg.arg2);
break;
case MSG_STREAM_STATE_CHANGE:
doHandleStreamStatusChange(msg.arg1, msg.arg2);
break;
case MSG_VOLUME_CHANGE:
doHandleVolumeChange(msg.arg1, msg.arg2);
break;
}
}
}
}