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.hardware.devicestate.DeviceStateManager; 21 import android.hardware.devicestate.DeviceStateManager.FoldStateListener; 22 import android.hardware.display.DisplayManager; 23 import android.hardware.display.DisplayManagerGlobal; 24 import android.os.Handler; 25 import android.os.HandlerExecutor; 26 import android.util.Log; 27 import android.view.Display; 28 import android.view.Surface; 29 30 import java.util.function.Consumer; 31 32 /** 33 * Class to handle device rotation events for AudioService, and forward device rotation 34 * and folded state to the audio HALs through AudioSystem. 35 * 36 * The role of this class is to monitor device orientation changes, and upon rotation, 37 * verify the UI orientation. In case of a change, send the new orientation, in increments 38 * of 90deg, through AudioSystem. 39 * 40 * Another role of this class is to track device folded state changes. In case of a 41 * change, send the new folded state through AudioSystem. 42 * 43 * Note that even though we're responding to device orientation events, we always 44 * query the display rotation so audio stays in sync with video/dialogs. This is 45 * done with .getDefaultDisplay().getRotation() from WINDOW_SERVICE. 46 * 47 * We also monitor current display ID and audio is able to know which display is active. 48 */ 49 class RotationHelper { 50 51 private static final String TAG = "AudioService.RotationHelper"; 52 53 private static final boolean DEBUG_ROTATION = false; 54 55 private static AudioDisplayListener sDisplayListener; 56 private static FoldStateListener sFoldStateListener; 57 /** callback to send rotation updates to AudioSystem */ 58 private static Consumer<Integer> sRotationCallback; 59 /** callback to send folded state updates to AudioSystem */ 60 private static Consumer<Boolean> sFoldStateCallback; 61 62 private static final Object sRotationLock = new Object(); 63 private static final Object sFoldStateLock = new Object(); 64 private static Integer sRotation = null; // R/W synchronized on sRotationLock 65 private static Boolean sFoldState = null; // R/W synchronized on sFoldStateLock 66 67 private static Context sContext; 68 private static Handler sHandler; 69 70 /** 71 * post conditions: 72 * - sDisplayListener != null 73 * - sContext != null 74 */ init(Context context, Handler handler, Consumer<Integer> rotationCallback, Consumer<Boolean> foldStateCallback)75 static void init(Context context, Handler handler, 76 Consumer<Integer> rotationCallback, Consumer<Boolean> foldStateCallback) { 77 if (context == null) { 78 throw new IllegalArgumentException("Invalid null context"); 79 } 80 sContext = context; 81 sHandler = handler; 82 sDisplayListener = new AudioDisplayListener(); 83 sFoldStateListener = new FoldStateListener(sContext, RotationHelper::updateFoldState); 84 sRotationCallback = rotationCallback; 85 sFoldStateCallback = foldStateCallback; 86 enable(); 87 } 88 enable()89 static void enable() { 90 ((DisplayManager) sContext.getSystemService(Context.DISPLAY_SERVICE)) 91 .registerDisplayListener(sDisplayListener, sHandler); 92 updateOrientation(); 93 94 sContext.getSystemService(DeviceStateManager.class) 95 .registerCallback(new HandlerExecutor(sHandler), sFoldStateListener); 96 } 97 disable()98 static void disable() { 99 ((DisplayManager) sContext.getSystemService(Context.DISPLAY_SERVICE)) 100 .unregisterDisplayListener(sDisplayListener); 101 sContext.getSystemService(DeviceStateManager.class) 102 .unregisterCallback(sFoldStateListener); 103 } 104 105 /** 106 * Query current display rotation and publish the change if any. 107 */ updateOrientation()108 static void updateOrientation() { 109 // Even though we're responding to device orientation events, 110 // use display rotation so audio stays in sync with video/dialogs 111 // TODO(b/148458001): Support multi-display 112 int newRotation = DisplayManagerGlobal.getInstance() 113 .getDisplayInfo(Display.DEFAULT_DISPLAY).rotation; 114 synchronized(sRotationLock) { 115 if (sRotation == null || sRotation != newRotation) { 116 sRotation = newRotation; 117 publishRotation(sRotation); 118 } 119 } 120 } 121 publishRotation(int rotation)122 private static void publishRotation(int rotation) { 123 if (DEBUG_ROTATION) { 124 Log.i(TAG, "publishing device rotation =" + rotation + " (x90deg)"); 125 } 126 int rotationDegrees; 127 switch (rotation) { 128 case Surface.ROTATION_0: 129 rotationDegrees = 0; 130 break; 131 case Surface.ROTATION_90: 132 rotationDegrees = 90; 133 break; 134 case Surface.ROTATION_180: 135 rotationDegrees = 180; 136 break; 137 case Surface.ROTATION_270: 138 rotationDegrees = 270; 139 break; 140 default: 141 Log.e(TAG, "Unknown device rotation"); 142 rotationDegrees = -1; 143 } 144 if (rotationDegrees != -1) { 145 sRotationCallback.accept(rotationDegrees); 146 } 147 } 148 149 /** 150 * publish the change of device folded state if any. 151 */ updateFoldState(boolean foldState)152 static void updateFoldState(boolean foldState) { 153 synchronized (sFoldStateLock) { 154 if (sFoldState == null || sFoldState != foldState) { 155 sFoldState = foldState; 156 sFoldStateCallback.accept(foldState); 157 } 158 } 159 } 160 161 /** 162 * forceUpdate is called when audioserver restarts. 163 */ forceUpdate()164 static void forceUpdate() { 165 synchronized (sRotationLock) { 166 sRotation = null; 167 } 168 updateOrientation(); // We will get at least one orientation update now. 169 synchronized (sFoldStateLock) { 170 if (sFoldState != null) { 171 sFoldStateCallback.accept(sFoldState); 172 } 173 } 174 } 175 176 /** 177 * Uses android.hardware.display.DisplayManager.DisplayListener 178 */ 179 final static class AudioDisplayListener implements DisplayManager.DisplayListener { 180 181 @Override onDisplayAdded(int displayId)182 public void onDisplayAdded(int displayId) { 183 } 184 185 @Override onDisplayRemoved(int displayId)186 public void onDisplayRemoved(int displayId) { 187 } 188 189 @Override onDisplayChanged(int displayId)190 public void onDisplayChanged(int displayId) { 191 if (DEBUG_ROTATION) { 192 Log.i(TAG, "onDisplayChanged diplayId:" + displayId); 193 } 194 updateOrientation(); 195 } 196 } 197 } 198