Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 24806db8 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

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
parent 6ffe9f05
Loading
Loading
Loading
Loading
+5 −55
Original line number Diff line number Diff line
@@ -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) {
+209 −0
Original line number Diff line number Diff line
/*
 * 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