diff options
author | Jean-Michel Trivi <jmtrivi@google.com> | 2015-10-01 15:00:59 -0700 |
---|---|---|
committer | Jean-Michel Trivi <jmtrivi@google.com> | 2015-10-01 18:40:26 -0700 |
commit | 24806db8f6f523542510097ce0af4a32beeda83b (patch) | |
tree | cfd5737ad7f910e52233e4f36a897192767d71e2 /services | |
parent | 6ffe9f05de81fab8e3cb4031a784e26c2c0acce1 (diff) | |
download | frameworks_base-24806db8f6f523542510097ce0af4a32beeda83b.zip frameworks_base-24806db8f6f523542510097ce0af4a32beeda83b.tar.gz frameworks_base-24806db8f6f523542510097ce0af4a32beeda83b.tar.bz2 |
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
Diffstat (limited to 'services')
-rw-r--r-- | services/core/java/com/android/server/audio/AudioService.java | 60 | ||||
-rw-r--r-- | services/core/java/com/android/server/audio/RotationHelper.java | 209 |
2 files changed, 214 insertions, 55 deletions
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 2c6bafc..152ff30 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -502,7 +502,6 @@ public class AudioService extends IAudioService.Stub { private volatile IRingtonePlayer mRingtonePlayer; private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED; - private int mDeviceRotation = Surface.ROTATION_0; // Request to override default use of A2DP for media. private boolean mBluetoothA2dpEnabled; @@ -546,8 +545,6 @@ public class AudioService extends IAudioService.Stub { // If absolute volume is supported in AVRCP device private boolean mAvrcpAbsVolSupported = false; - private AudioOrientationEventListener mOrientationListener; - private static Long mLastDeviceConnectMsgTime = new Long(0); private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate; @@ -670,15 +667,7 @@ public class AudioService extends IAudioService.Stub { } mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false); if (mMonitorRotation) { - mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)) - .getDefaultDisplay().getRotation(); - Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation); - - mOrientationListener = new AudioOrientationEventListener(mContext); - mOrientationListener.enable(); - - // initialize rotation in AudioSystem - setRotationForAudioSystem(); + RotationHelper.init(mContext, mAudioHandler); } context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); @@ -808,7 +797,7 @@ public class AudioService extends IAudioService.Stub { setOrientationForAudioSystem(); } if (mMonitorRotation) { - setRotationForAudioSystem(); + RotationHelper.updateOrientation(); } synchronized (mBluetoothA2dpEnabledLock) { @@ -1061,25 +1050,6 @@ public class AudioService extends IAudioService.Stub { 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 /////////////////////////////////////////////////////////////////////////// @@ -5069,14 +5039,13 @@ public class AudioService extends IAudioService.Stub { } } else if (action.equals(Intent.ACTION_SCREEN_ON)) { if (mMonitorRotation) { - mOrientationListener.onOrientationChanged(0); //argument is ignored anyway - mOrientationListener.enable(); + RotationHelper.enable(); } AudioSystem.setParameters("screen_state=on"); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { if (mMonitorRotation) { //reduce wakeups (save current) by only listening when display is on - mOrientationListener.disable(); + RotationHelper.disable(); } AudioSystem.setParameters("screen_state=off"); } 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() { switch (mDeviceOrientation) { 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. // Must be called synchronized on mConnectedDevices public void setBluetoothA2dpOnInt(boolean on) { diff --git a/services/core/java/com/android/server/audio/RotationHelper.java b/services/core/java/com/android/server/audio/RotationHelper.java new file mode 100644 index 0000000..f03e6c7 --- /dev/null +++ b/services/core/java/com/android/server/audio/RotationHelper.java @@ -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) { } + } + } + } +}
\ No newline at end of file |