1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.audio; 18 19 import android.content.Context; 20 import android.media.AudioSystem; 21 import android.os.Handler; 22 import android.util.Log; 23 import android.view.OrientationEventListener; 24 import android.view.Surface; 25 import android.view.WindowManager; 26 27 import com.android.server.policy.WindowOrientationListener; 28 29 /** 30 * Class to handle device rotation events for AudioService, and forward device rotation 31 * to the audio HALs through AudioSystem. 32 * 33 * The role of this class is to monitor device orientation changes, and upon rotation, 34 * verify the UI orientation. In case of a change, send the new orientation, in increments 35 * of 90deg, through AudioSystem. 36 * 37 * Note that even though we're responding to device orientation events, we always 38 * query the display rotation so audio stays in sync with video/dialogs. This is 39 * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE. 40 */ 41 class RotationHelper { 42 43 private static final String TAG = "AudioService.RotationHelper"; 44 45 private static AudioOrientationListener sOrientationListener; 46 private static AudioWindowOrientationListener sWindowOrientationListener; 47 48 private static final Object sRotationLock = new Object(); 49 private static int sDeviceRotation = Surface.ROTATION_0; // R/W synchronized on sRotationLock 50 51 private static Context sContext; 52 53 /** 54 * post conditions: 55 * - (sWindowOrientationListener != null) xor (sOrientationListener != null) 56 * - sWindowOrientationListener xor sOrientationListener is enabled 57 * - sContext != null 58 */ init(Context context, Handler handler)59 static void init(Context context, Handler handler) { 60 if (context == null) { 61 throw new IllegalArgumentException("Invalid null context"); 62 } 63 sContext = context; 64 sWindowOrientationListener = new AudioWindowOrientationListener(context, handler); 65 sWindowOrientationListener.enable(); 66 if (!sWindowOrientationListener.canDetectOrientation()) { 67 // cannot use com.android.server.policy.WindowOrientationListener, revert to public 68 // orientation API 69 Log.i(TAG, "Not using WindowOrientationListener, reverting to OrientationListener"); 70 sWindowOrientationListener.disable(); 71 sWindowOrientationListener = null; 72 sOrientationListener = new AudioOrientationListener(context); 73 sOrientationListener.enable(); 74 } 75 } 76 enable()77 static void enable() { 78 if (sWindowOrientationListener != null) { 79 sWindowOrientationListener.enable(); 80 } else { 81 sOrientationListener.enable(); 82 } 83 updateOrientation(); 84 } 85 disable()86 static void disable() { 87 if (sWindowOrientationListener != null) { 88 sWindowOrientationListener.disable(); 89 } else { 90 sOrientationListener.disable(); 91 } 92 } 93 94 /** 95 * Query current display rotation and publish the change if any. 96 */ updateOrientation()97 static void updateOrientation() { 98 // Even though we're responding to device orientation events, 99 // use display rotation so audio stays in sync with video/dialogs 100 int newRotation = ((WindowManager) sContext.getSystemService( 101 Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation(); 102 synchronized(sRotationLock) { 103 if (newRotation != sDeviceRotation) { 104 sDeviceRotation = newRotation; 105 publishRotation(sDeviceRotation); 106 } 107 } 108 } 109 publishRotation(int rotation)110 private static void publishRotation(int rotation) { 111 Log.v(TAG, "publishing device rotation =" + rotation + " (x90deg)"); 112 switch (rotation) { 113 case Surface.ROTATION_0: 114 AudioSystem.setParameters("rotation=0"); 115 break; 116 case Surface.ROTATION_90: 117 AudioSystem.setParameters("rotation=90"); 118 break; 119 case Surface.ROTATION_180: 120 AudioSystem.setParameters("rotation=180"); 121 break; 122 case Surface.ROTATION_270: 123 AudioSystem.setParameters("rotation=270"); 124 break; 125 default: 126 Log.e(TAG, "Unknown device rotation"); 127 } 128 } 129 130 /** 131 * Uses android.view.OrientationEventListener 132 */ 133 final static class AudioOrientationListener extends OrientationEventListener { AudioOrientationListener(Context context)134 AudioOrientationListener(Context context) { 135 super(context); 136 } 137 138 @Override onOrientationChanged(int orientation)139 public void onOrientationChanged(int orientation) { 140 updateOrientation(); 141 } 142 } 143 144 /** 145 * Uses com.android.server.policy.WindowOrientationListener 146 */ 147 final static class AudioWindowOrientationListener extends WindowOrientationListener { 148 private static RotationCheckThread sRotationCheckThread; 149 AudioWindowOrientationListener(Context context, Handler handler)150 AudioWindowOrientationListener(Context context, Handler handler) { 151 super(context, handler); 152 } 153 onProposedRotationChanged(int rotation)154 public void onProposedRotationChanged(int rotation) { 155 updateOrientation(); 156 if (sRotationCheckThread != null) { 157 sRotationCheckThread.endCheck(); 158 } 159 sRotationCheckThread = new RotationCheckThread(); 160 sRotationCheckThread.beginCheck(); 161 } 162 } 163 164 /** 165 * When com.android.server.policy.WindowOrientationListener report an orientation change, 166 * the UI may not have rotated yet. This thread polls with gradually increasing delays 167 * the new orientation. 168 */ 169 final static class RotationCheckThread extends Thread { 170 // how long to wait between each rotation check 171 private final int[] WAIT_TIMES_MS = { 10, 20, 50, 100, 100, 200, 200, 500 }; 172 private int mWaitCounter; 173 private final Object mCounterLock = new Object(); 174 RotationCheckThread()175 RotationCheckThread() { 176 super("RotationCheck"); 177 } 178 beginCheck()179 void beginCheck() { 180 synchronized(mCounterLock) { 181 mWaitCounter = 0; 182 } 183 try { 184 start(); 185 } catch (IllegalStateException e) { } 186 } 187 endCheck()188 void endCheck() { 189 synchronized(mCounterLock) { 190 mWaitCounter = WAIT_TIMES_MS.length; 191 } 192 } 193 run()194 public void run() { 195 while (mWaitCounter < WAIT_TIMES_MS.length) { 196 int waitTimeMs; 197 synchronized(mCounterLock) { 198 waitTimeMs = mWaitCounter < WAIT_TIMES_MS.length ? 199 WAIT_TIMES_MS[mWaitCounter] : 0; 200 mWaitCounter++; 201 } 202 try { 203 if (waitTimeMs > 0) { 204 sleep(waitTimeMs); 205 updateOrientation(); 206 } 207 } catch (InterruptedException e) { } 208 } 209 } 210 } 211 }