AudioService: alternative way of handling device rotation
For devices that monitor orientation (primarily for channel assignment to stereo speakers): The com.android.server.policy.WindowOrientationListener API is more power efficient than simply monitoring the device's orientation. When supported, use it instead of android.view.OrientationEventListener. When WindowOrientationListener reports an orientation change, start a thread to poll the UI orientation, as its change may lag behind the observed rotation. Gradually increasing delays between polls are stored in a table. Bug 24415763 Change-Id: I69bf68da6107af24cd02a48961dd17ceab557816
This commit is contained in:
@@ -502,7 +502,6 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
private volatile IRingtonePlayer mRingtonePlayer;
|
private volatile IRingtonePlayer mRingtonePlayer;
|
||||||
|
|
||||||
private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
|
private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
|
||||||
private int mDeviceRotation = Surface.ROTATION_0;
|
|
||||||
|
|
||||||
// Request to override default use of A2DP for media.
|
// Request to override default use of A2DP for media.
|
||||||
private boolean mBluetoothA2dpEnabled;
|
private boolean mBluetoothA2dpEnabled;
|
||||||
@@ -546,8 +545,6 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
// If absolute volume is supported in AVRCP device
|
// If absolute volume is supported in AVRCP device
|
||||||
private boolean mAvrcpAbsVolSupported = false;
|
private boolean mAvrcpAbsVolSupported = false;
|
||||||
|
|
||||||
private AudioOrientationEventListener mOrientationListener;
|
|
||||||
|
|
||||||
private static Long mLastDeviceConnectMsgTime = new Long(0);
|
private static Long mLastDeviceConnectMsgTime = new Long(0);
|
||||||
|
|
||||||
private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
|
private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
|
||||||
@@ -670,15 +667,7 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
}
|
}
|
||||||
mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
|
mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
|
||||||
if (mMonitorRotation) {
|
if (mMonitorRotation) {
|
||||||
mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
|
RotationHelper.init(mContext, mAudioHandler);
|
||||||
.getDefaultDisplay().getRotation();
|
|
||||||
Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation);
|
|
||||||
|
|
||||||
mOrientationListener = new AudioOrientationEventListener(mContext);
|
|
||||||
mOrientationListener.enable();
|
|
||||||
|
|
||||||
// initialize rotation in AudioSystem
|
|
||||||
setRotationForAudioSystem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
|
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
|
||||||
@@ -808,7 +797,7 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
setOrientationForAudioSystem();
|
setOrientationForAudioSystem();
|
||||||
}
|
}
|
||||||
if (mMonitorRotation) {
|
if (mMonitorRotation) {
|
||||||
setRotationForAudioSystem();
|
RotationHelper.updateOrientation();
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mBluetoothA2dpEnabledLock) {
|
synchronized (mBluetoothA2dpEnabledLock) {
|
||||||
@@ -1061,25 +1050,6 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
|
return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AudioOrientationEventListener
|
|
||||||
extends OrientationEventListener {
|
|
||||||
public AudioOrientationEventListener(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onOrientationChanged(int orientation) {
|
|
||||||
//Even though we're responding to phone orientation events,
|
|
||||||
//use display rotation so audio stays in sync with video/dialogs
|
|
||||||
int newRotation = ((WindowManager) mContext.getSystemService(
|
|
||||||
Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
|
|
||||||
if (newRotation != mDeviceRotation) {
|
|
||||||
mDeviceRotation = newRotation;
|
|
||||||
setRotationForAudioSystem();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// IPC methods
|
// IPC methods
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
@@ -5069,14 +5039,13 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
}
|
}
|
||||||
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
|
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
|
||||||
if (mMonitorRotation) {
|
if (mMonitorRotation) {
|
||||||
mOrientationListener.onOrientationChanged(0); //argument is ignored anyway
|
RotationHelper.enable();
|
||||||
mOrientationListener.enable();
|
|
||||||
}
|
}
|
||||||
AudioSystem.setParameters("screen_state=on");
|
AudioSystem.setParameters("screen_state=on");
|
||||||
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
|
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
|
||||||
if (mMonitorRotation) {
|
if (mMonitorRotation) {
|
||||||
//reduce wakeups (save current) by only listening when display is on
|
//reduce wakeups (save current) by only listening when display is on
|
||||||
mOrientationListener.disable();
|
RotationHelper.disable();
|
||||||
}
|
}
|
||||||
AudioSystem.setParameters("screen_state=off");
|
AudioSystem.setParameters("screen_state=off");
|
||||||
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
|
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
|
||||||
@@ -5321,6 +5290,7 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO move to an external "orientation helper" class
|
||||||
private void setOrientationForAudioSystem() {
|
private void setOrientationForAudioSystem() {
|
||||||
switch (mDeviceOrientation) {
|
switch (mDeviceOrientation) {
|
||||||
case Configuration.ORIENTATION_LANDSCAPE:
|
case Configuration.ORIENTATION_LANDSCAPE:
|
||||||
@@ -5344,26 +5314,6 @@ public class AudioService extends IAudioService.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setRotationForAudioSystem() {
|
|
||||||
switch (mDeviceRotation) {
|
|
||||||
case Surface.ROTATION_0:
|
|
||||||
AudioSystem.setParameters("rotation=0");
|
|
||||||
break;
|
|
||||||
case Surface.ROTATION_90:
|
|
||||||
AudioSystem.setParameters("rotation=90");
|
|
||||||
break;
|
|
||||||
case Surface.ROTATION_180:
|
|
||||||
AudioSystem.setParameters("rotation=180");
|
|
||||||
break;
|
|
||||||
case Surface.ROTATION_270:
|
|
||||||
AudioSystem.setParameters("rotation=270");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Log.e(TAG, "Unknown device rotation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Handles request to override default use of A2DP for media.
|
// Handles request to override default use of A2DP for media.
|
||||||
// Must be called synchronized on mConnectedDevices
|
// Must be called synchronized on mConnectedDevices
|
||||||
public void setBluetoothA2dpOnInt(boolean on) {
|
public void setBluetoothA2dpOnInt(boolean on) {
|
||||||
|
|||||||
209
services/core/java/com/android/server/audio/RotationHelper.java
Normal file
209
services/core/java/com/android/server/audio/RotationHelper.java
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* 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.server.audio;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.media.AudioSystem;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.OrientationEventListener;
|
||||||
|
import android.view.Surface;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
import com.android.server.policy.WindowOrientationListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to handle device rotation events for AudioService, and forward device rotation
|
||||||
|
* to the audio HALs through AudioSystem.
|
||||||
|
*
|
||||||
|
* The role of this class is to monitor device orientation changes, and upon rotation,
|
||||||
|
* verify the UI orientation. In case of a change, send the new orientation, in increments
|
||||||
|
* of 90deg, through AudioSystem.
|
||||||
|
*
|
||||||
|
* Note that even though we're responding to device orientation events, we always
|
||||||
|
* query the display rotation so audio stays in sync with video/dialogs. This is
|
||||||
|
* done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
|
||||||
|
*/
|
||||||
|
class RotationHelper {
|
||||||
|
|
||||||
|
private static final String TAG = "AudioService.RotationHelper";
|
||||||
|
|
||||||
|
private static AudioOrientationListener sOrientationListener;
|
||||||
|
private static AudioWindowOrientationListener sWindowOrientationListener;
|
||||||
|
|
||||||
|
private static final Object sRotationLock = new Object();
|
||||||
|
private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
|
||||||
|
|
||||||
|
private static Context sContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* post conditions:
|
||||||
|
* - (sWindowOrientationListener != null) xor (sOrientationListener != null)
|
||||||
|
* - sWindowOrientationListener xor sOrientationListener is enabled
|
||||||
|
* - sContext != null
|
||||||
|
*/
|
||||||
|
static void init(Context context, Handler handler) {
|
||||||
|
if (context == null) {
|
||||||
|
throw new IllegalArgumentException("Invalid null context");
|
||||||
|
}
|
||||||
|
sContext = context;
|
||||||
|
sWindowOrientationListener = new AudioWindowOrientationListener(context, handler);
|
||||||
|
sWindowOrientationListener.enable();
|
||||||
|
if (!sWindowOrientationListener.canDetectOrientation()) {
|
||||||
|
// cannot use com.android.server.policy.WindowOrientationListener, revert to public
|
||||||
|
// orientation API
|
||||||
|
Log.i(TAG, "Not using WindowOrientationListener, reverting to OrientationListener");
|
||||||
|
sWindowOrientationListener.disable();
|
||||||
|
sWindowOrientationListener = null;
|
||||||
|
sOrientationListener = new AudioOrientationListener(context);
|
||||||
|
sOrientationListener.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enable() {
|
||||||
|
if (sWindowOrientationListener != null) {
|
||||||
|
sWindowOrientationListener.enable();
|
||||||
|
} else {
|
||||||
|
sOrientationListener.enable();
|
||||||
|
}
|
||||||
|
updateOrientation();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void disable() {
|
||||||
|
if (sWindowOrientationListener != null) {
|
||||||
|
sWindowOrientationListener.disable();
|
||||||
|
} else {
|
||||||
|
sOrientationListener.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query current display rotation and publish the change if any.
|
||||||
|
*/
|
||||||
|
static void updateOrientation() {
|
||||||
|
// Even though we're responding to device orientation events,
|
||||||
|
// use display rotation so audio stays in sync with video/dialogs
|
||||||
|
int newRotation = ((WindowManager) sContext.getSystemService(
|
||||||
|
Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
|
||||||
|
synchronized(sRotationLock) {
|
||||||
|
if (newRotation != sDeviceRotation) {
|
||||||
|
sDeviceRotation = newRotation;
|
||||||
|
publishRotation(sDeviceRotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void publishRotation(int rotation) {
|
||||||
|
Log.v(TAG, "publishing device rotation =" + rotation + " (x90deg)");
|
||||||
|
switch (rotation) {
|
||||||
|
case Surface.ROTATION_0:
|
||||||
|
AudioSystem.setParameters("rotation=0");
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_90:
|
||||||
|
AudioSystem.setParameters("rotation=90");
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_180:
|
||||||
|
AudioSystem.setParameters("rotation=180");
|
||||||
|
break;
|
||||||
|
case Surface.ROTATION_270:
|
||||||
|
AudioSystem.setParameters("rotation=270");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Log.e(TAG, "Unknown device rotation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses android.view.OrientationEventListener
|
||||||
|
*/
|
||||||
|
final static class AudioOrientationListener extends OrientationEventListener {
|
||||||
|
AudioOrientationListener(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOrientationChanged(int orientation) {
|
||||||
|
updateOrientation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses com.android.server.policy.WindowOrientationListener
|
||||||
|
*/
|
||||||
|
final static class AudioWindowOrientationListener extends WindowOrientationListener {
|
||||||
|
private static RotationCheckThread sRotationCheckThread;
|
||||||
|
|
||||||
|
AudioWindowOrientationListener(Context context, Handler handler) {
|
||||||
|
super(context, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onProposedRotationChanged(int rotation) {
|
||||||
|
updateOrientation();
|
||||||
|
if (sRotationCheckThread != null) {
|
||||||
|
sRotationCheckThread.endCheck();
|
||||||
|
}
|
||||||
|
sRotationCheckThread = new RotationCheckThread();
|
||||||
|
sRotationCheckThread.beginCheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When com.android.server.policy.WindowOrientationListener report an orientation change,
|
||||||
|
* the UI may not have rotated yet. This thread polls with gradually increasing delays
|
||||||
|
* the new orientation.
|
||||||
|
*/
|
||||||
|
final static class RotationCheckThread extends Thread {
|
||||||
|
// how long to wait between each rotation check
|
||||||
|
private final int[] WAIT_TIMES_MS = { 10, 20, 50, 100, 100, 200, 200, 500 };
|
||||||
|
private int mWaitCounter;
|
||||||
|
private final Object mCounterLock = new Object();
|
||||||
|
|
||||||
|
RotationCheckThread() {
|
||||||
|
super("RotationCheck");
|
||||||
|
}
|
||||||
|
|
||||||
|
void beginCheck() {
|
||||||
|
synchronized(mCounterLock) {
|
||||||
|
mWaitCounter = 0;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
start();
|
||||||
|
} catch (IllegalStateException e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
void endCheck() {
|
||||||
|
synchronized(mCounterLock) {
|
||||||
|
mWaitCounter = WAIT_TIMES_MS.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
int newRotation;
|
||||||
|
while (mWaitCounter < WAIT_TIMES_MS.length) {
|
||||||
|
updateOrientation();
|
||||||
|
int waitTimeMs;
|
||||||
|
synchronized(mCounterLock) {
|
||||||
|
waitTimeMs = WAIT_TIMES_MS[mWaitCounter];
|
||||||
|
mWaitCounter++;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
sleep(waitTimeMs);
|
||||||
|
} catch (InterruptedException e) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user