diff --git a/AudioRotationMonitor/Android.mk b/AudioRotationMonitor/Android.mk new file mode 100644 index 0000000..992c9d9 --- /dev/null +++ b/AudioRotationMonitor/Android.mk @@ -0,0 +1,17 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := AudioRotationMonitor +LOCAL_CERTIFICATE := platform +LOCAL_PRIVATE_PLATFORM_APIS := true + +LOCAL_PROGUARD_FLAG_FILES := proguard.flags + +include $(BUILD_PACKAGE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/AudioRotationMonitor/AndroidManifest.xml b/AudioRotationMonitor/AndroidManifest.xml new file mode 100644 index 0000000..e179d13 --- /dev/null +++ b/AudioRotationMonitor/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/AudioRotationMonitor/proguard.flags b/AudioRotationMonitor/proguard.flags new file mode 100644 index 0000000..bc0051c --- /dev/null +++ b/AudioRotationMonitor/proguard.flags @@ -0,0 +1,3 @@ +-keep class org.lineageos.audiorotationmonitor.* { + *; +} diff --git a/AudioRotationMonitor/service/Android.mk b/AudioRotationMonitor/service/Android.mk new file mode 100644 index 0000000..6b21877 --- /dev/null +++ b/AudioRotationMonitor/service/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := set-audio-rotation +LOCAL_MODULE_TAGS := optional +LOCAL_INIT_RC := set-audio-rotation.rc +LOCAL_SRC_FILES := \ + set-audio-rotation.cpp +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libtinyalsa + +include $(BUILD_EXECUTABLE) diff --git a/AudioRotationMonitor/service/set-audio-rotation.cpp b/AudioRotationMonitor/service/set-audio-rotation.cpp new file mode 100644 index 0000000..69f9b09 --- /dev/null +++ b/AudioRotationMonitor/service/set-audio-rotation.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define LOG_TAG "set-audio-rotation" + +#include +#include + +constexpr int SLOT_POSITIONS_0[] = { 0, 1, 0, 1 }; +constexpr int SLOT_POSITIONS_90[] = { 1, 1, 0, 0 }; +constexpr int SLOT_POSITIONS_180[] = { 1, 0, 1, 0 }; +constexpr int SLOT_POSITIONS_270[] = { 0, 0, 1, 1 }; + +void setMixerValueByName(mixer *mixer, const char *name, int value) { + const auto ctl = mixer_get_ctl_by_name(mixer, name); + + if (ctl == nullptr) { + LOG(ERROR) << "Failed to find mixer ctl for " << name; + return; + } + + if (mixer_ctl_set_value(ctl, 0, value) < 0) { + LOG(ERROR) << "Failed to set ctl value " << value << " for " << name; + return; + } +} + +void setSlotPositions(const int *values) { + const auto mixer = mixer_open(0); + + if (mixer == nullptr) { + LOG(ERROR) << "Failed to open mixer"; + return; + } + + setMixerValueByName(mixer, "ExtSPK LL TDM_ADC_SEL", values[0]); + setMixerValueByName(mixer, "ExtSPK LR TDM_ADC_SEL", values[1]); + setMixerValueByName(mixer, "ExtSPK UL TDM_ADC_SEL", values[2]); + setMixerValueByName(mixer, "ExtSPK UR TDM_ADC_SEL", values[3]); + + setMixerValueByName(mixer, "ExtSPK LL TDM_DAC_SEL", values[0]); + setMixerValueByName(mixer, "ExtSPK LR TDM_DAC_SEL", values[1]); + setMixerValueByName(mixer, "ExtSPK UL TDM_DAC_SEL", values[2]); + setMixerValueByName(mixer, "ExtSPK UR TDM_DAC_SEL", values[3]); + + mixer_close(mixer); +}; + +int main(int argc, char **argv) { + if (argc != 2) { + return -1; + } + + if (strcmp(argv[1], "0") == 0) { + setSlotPositions(SLOT_POSITIONS_0); + } else if (strcmp(argv[1], "1") == 0) { + setSlotPositions(SLOT_POSITIONS_90); + } else if (strcmp(argv[1], "2") == 0) { + setSlotPositions(SLOT_POSITIONS_180); + } else if (strcmp(argv[1], "3") == 0) { + setSlotPositions(SLOT_POSITIONS_270); + } else { + return -1; + } + + return 0; +} diff --git a/AudioRotationMonitor/service/set-audio-rotation.rc b/AudioRotationMonitor/service/set-audio-rotation.rc new file mode 100644 index 0000000..5dd4329 --- /dev/null +++ b/AudioRotationMonitor/service/set-audio-rotation.rc @@ -0,0 +1,2 @@ +on property:sys.audio.rotation=* + exec - root audio -- /system/bin/set-audio-rotation ${sys.audio.rotation} diff --git a/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/BootCompletedReceiver.java b/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/BootCompletedReceiver.java new file mode 100644 index 0000000..3ef3d76 --- /dev/null +++ b/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/BootCompletedReceiver.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.audiorotationmonitor; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class BootCompletedReceiver extends BroadcastReceiver { + private static final String TAG = "AudioRotationMonitor"; + + @Override + public void onReceive(final Context context, Intent intent) { + Log.d(TAG, "Starting"); + context.startService(new Intent(context, DisplayListenerService.class)); + } +} diff --git a/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/DisplayListener.java b/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/DisplayListener.java new file mode 100644 index 0000000..d4d28d9 --- /dev/null +++ b/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/DisplayListener.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.audiorotationmonitor; + +import android.content.Context; +import android.hardware.display.DisplayManager; +import android.os.Handler; +import android.os.SystemProperties; +import android.util.Log; +import android.view.Surface; +import android.view.WindowManager; + +public class DisplayListener implements DisplayManager.DisplayListener { + private static final boolean DEBUG = true; + private static final String TAG = "DisplayListener"; + + private static final String AUDIO_ROTATION_PROP = "sys.audio.rotation"; + + private Context mContext; + private Handler mHandler; + + private DisplayManager mDisplayManager; + private WindowManager mWindowManager; + + private final Object mRotationLock = new Object(); + private int mDeviceRotation = Surface.ROTATION_0; + + public DisplayListener(Context context) { + mContext = context; + mHandler = new Handler(); + + mDisplayManager = mContext.getSystemService(DisplayManager.class); + mWindowManager = mContext.getSystemService(WindowManager.class); + } + + @Override + public void onDisplayAdded(int displayId) { + if (DEBUG) Log.d(TAG, "onDisplayAdded"); + } + + @Override + public void onDisplayRemoved(int displayId) { + if (DEBUG) Log.d(TAG, "onDisplayRemoved"); + } + + @Override + public void onDisplayChanged(int displayId) { + if (DEBUG) Log.d(TAG, "onDisplayChanged"); + updateOrientation(); + } + + private void updateOrientation() { + // Even though we're responding to device orientation events, + // use display rotation so audio stays in sync with video/dialogs + int newRotation = mWindowManager.getDefaultDisplay().getRotation(); + + synchronized (mRotationLock) { + if (newRotation != mDeviceRotation) { + mDeviceRotation = newRotation; + SystemProperties.set(AUDIO_ROTATION_PROP, String.valueOf(mDeviceRotation)); + } + } + } + + public void enable() { + if (DEBUG) Log.d(TAG, "Enabling"); + mDisplayManager.registerDisplayListener(this, mHandler); + updateOrientation(); + } + + public void disable() { + if (DEBUG) Log.d(TAG, "Disabling"); + mDisplayManager.unregisterDisplayListener(this); + } +} diff --git a/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/DisplayListenerService.java b/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/DisplayListenerService.java new file mode 100644 index 0000000..0897db7 --- /dev/null +++ b/AudioRotationMonitor/src/org/lineageos/audiorotationmonitor/DisplayListenerService.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2019 The LineageOS Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.lineageos.audiorotationmonitor; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.util.Log; + +public class DisplayListenerService extends Service { + private static final String TAG = "DisplayListenerService"; + private static final boolean DEBUG = true; + + private DisplayListener mDisplayListener; + + @Override + public void onCreate() { + if (DEBUG) Log.d(TAG, "Creating service"); + mDisplayListener = new DisplayListener(this); + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if (DEBUG) Log.d(TAG, "Starting service"); + mDisplayListener.enable(); + return START_STICKY; + } + + @Override + public void onDestroy() { + if (DEBUG) Log.d(TAG, "Destroying service"); + mDisplayListener.disable(); + super.onDestroy(); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/gts3l.mk b/gts3l.mk index f53475b..0d1445f 100755 --- a/gts3l.mk +++ b/gts3l.mk @@ -101,7 +101,9 @@ PRODUCT_PACKAGES += \ libqcomvoiceprocessing \ libvolumelistener \ tinymix \ - libaudioprimary_shim + libaudioprimary_shim \ + AudioRotationMonitor \ + set-audio-rotation PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/audio/audio/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio/audio_policy_configuration.xml \ diff --git a/sepolicy/file_contexts b/sepolicy/file_contexts index 1753b9a..203118b 100644 --- a/sepolicy/file_contexts +++ b/sepolicy/file_contexts @@ -17,6 +17,7 @@ # Binaries /(vendor|system/vendor)/bin/hw/macloader u:object_r:macloader_exec:s0 /(vendor|system/vendor)/bin/secril_config_svc u:object_r:secril_config_svc_exec:s0 +/system/bin/set-audio-rotation u:object_r:set-audio-rotation_exec:s0 # Data files /data/camera(/.*)? u:object_r:camera_socket:s0 diff --git a/sepolicy/property_contexts b/sepolicy/property_contexts index fb644a9..ae769cc 100644 --- a/sepolicy/property_contexts +++ b/sepolicy/property_contexts @@ -32,6 +32,7 @@ ro.vendor.camera.wrapper.hal3TrebleMinorVersion u:object_r:sec_camera_prop:s0 ro.vendor.multisim. u:object_r:vendor_radio_prop:s0 ro.vendor.radio. u:object_r:vendor_radio_prop:s0 service.camera. u:object_r:sec_camera_prop:s0 +sys.audio.rotation u:object_r:exported_system_prop:s0 sys.cameramode. u:object_r:sec_camera_prop:s0 system.camera.CC. u:object_r:sec_camera_prop:s0 vendor.bluetooth_fw_ver u:object_r:vendor_bluetooth_prop:s0 diff --git a/sepolicy/set-audio-rotation.te b/sepolicy/set-audio-rotation.te new file mode 100644 index 0000000..14410e5 --- /dev/null +++ b/sepolicy/set-audio-rotation.te @@ -0,0 +1,8 @@ +type set-audio-rotation, domain, coredomain; +type set-audio-rotation_exec, exec_type, file_type; + +init_daemon_domain(set-audio-rotation) + +# Allow set-audio-rotation to read and write to audio_device +allow set-audio-rotation audio_device:dir r_dir_perms; +allow set-audio-rotation audio_device:chr_file rw_file_perms;