124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi/*
224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * Copyright (C) 2015 The Android Open Source Project
324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi *
424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * Licensed under the Apache License, Version 2.0 (the "License");
524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * you may not use this file except in compliance with the License.
624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * You may obtain a copy of the License at
724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi *
824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi *      http://www.apache.org/licenses/LICENSE-2.0
924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi *
1024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * Unless required by applicable law or agreed to in writing, software
1124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * distributed under the License is distributed on an "AS IS" BASIS,
1224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * See the License for the specific language governing permissions and
1424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * limitations under the License.
1524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi */
1624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
1724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivipackage com.android.server.audio;
1824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
1924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport android.content.Context;
2024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport android.media.AudioSystem;
2124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport android.os.Handler;
2224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport android.util.Log;
2324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport android.view.OrientationEventListener;
2424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport android.view.Surface;
2524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport android.view.WindowManager;
2624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
2724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviimport com.android.server.policy.WindowOrientationListener;
2824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
2924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi/**
3024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * Class to handle device rotation events for AudioService, and forward device rotation
3124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * to the audio HALs through AudioSystem.
3224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi *
3324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * The role of this class is to monitor device orientation changes, and upon rotation,
3424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * verify the UI orientation. In case of a change, send the new orientation, in increments
3524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * of 90deg, through AudioSystem.
3624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi *
3724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * Note that even though we're responding to device orientation events, we always
3824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * query the display rotation so audio stays in sync with video/dialogs. This is
3924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE.
4024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi */
4124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Triviclass RotationHelper {
4224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
4324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    private static final String TAG = "AudioService.RotationHelper";
4424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
4524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    private static AudioOrientationListener sOrientationListener;
4624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    private static AudioWindowOrientationListener sWindowOrientationListener;
4724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
4824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    private static final Object sRotationLock = new Object();
4924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock
5024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
5124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    private static Context sContext;
5224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
5324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    /**
5424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * post conditions:
5524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * - (sWindowOrientationListener != null) xor (sOrientationListener != null)
5624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * - sWindowOrientationListener xor sOrientationListener is enabled
5724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * - sContext != null
5824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     */
5924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    static void init(Context context, Handler handler) {
6024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        if (context == null) {
6124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            throw new IllegalArgumentException("Invalid null context");
6224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
6324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        sContext = context;
6424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        sWindowOrientationListener = new AudioWindowOrientationListener(context, handler);
6524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        sWindowOrientationListener.enable();
6624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        if (!sWindowOrientationListener.canDetectOrientation()) {
6724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            // cannot use com.android.server.policy.WindowOrientationListener, revert to public
6824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            // orientation API
6924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            Log.i(TAG, "Not using WindowOrientationListener, reverting to OrientationListener");
7024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sWindowOrientationListener.disable();
7124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sWindowOrientationListener = null;
7224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sOrientationListener = new AudioOrientationListener(context);
7324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sOrientationListener.enable();
7424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
7524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
7624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
7724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    static void enable() {
7824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        if (sWindowOrientationListener != null) {
7924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sWindowOrientationListener.enable();
8024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        } else {
8124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sOrientationListener.enable();
8224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
8324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        updateOrientation();
8424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
8524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
8624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    static void disable() {
8724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        if (sWindowOrientationListener != null) {
8824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sWindowOrientationListener.disable();
8924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        } else {
9024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sOrientationListener.disable();
9124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
9224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
9324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
9424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    /**
9524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * Query current display rotation and publish the change if any.
9624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     */
9724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    static void updateOrientation() {
9824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        // Even though we're responding to device orientation events,
9924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        // use display rotation so audio stays in sync with video/dialogs
10024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        int newRotation = ((WindowManager) sContext.getSystemService(
10124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
10224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        synchronized(sRotationLock) {
10324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            if (newRotation != sDeviceRotation) {
10424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                sDeviceRotation = newRotation;
10524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                publishRotation(sDeviceRotation);
10624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            }
10724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
10824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
10924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
11024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    private static void publishRotation(int rotation) {
11124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        Log.v(TAG, "publishing device rotation =" + rotation + " (x90deg)");
11224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        switch (rotation) {
11324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            case Surface.ROTATION_0:
11424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                AudioSystem.setParameters("rotation=0");
11524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                break;
11624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            case Surface.ROTATION_90:
11724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                AudioSystem.setParameters("rotation=90");
11824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                break;
11924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            case Surface.ROTATION_180:
12024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                AudioSystem.setParameters("rotation=180");
12124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                break;
12224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            case Surface.ROTATION_270:
12324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                AudioSystem.setParameters("rotation=270");
12424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                break;
12524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            default:
12624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                Log.e(TAG, "Unknown device rotation");
12724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
12824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
12924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
13024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    /**
13124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * Uses android.view.OrientationEventListener
13224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     */
13324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    final static class AudioOrientationListener extends OrientationEventListener {
13424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        AudioOrientationListener(Context context) {
13524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            super(context);
13624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
13724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
13824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        @Override
13924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        public void onOrientationChanged(int orientation) {
14024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            updateOrientation();
14124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
14224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
14324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
14424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    /**
14524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * Uses com.android.server.policy.WindowOrientationListener
14624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     */
14724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    final static class AudioWindowOrientationListener extends WindowOrientationListener {
14824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        private static RotationCheckThread sRotationCheckThread;
14924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
15024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        AudioWindowOrientationListener(Context context, Handler handler) {
15124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            super(context, handler);
15224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
15324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
15424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        public void onProposedRotationChanged(int rotation) {
15524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            updateOrientation();
15624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            if (sRotationCheckThread != null) {
15724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                sRotationCheckThread.endCheck();
15824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            }
15924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sRotationCheckThread = new RotationCheckThread();
16024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            sRotationCheckThread.beginCheck();
16124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
16224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
16324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
16424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    /**
16524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * When com.android.server.policy.WindowOrientationListener report an orientation change,
16624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * the UI may not have rotated yet. This thread polls with gradually increasing delays
16724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     * the new orientation.
16824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi     */
16924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    final static class RotationCheckThread extends Thread {
17024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        // how long to wait between each rotation check
17124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        private final int[] WAIT_TIMES_MS = { 10, 20, 50, 100, 100, 200, 200, 500 };
17224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        private int mWaitCounter;
17324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        private final Object mCounterLock = new Object();
17424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
17524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        RotationCheckThread() {
17624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            super("RotationCheck");
17724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
17824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
17924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        void beginCheck() {
18024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            synchronized(mCounterLock) {
18124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                mWaitCounter = 0;
18224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            }
18324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            try {
18424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                start();
18524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            } catch (IllegalStateException e) { }
18624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
18724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
18824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        void endCheck() {
18924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            synchronized(mCounterLock) {
19024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                mWaitCounter = WAIT_TIMES_MS.length;
19124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            }
19224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
19324806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi
19424806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        public void run() {
19524806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            while (mWaitCounter < WAIT_TIMES_MS.length) {
19624806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                int waitTimeMs;
19724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                synchronized(mCounterLock) {
1980155856a1d1cb272d83b6175953dd7151fe65daaJean-Michel Trivi                    waitTimeMs = mWaitCounter < WAIT_TIMES_MS.length ?
1990155856a1d1cb272d83b6175953dd7151fe65daaJean-Michel Trivi                            WAIT_TIMES_MS[mWaitCounter] : 0;
20024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                    mWaitCounter++;
20124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                }
20224806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                try {
2030155856a1d1cb272d83b6175953dd7151fe65daaJean-Michel Trivi                    if (waitTimeMs > 0) {
2040155856a1d1cb272d83b6175953dd7151fe65daaJean-Michel Trivi                        sleep(waitTimeMs);
2050155856a1d1cb272d83b6175953dd7151fe65daaJean-Michel Trivi                        updateOrientation();
2060155856a1d1cb272d83b6175953dd7151fe65daaJean-Michel Trivi                    }
20724806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi                } catch (InterruptedException e) { }
20824806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi            }
20924806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi        }
21024806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi    }
21124806db8f6f523542510097ce0af4a32beeda83bJean-Michel Trivi}