Merge "CEC: Add listener for HDMI CEC volume control feature" into rvc-dev

This commit is contained in:
Nick Chalko
2020-06-04 19:03:05 +00:00
committed by Android (Google) Code Review
6 changed files with 287 additions and 0 deletions

View File

@@ -18,6 +18,7 @@ package android.hardware.hdmi;
import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +31,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.ArrayMap;
@@ -40,6 +42,7 @@ import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
* The {@link HdmiControlManager} class is used to send HDMI control messages
@@ -817,6 +820,24 @@ public final class HdmiControlManager {
private final ArrayMap<HdmiControlStatusChangeListener, IHdmiControlStatusChangeListener>
mHdmiControlStatusChangeListeners = new ArrayMap<>();
/**
* Listener used to get the status of the HDMI CEC volume control feature (enabled/disabled).
* @hide
*/
public interface HdmiCecVolumeControlFeatureListener {
/**
* Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
*
* @param enabled status of HDMI CEC volume control feature
* @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
**/
void onHdmiCecVolumeControlFeature(boolean enabled);
}
private final ArrayMap<HdmiCecVolumeControlFeatureListener,
IHdmiCecVolumeControlFeatureListener>
mHdmiCecVolumeControlFeatureListeners = new ArrayMap<>();
/**
* Listener used to get vendor-specific commands.
*/
@@ -979,4 +1000,76 @@ public final class HdmiControlManager {
};
}
/**
* Adds a listener to get informed of changes to the state of the HDMI CEC volume control
* feature.
*
* Upon adding a listener, the current state of the HDMI CEC volume control feature will be
* sent immediately.
*
* <p>To stop getting the notification,
* use {@link #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener)}.
*
* @param listener {@link HdmiCecVolumeControlFeatureListener} instance
* @hide
* @see #removeHdmiCecVolumeControlFeatureListener(HdmiCecVolumeControlFeatureListener)
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void addHdmiCecVolumeControlFeatureListener(@NonNull @CallbackExecutor Executor executor,
@NonNull HdmiCecVolumeControlFeatureListener listener) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
return;
}
if (mHdmiCecVolumeControlFeatureListeners.containsKey(listener)) {
Log.e(TAG, "listener is already registered");
return;
}
IHdmiCecVolumeControlFeatureListener wrappedListener =
createHdmiCecVolumeControlFeatureListenerWrapper(executor, listener);
mHdmiCecVolumeControlFeatureListeners.put(listener, wrappedListener);
try {
mService.addHdmiCecVolumeControlFeatureListener(wrappedListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes a listener to stop getting informed of changes to the state of the HDMI CEC volume
* control feature.
*
* @param listener {@link HdmiCecVolumeControlFeatureListener} instance to be removed
* @hide
*/
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void removeHdmiCecVolumeControlFeatureListener(
HdmiCecVolumeControlFeatureListener listener) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
return;
}
IHdmiCecVolumeControlFeatureListener wrappedListener =
mHdmiCecVolumeControlFeatureListeners.remove(listener);
if (wrappedListener == null) {
Log.e(TAG, "tried to remove not-registered listener");
return;
}
try {
mService.removeHdmiCecVolumeControlFeatureListener(wrappedListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private IHdmiCecVolumeControlFeatureListener createHdmiCecVolumeControlFeatureListenerWrapper(
Executor executor, final HdmiCecVolumeControlFeatureListener listener) {
return new android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener.Stub() {
@Override
public void onHdmiCecVolumeControlFeature(boolean enabled) {
Binder.clearCallingIdentity();
executor.execute(() -> listener.onHdmiCecVolumeControlFeature(enabled));
}
};
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2020 The Android Open Source 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 android.hardware.hdmi;
/**
* Listener used to get the status of the HDMI CEC volume control feature (enabled/disabled).
* @hide
*/
oneway interface IHdmiCecVolumeControlFeatureListener {
/**
* Called when the HDMI Control (CEC) volume control feature is enabled/disabled.
*
* @param enabled status of HDMI CEC volume control feature
* @see {@link HdmiControlManager#setHdmiCecVolumeControlEnabled(boolean)} ()}
**/
void onHdmiCecVolumeControlFeature(boolean enabled);
}

View File

@@ -18,6 +18,7 @@ package android.hardware.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiDeviceEventListener;
@@ -44,6 +45,8 @@ interface IHdmiControlService {
void queryDisplayStatus(IHdmiControlCallback callback);
void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
void addHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
void removeHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
void addHotplugEventListener(IHdmiHotplugEventListener listener);
void removeHotplugEventListener(IHdmiHotplugEventListener listener);
void addDeviceEventListener(IHdmiDeviceEventListener listener);

View File

@@ -363,6 +363,16 @@ public class HdmiAudioSystemClientTest {
public boolean isHdmiCecVolumeControlEnabled() {
return true;
}
@Override
public void addHdmiCecVolumeControlFeatureListener(
IHdmiCecVolumeControlFeatureListener listener) {
}
@Override
public void removeHdmiCecVolumeControlFeatureListener(
IHdmiCecVolumeControlFeatureListener listener) {
}
}
}

View File

@@ -40,6 +40,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
@@ -63,6 +64,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
@@ -268,6 +270,11 @@ public class HdmiControlService extends SystemService {
private final ArrayList<HdmiControlStatusChangeListenerRecord>
mHdmiControlStatusChangeListenerRecords = new ArrayList<>();
// List of records for HDMI control volume control status change listener for death monitoring.
@GuardedBy("mLock")
private final RemoteCallbackList<IHdmiCecVolumeControlFeatureListener>
mHdmiCecVolumeControlFeatureListenerRecords = new RemoteCallbackList<>();
// List of records for hotplug event listener to handle the the caller killed in action.
@GuardedBy("mLock")
private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
@@ -1813,6 +1820,21 @@ public class HdmiControlService extends SystemService {
HdmiControlService.this.removeHdmiControlStatusChangeListener(listener);
}
@Override
public void addHdmiCecVolumeControlFeatureListener(
final IHdmiCecVolumeControlFeatureListener listener) {
enforceAccessPermission();
HdmiControlService.this.addHdmiCecVolumeControlFeatureListener(listener);
}
@Override
public void removeHdmiCecVolumeControlFeatureListener(
final IHdmiCecVolumeControlFeatureListener listener) {
enforceAccessPermission();
HdmiControlService.this.removeHdmiControlVolumeControlStatusChangeListener(listener);
}
@Override
public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
enforceAccessPermission();
@@ -2409,6 +2431,33 @@ public class HdmiControlService extends SystemService {
}
}
@VisibleForTesting
void addHdmiCecVolumeControlFeatureListener(
final IHdmiCecVolumeControlFeatureListener listener) {
mHdmiCecVolumeControlFeatureListenerRecords.register(listener);
runOnServiceThread(new Runnable() {
@Override
public void run() {
// Return the current status of mHdmiCecVolumeControlEnabled;
synchronized (mLock) {
try {
listener.onHdmiCecVolumeControlFeature(mHdmiCecVolumeControlEnabled);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to report HdmiControlVolumeControlStatusChange: "
+ mHdmiCecVolumeControlEnabled, e);
}
}
}
});
}
@VisibleForTesting
void removeHdmiControlVolumeControlStatusChangeListener(
final IHdmiCecVolumeControlFeatureListener listener) {
mHdmiCecVolumeControlFeatureListenerRecords.unregister(listener);
}
private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
try {
@@ -2682,6 +2731,19 @@ public class HdmiControlService extends SystemService {
}
}
private void announceHdmiCecVolumeControlFeatureChange(boolean isEnabled) {
assertRunOnServiceThread();
mHdmiCecVolumeControlFeatureListenerRecords.broadcast(listener -> {
try {
listener.onHdmiCecVolumeControlFeature(isEnabled);
} catch (RemoteException e) {
Slog.e(TAG,
"Failed to report HdmiControlVolumeControlStatusChange: "
+ isEnabled);
}
});
}
public HdmiCecLocalDeviceTv tv() {
return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
}
@@ -3026,6 +3088,7 @@ public class HdmiControlService extends SystemService {
isHdmiCecVolumeControlEnabled);
}
}
announceHdmiCecVolumeControlFeatureChange(isHdmiCecVolumeControlEnabled);
}
boolean isHdmiCecVolumeControlEnabled() {

View File

@@ -33,6 +33,7 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.Looper;
@@ -261,4 +262,89 @@ public class HdmiControlServiceTest {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
assertThat(mHdmiControlService.isHdmiCecVolumeControlEnabled()).isTrue();
}
@Test
public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_enabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
mTestLooper.dispatchAll();
assertThat(callback.mCallbackReceived).isTrue();
assertThat(callback.mVolumeControlEnabled).isTrue();
}
@Test
public void addHdmiCecVolumeControlFeatureListener_emitsCurrentState_disabled() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
mTestLooper.dispatchAll();
assertThat(callback.mCallbackReceived).isTrue();
assertThat(callback.mVolumeControlEnabled).isFalse();
}
@Test
public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mTestLooper.dispatchAll();
assertThat(callback.mCallbackReceived).isTrue();
assertThat(callback.mVolumeControlEnabled).isTrue();
}
@Test
public void addHdmiCecVolumeControlFeatureListener_honorsUnregistration() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
VolumeControlFeatureCallback callback = new VolumeControlFeatureCallback();
mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback);
mTestLooper.dispatchAll();
mHdmiControlService.removeHdmiControlVolumeControlStatusChangeListener(callback);
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mTestLooper.dispatchAll();
assertThat(callback.mCallbackReceived).isTrue();
assertThat(callback.mVolumeControlEnabled).isFalse();
}
@Test
public void addHdmiCecVolumeControlFeatureListener_notifiesStateUpdate_multiple() {
mHdmiControlService.setHdmiCecVolumeControlEnabled(false);
VolumeControlFeatureCallback callback1 = new VolumeControlFeatureCallback();
VolumeControlFeatureCallback callback2 = new VolumeControlFeatureCallback();
mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback1);
mHdmiControlService.addHdmiCecVolumeControlFeatureListener(callback2);
mHdmiControlService.setHdmiCecVolumeControlEnabled(true);
mTestLooper.dispatchAll();
assertThat(callback1.mCallbackReceived).isTrue();
assertThat(callback2.mCallbackReceived).isTrue();
assertThat(callback1.mVolumeControlEnabled).isTrue();
assertThat(callback2.mVolumeControlEnabled).isTrue();
}
private static class VolumeControlFeatureCallback extends
IHdmiCecVolumeControlFeatureListener.Stub {
boolean mCallbackReceived = false;
boolean mVolumeControlEnabled = false;
@Override
public void onHdmiCecVolumeControlFeature(boolean enabled) throws RemoteException {
this.mCallbackReceived = true;
this.mVolumeControlEnabled = enabled;
}
}
}