Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7ddbb62c authored by Marvin Ramin's avatar Marvin Ramin Committed by Nick Chalko
Browse files

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
parent afbd12c4
Loading
Loading
Loading
Loading
+93 −0
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.hardware.hdmi;


import static com.android.internal.os.RoSystemProperties.PROPERTY_HDMI_IS_DEVICE_HDMI_CEC_SWITCH;
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.IntDef;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -30,6 +31,7 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.SystemService;
import android.content.Context;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.SystemProperties;
import android.util.ArrayMap;
import android.util.ArrayMap;
@@ -40,6 +42,7 @@ import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Objects;
import java.util.concurrent.Executor;


/**
/**
 * The {@link HdmiControlManager} class is used to send HDMI control messages
 * The {@link HdmiControlManager} class is used to send HDMI control messages
@@ -817,6 +820,24 @@ public final class HdmiControlManager {
    private final ArrayMap<HdmiControlStatusChangeListener, IHdmiControlStatusChangeListener>
    private final ArrayMap<HdmiControlStatusChangeListener, IHdmiControlStatusChangeListener>
            mHdmiControlStatusChangeListeners = new ArrayMap<>();
            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.
     * 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));
            }
        };
    }
}
}
+32 −0
Original line number Original line Diff line number Diff line
/*
 * 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);
}
+3 −0
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package android.hardware.hdmi;


import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiDeviceEventListener;
import android.hardware.hdmi.IHdmiDeviceEventListener;
@@ -44,6 +45,8 @@ interface IHdmiControlService {
    void queryDisplayStatus(IHdmiControlCallback callback);
    void queryDisplayStatus(IHdmiControlCallback callback);
    void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
    void addHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
    void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
    void removeHdmiControlStatusChangeListener(IHdmiControlStatusChangeListener listener);
    void addHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
    void removeHdmiCecVolumeControlFeatureListener(IHdmiCecVolumeControlFeatureListener listener);
    void addHotplugEventListener(IHdmiHotplugEventListener listener);
    void addHotplugEventListener(IHdmiHotplugEventListener listener);
    void removeHotplugEventListener(IHdmiHotplugEventListener listener);
    void removeHotplugEventListener(IHdmiHotplugEventListener listener);
    void addDeviceEventListener(IHdmiDeviceEventListener listener);
    void addDeviceEventListener(IHdmiDeviceEventListener listener);
+10 −0
Original line number Original line Diff line number Diff line
@@ -363,6 +363,16 @@ public class HdmiAudioSystemClientTest {
        public boolean isHdmiCecVolumeControlEnabled() {
        public boolean isHdmiCecVolumeControlEnabled() {
            return true;
            return true;
        }
        }

        @Override
        public void addHdmiCecVolumeControlFeatureListener(
                IHdmiCecVolumeControlFeatureListener listener) {
        }

        @Override
        public void removeHdmiCecVolumeControlFeatureListener(
                IHdmiCecVolumeControlFeatureListener listener) {
        }
    }
    }


}
}
+63 −0
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.HdmiHotplugEvent;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
@@ -63,6 +64,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Looper;
import android.os.Looper;
import android.os.PowerManager;
import android.os.PowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.SystemProperties;
@@ -268,6 +270,11 @@ public class HdmiControlService extends SystemService {
    private final ArrayList<HdmiControlStatusChangeListenerRecord>
    private final ArrayList<HdmiControlStatusChangeListenerRecord>
            mHdmiControlStatusChangeListenerRecords = new ArrayList<>();
            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.
    // List of records for hotplug event listener to handle the the caller killed in action.
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
    private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
@@ -1813,6 +1820,21 @@ public class HdmiControlService extends SystemService {
            HdmiControlService.this.removeHdmiControlStatusChangeListener(listener);
            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
        @Override
        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
        public void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
            enforceAccessPermission();
            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) {
    private void addHotplugEventListener(final IHdmiHotplugEventListener listener) {
        final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
        final HotplugEventListenerRecord record = new HotplugEventListenerRecord(listener);
        try {
        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() {
    public HdmiCecLocalDeviceTv tv() {
        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
        return (HdmiCecLocalDeviceTv) mCecController.getLocalDevice(HdmiDeviceInfo.DEVICE_TV);
    }
    }
@@ -3027,6 +3089,7 @@ public class HdmiControlService extends SystemService {
                        isHdmiCecVolumeControlEnabled);
                        isHdmiCecVolumeControlEnabled);
            }
            }
        }
        }
        announceHdmiCecVolumeControlFeatureChange(isHdmiCecVolumeControlEnabled);
    }
    }


    boolean isHdmiCecVolumeControlEnabled() {
    boolean isHdmiCecVolumeControlEnabled() {
Loading