[CEC Configuration] Add listener system API

Bug: 174731501
Bug: 172637440
Bug: 172905515
Test: atest com.google.android.gts.hdmicec.HdmiControlManagerHostTest
Change-Id: I830f3e312d5cf70fc84e389a12ecd50c2e1bfae6
This commit is contained in:
Michal Olech
2020-12-15 16:47:46 +01:00
parent 8142ddd64d
commit d54dbf9869
7 changed files with 265 additions and 0 deletions

View File

@@ -2741,6 +2741,8 @@ package android.hardware.hdmi {
}
public final class HdmiControlManager {
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHdmiCecEnabledChangeListener(@NonNull android.hardware.hdmi.HdmiControlManager.CecSettingChangeListener);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHdmiCecEnabledChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.CecSettingChangeListener);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void addHotplugEventListener(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public java.util.List<java.lang.Integer> getAllowedCecSettingIntValues(@NonNull String);
@@ -2759,6 +2761,7 @@ package android.hardware.hdmi {
method @NonNull @RequiresPermission(android.Manifest.permission.HDMI_CEC) public java.util.List<java.lang.String> getUserCecSettings();
method public boolean isDeviceConnected(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
method public void powerOffDevice(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHdmiCecEnabledChangeListener(@NonNull android.hardware.hdmi.HdmiControlManager.CecSettingChangeListener);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void removeHotplugEventListener(android.hardware.hdmi.HdmiControlManager.HotplugEventListener);
method public void setActiveSource(@NonNull android.hardware.hdmi.HdmiDeviceInfo);
method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setHdmiCecEnabled(@NonNull int);
@@ -2871,6 +2874,10 @@ package android.hardware.hdmi {
field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa
}
public static interface HdmiControlManager.CecSettingChangeListener {
method public void onChange(@NonNull String);
}
@IntDef({android.hardware.hdmi.HdmiControlManager.RESULT_SUCCESS, android.hardware.hdmi.HdmiControlManager.RESULT_TIMEOUT, android.hardware.hdmi.HdmiControlManager.RESULT_SOURCE_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_TARGET_NOT_AVAILABLE, android.hardware.hdmi.HdmiControlManager.RESULT_ALREADY_IN_PROGRESS, android.hardware.hdmi.HdmiControlManager.RESULT_EXCEPTION, android.hardware.hdmi.HdmiControlManager.RESULT_INCORRECT_MODE, android.hardware.hdmi.HdmiControlManager.RESULT_COMMUNICATION_FAILED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public static @interface HdmiControlManager.ControlCallbackResult {
}

View File

@@ -1371,6 +1371,87 @@ public final class HdmiControlManager {
};
}
/**
* Listener used to get setting change notification.
*
* @hide
*/
@SystemApi
public interface CecSettingChangeListener {
/**
* Called when value of a setting changes.
*
* @param setting name of a CEC setting that changed
*/
void onChange(@NonNull @CecSettingName String setting);
}
private final ArrayMap<String,
ArrayMap<CecSettingChangeListener, IHdmiCecSettingChangeListener>>
mCecSettingChangeListeners = new ArrayMap<>();
private void addCecSettingChangeListener(
@NonNull @CecSettingName String setting,
@NonNull @CallbackExecutor Executor executor,
@NonNull CecSettingChangeListener listener) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
return;
}
if (mCecSettingChangeListeners.containsKey(setting)
&& mCecSettingChangeListeners.get(setting).containsKey(listener)) {
Log.e(TAG, "listener is already registered");
return;
}
IHdmiCecSettingChangeListener wrappedListener =
getCecSettingChangeListenerWrapper(executor, listener);
if (!mCecSettingChangeListeners.containsKey(setting)) {
mCecSettingChangeListeners.put(setting, new ArrayMap<>());
}
mCecSettingChangeListeners.get(setting).put(listener, wrappedListener);
try {
mService.addCecSettingChangeListener(setting, wrappedListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private void removeCecSettingChangeListener(
@NonNull @CecSettingName String setting,
@NonNull CecSettingChangeListener listener) {
if (mService == null) {
Log.e(TAG, "HdmiControlService is not available");
return;
}
IHdmiCecSettingChangeListener wrappedListener =
!mCecSettingChangeListeners.containsKey(setting) ? null :
mCecSettingChangeListeners.get(setting).remove(listener);
if (wrappedListener == null) {
Log.e(TAG, "tried to remove not-registered listener");
return;
}
try {
mService.removeCecSettingChangeListener(setting, wrappedListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private IHdmiCecSettingChangeListener getCecSettingChangeListenerWrapper(
Executor executor, final CecSettingChangeListener listener) {
return new IHdmiCecSettingChangeListener.Stub() {
@Override
public void onChange(String setting) {
final long token = Binder.clearCallingIdentity();
try {
executor.execute(() -> listener.onChange(setting));
} finally {
Binder.restoreCallingIdentity(token);
}
}
};
}
/**
* Get a set of user-modifiable settings.
*
@@ -1492,6 +1573,53 @@ public final class HdmiControlManager {
}
}
/**
* Add change listener for global status of HDMI CEC.
*
* <p>To stop getting the notification,
* use {@link #removeHdmiCecEnabledChangeListener(CecSettingChangeListener)}.
*
* Note that each invocation of the callback will be executed on an arbitrary
* Binder thread. This means that all callback implementations must be
* thread safe. To specify the execution thread, use
* {@link addHdmiCecEnabledChangeListener(Executor, CecSettingChangeListener)}.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void addHdmiCecEnabledChangeListener(@NonNull CecSettingChangeListener listener) {
addHdmiCecEnabledChangeListener(ConcurrentUtils.DIRECT_EXECUTOR, listener);
}
/**
* Add change listener for global status of HDMI CEC.
*
* <p>To stop getting the notification,
* use {@link #removeHdmiCecEnabledChangeListener(CecSettingChangeListener)}.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void addHdmiCecEnabledChangeListener(
@NonNull @CallbackExecutor Executor executor,
@NonNull CecSettingChangeListener listener) {
addCecSettingChangeListener(CEC_SETTING_NAME_HDMI_CEC_ENABLED, executor, listener);
}
/**
* Remove change listener for global status of HDMI CEC.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.HDMI_CEC)
public void removeHdmiCecEnabledChangeListener(
@NonNull CecSettingChangeListener listener) {
removeCecSettingChangeListener(CEC_SETTING_NAME_HDMI_CEC_ENABLED, listener);
}
/**
* Set the version of the HDMI CEC specification currently used.
*

View File

@@ -305,6 +305,18 @@ public final class HdmiControlServiceWrapper {
HdmiControlServiceWrapper.this.removeHdmiCecVolumeControlFeatureListener(listener);
}
@Override
public void addCecSettingChangeListener(String name,
IHdmiCecSettingChangeListener listener) {
HdmiControlServiceWrapper.this.addCecSettingChangeListener(name, listener);
}
@Override
public void removeCecSettingChangeListener(String name,
IHdmiCecSettingChangeListener listener) {
HdmiControlServiceWrapper.this.removeCecSettingChangeListener(name, listener);
}
@Override
public List<String> getUserCecSettings() {
return HdmiControlServiceWrapper.this.getUserCecSettings();
@@ -521,6 +533,14 @@ public final class HdmiControlServiceWrapper {
public void removeHdmiCecVolumeControlFeatureListener(
IHdmiCecVolumeControlFeatureListener listener) {}
/** @hide */
public void addCecSettingChangeListener(String name,
IHdmiCecSettingChangeListener listener) {}
/** @hide */
public void removeCecSettingChangeListener(String name,
IHdmiCecSettingChangeListener listener) {}
/** @hide */
public List<String> getUserCecSettings() {
return new ArrayList<>();

View File

@@ -0,0 +1,27 @@
/*
* 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;
/**
* Callback interface definition for HDMI client to get informed of
* CEC setting change.
*
* @hide
*/
oneway interface IHdmiCecSettingChangeListener {
void onChange(in String setting);
}

View File

@@ -18,6 +18,7 @@ package android.hardware.hdmi;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecSettingChangeListener;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
@@ -89,6 +90,8 @@ interface IHdmiControlService {
boolean isHdmiCecVolumeControlEnabled();
void reportAudioStatus(int deviceType, int volume, int maxVolume, boolean isMute);
void setSystemAudioModeOnForAudioOnlySource();
void addCecSettingChangeListener(String name, IHdmiCecSettingChangeListener listener);
void removeCecSettingChangeListener(String name, IHdmiCecSettingChangeListener listener);
List<String> getUserCecSettings();
List<String> getAllowedCecSettingStringValues(String name);
int[] getAllowedCecSettingIntValues(String name);

View File

@@ -394,6 +394,16 @@ public class HdmiAudioSystemClientTest {
return new ArrayList<>();
}
@Override
public void addCecSettingChangeListener(String name,
IHdmiCecSettingChangeListener listener) {
}
@Override
public void removeCecSettingChangeListener(String name,
IHdmiCecSettingChangeListener listener) {
}
@Override
public int[] getAllowedCecSettingIntValues(String name) {
return new int[0];

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.IHdmiCecSettingChangeListener;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
@@ -74,6 +75,7 @@ import android.os.UserHandle;
import android.provider.Settings.Global;
import android.sysprop.HdmiProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -308,6 +310,11 @@ public class HdmiControlService extends SystemService {
private final ArrayList<VendorCommandListenerRecord> mVendorCommandListenerRecords =
new ArrayList<>();
// List of records for CEC setting change listener to handle the caller killed in action.
@GuardedBy("mLock")
private final ArrayMap<String, RemoteCallbackList<IHdmiCecSettingChangeListener>>
mHdmiCecSettingChangeListenerRecords = new ArrayMap<>();
@GuardedBy("mLock")
private InputChangeListenerRecord mInputChangeListenerRecord;
@@ -2221,6 +2228,20 @@ public class HdmiControlService extends SystemService {
}
}
@Override
public void addCecSettingChangeListener(String name,
final IHdmiCecSettingChangeListener listener) {
enforceAccessPermission();
HdmiControlService.this.addCecSettingChangeListener(name, listener);
}
@Override
public void removeCecSettingChangeListener(String name,
final IHdmiCecSettingChangeListener listener) {
enforceAccessPermission();
HdmiControlService.this.removeCecSettingChangeListener(name, listener);
}
@Override
public List<String> getUserCecSettings() {
enforceAccessPermission();
@@ -3450,4 +3471,53 @@ public class HdmiControlService extends SystemService {
protected HdmiCecConfig getHdmiCecConfig() {
return mHdmiCecConfig;
}
private HdmiCecConfig.SettingChangeListener mSettingChangeListener =
new HdmiCecConfig.SettingChangeListener() {
@Override
public void onChange(String name) {
synchronized (mLock) {
if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
return;
}
mHdmiCecSettingChangeListenerRecords.get(name).broadcast(listener -> {
invokeCecSettingChangeListenerLocked(name, listener);
});
}
}
};
private void addCecSettingChangeListener(String name,
final IHdmiCecSettingChangeListener listener) {
synchronized (mLock) {
if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
mHdmiCecSettingChangeListenerRecords.put(name, new RemoteCallbackList<>());
mHdmiCecConfig.registerChangeListener(name, mSettingChangeListener);
}
mHdmiCecSettingChangeListenerRecords.get(name).register(listener);
}
}
private void removeCecSettingChangeListener(String name,
final IHdmiCecSettingChangeListener listener) {
synchronized (mLock) {
if (!mHdmiCecSettingChangeListenerRecords.containsKey(name)) {
return;
}
mHdmiCecSettingChangeListenerRecords.get(name).unregister(listener);
if (mHdmiCecSettingChangeListenerRecords.get(name).getRegisteredCallbackCount() == 0) {
mHdmiCecSettingChangeListenerRecords.remove(name);
mHdmiCecConfig.removeChangeListener(name, mSettingChangeListener);
}
}
}
private void invokeCecSettingChangeListenerLocked(String name,
final IHdmiCecSettingChangeListener listener) {
try {
listener.onChange(name);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to report setting change", e);
}
}
}