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}