blob: 359cc360bbc78c3e75fe4f096dab38d728b4007c [file] [log] [blame]
Jean-Michel Trivi24806db2015-10-01 15:00:59 -07001/*
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
17package com.android.server.audio;
18
19import android.content.Context;
20import android.media.AudioSystem;
21import android.os.Handler;
22import android.util.Log;
23import android.view.OrientationEventListener;
24import android.view.Surface;
25import android.view.WindowManager;
26
27import 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 */
41class 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 */
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
77 static void enable() {
78 if (sWindowOrientationListener != null) {
79 sWindowOrientationListener.enable();
80 } else {
81 sOrientationListener.enable();
82 }
83 updateOrientation();
84 }
85
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 */
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
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 {
134 AudioOrientationListener(Context context) {
135 super(context);
136 }
137
138 @Override
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
150 AudioWindowOrientationListener(Context context, Handler handler) {
151 super(context, handler);
152 }
153
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
175 RotationCheckThread() {
176 super("RotationCheck");
177 }
178
179 void beginCheck() {
180 synchronized(mCounterLock) {
181 mWaitCounter = 0;
182 }
183 try {
184 start();
185 } catch (IllegalStateException e) { }
186 }
187
188 void endCheck() {
189 synchronized(mCounterLock) {
190 mWaitCounter = WAIT_TIMES_MS.length;
191 }
192 }
193
194 public void run() {
Jean-Michel Trivi24806db2015-10-01 15:00:59 -0700195 while (mWaitCounter < WAIT_TIMES_MS.length) {
Jean-Michel Trivi24806db2015-10-01 15:00:59 -0700196 int waitTimeMs;
197 synchronized(mCounterLock) {
Jean-Michel Trivi01558562015-10-05 15:26:21 -0700198 waitTimeMs = mWaitCounter < WAIT_TIMES_MS.length ?
199 WAIT_TIMES_MS[mWaitCounter] : 0;
Jean-Michel Trivi24806db2015-10-01 15:00:59 -0700200 mWaitCounter++;
201 }
202 try {
Jean-Michel Trivi01558562015-10-05 15:26:21 -0700203 if (waitTimeMs > 0) {
204 sleep(waitTimeMs);
205 updateOrientation();
206 }
Jean-Michel Trivi24806db2015-10-01 15:00:59 -0700207 } catch (InterruptedException e) { }
208 }
209 }
210 }
211}