CEC: Add listener for HDMI CEC volume control feature
Adds a listener to receive updates to the state of the HDMI CEC volume control features. Interested parties can register and unregister to get notified about state updates which are sent on every change to the value. Test: atest HdmiControlServiceTest Bug: 152018314 Change-Id: I342d748114bae99b3c3f236502d73bfeac9e9ac5 Merged-In: I342d748114bae99b3c3f236502d73bfeac9e9ac5
This commit is contained in:
committed by
Nick Chalko
parent
afbd12c4ed
commit
7ddbb62ccd
@@ -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));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -363,6 +363,16 @@ public class HdmiAudioSystemClientTest {
|
||||
public boolean isHdmiCecVolumeControlEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addHdmiCecVolumeControlFeatureListener(
|
||||
IHdmiCecVolumeControlFeatureListener listener) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeHdmiCecVolumeControlFeatureListener(
|
||||
IHdmiCecVolumeControlFeatureListener listener) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -3027,6 +3089,7 @@ public class HdmiControlService extends SystemService {
|
||||
isHdmiCecVolumeControlEnabled);
|
||||
}
|
||||
}
|
||||
announceHdmiCecVolumeControlFeatureChange(isHdmiCecVolumeControlEnabled);
|
||||
}
|
||||
|
||||
boolean isHdmiCecVolumeControlEnabled() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user