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

Commit d643f764 authored by Jungshik Jang's avatar Jungshik Jang
Browse files

Add SendMessageCallback to Hdmi control service.

As we have separate IO thread, we should have async callback
mechanism to get result of send request.
For that, I added SendMessageCallback interface to HdmiControl
Service
Along with this, replace message-based IO handling with
post Runnable based one for consistency

Change-Id: I61cf5b751b4f2af3b34956060869f3512f161d11
parent 521d8cda
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -164,8 +164,13 @@ abstract class FeatureAction {
        mActionTimer.sendTimerMessage(state, delayMillis);
    }

    protected final boolean sendCommand(HdmiCecMessage cmd) {
        return mService.sendCecCommand(cmd);
    protected final void sendCommand(HdmiCecMessage cmd) {
        mService.sendCecCommand(cmd);
    }

    protected final void sendCommand(HdmiCecMessage cmd,
            HdmiControlService.SendMessageCallback callback) {
        mService.sendCecCommand(cmd, callback);
    }

    /**
+83 −110
Original line number Diff line number Diff line
@@ -20,9 +20,6 @@ import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;

@@ -176,53 +173,20 @@ final class HdmiCecController {
     *                         Otherwise, scan address will start from {@code preferredAddress}
     * @param callback callback interface to report allocated logical address to caller
     */
    void allocateLogicalAddress(int deviceType, int preferredAddress,
            AllocateLogicalAddressCallback callback) {
        Message msg = mIoHandler.obtainMessage(MSG_ALLOCATE_LOGICAL_ADDRESS);
        msg.arg1 = deviceType;
        msg.arg2 = preferredAddress;
        msg.obj = callback;
        mIoHandler.sendMessage(msg);
    }

    private static byte[] buildBody(int opcode, byte[] params) {
        byte[] body = new byte[params.length + 1];
        body[0] = (byte) opcode;
        System.arraycopy(params, 0, body, 1, params.length);
        return body;
    }

    private final class IoHandler extends Handler {
        private IoHandler(Looper looper) {
            super(looper);
        }

    void allocateLogicalAddress(final int deviceType, final int preferredAddress,
            final AllocateLogicalAddressCallback callback) {
        runOnIoThread(new Runnable() {
            @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SEND_CEC_COMMAND:
                    HdmiCecMessage cecMessage = (HdmiCecMessage) msg.obj;
                    byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
                    nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
                            cecMessage.getDestination(), body);
                    break;
                case MSG_ALLOCATE_LOGICAL_ADDRESS:
                    int deviceType = msg.arg1;
                    int preferredAddress = msg.arg2;
                    AllocateLogicalAddressCallback callback =
                            (AllocateLogicalAddressCallback) msg.obj;
            public void run() {
                handleAllocateLogicalAddress(deviceType, preferredAddress, callback);
                    break;
                default:
                    Slog.w(TAG, "Unsupported CEC Io request:" + msg.what);
                    break;
            }
        });
    }

        private void handleAllocateLogicalAddress(int deviceType, int preferredAddress,
                AllocateLogicalAddressCallback callback) {
    private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
            final AllocateLogicalAddressCallback callback) {
        int startAddress = preferredAddress;
            // If preferred address is "unregistered", start_index will be the smallest
        // If preferred address is "unregistered", start address will be the smallest
        // address matched with the given device type.
        if (preferredAddress == HdmiCec.ADDR_UNREGISTERED) {
            for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
@@ -233,7 +197,7 @@ final class HdmiCecController {
            }
        }

            int logcialAddress = HdmiCec.ADDR_UNREGISTERED;
        int logicalAddress = HdmiCec.ADDR_UNREGISTERED;
        // Iterates all possible addresses which has the same device type.
        for (int i = 0; i < NUM_LOGICAL_ADDRESS; ++i) {
            int curAddress = (startAddress + i) % NUM_LOGICAL_ADDRESS;
@@ -247,44 +211,28 @@ final class HdmiCecController {
                int error = nativeSendCecCommand(mNativePtr, curAddress, curAddress,
                        EMPTY_BODY);
                if (error != ERROR_SUCCESS) {
                        logcialAddress = curAddress;
                    logicalAddress = curAddress;
                    break;
                }
            }
        }

            Message msg = mControlHandler.obtainMessage(MSG_REPORT_LOGICAL_ADDRESS);
            msg.arg1 = deviceType;
            msg.arg2 = logcialAddress;
            msg.obj = callback;
            mControlHandler.sendMessage(msg);
        final int assignedAddress = logicalAddress;
        if (callback != null) {
            runOnServiceThread(new Runnable() {
                    @Override
                public void run() {
                    callback.onAllocated(deviceType, assignedAddress);
                }
            });
        }

    private final class ControlHandler extends Handler {
        private ControlHandler(Looper looper) {
            super(looper);
    }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RECEIVE_CEC_COMMAND:
                    // TODO: delegate it to HdmiControl service.
                    onReceiveCommand((HdmiCecMessage) msg.obj);
                    break;
                case MSG_REPORT_LOGICAL_ADDRESS:
                    int deviceType = msg.arg1;
                    int logicalAddress = msg.arg2;
                    AllocateLogicalAddressCallback callback =
                            (AllocateLogicalAddressCallback) msg.obj;
                    callback.onAllocated(deviceType, logicalAddress);
                    break;
                default:
                    Slog.i(TAG, "Unsupported message type:" + msg.what);
                    break;
            }
        }
    private static byte[] buildBody(int opcode, byte[] params) {
        byte[] body = new byte[params.length + 1];
        body[0] = (byte) opcode;
        System.arraycopy(params, 0, body, 1, params.length);
        return body;
    }

    /**
@@ -411,10 +359,18 @@ final class HdmiCecController {
        return nativeGetVendorId(mNativePtr);
    }

    private void runOnIoThread(Runnable runnable) {
        mIoHandler.post(runnable);
    }

    private void runOnServiceThread(Runnable runnable) {
        mControlHandler.post(runnable);
    }

    private void init(HdmiControlService service, long nativePtr) {
        mService = service;
        mIoHandler = new IoHandler(service.getServiceLooper());
        mControlHandler = new ControlHandler(service.getServiceLooper());
        mIoHandler = new Handler(service.getServiceLooper());
        mControlHandler = new Handler(service.getServiceLooper());
        mNativePtr = nativePtr;
    }

@@ -438,13 +394,27 @@ final class HdmiCecController {
        HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand
                (sourceAddress, message.getSource(), message.getOpcode(),
                        HdmiCecMessageBuilder.ABORT_REFUSED);
        sendCommand(cecMessage);

        sendCommand(cecMessage, null);
    }

    boolean sendCommand(HdmiCecMessage cecMessage) {
        Message message = mIoHandler.obtainMessage(MSG_SEND_CEC_COMMAND, cecMessage);
        return mIoHandler.sendMessage(message);
    void sendCommand(final HdmiCecMessage cecMessage,
            final HdmiControlService.SendMessageCallback callback) {
        runOnIoThread(new Runnable() {
            @Override
            public void run() {
                byte[] body = buildBody(cecMessage.getOpcode(), cecMessage.getParams());
                final int error = nativeSendCecCommand(mNativePtr, cecMessage.getSource(),
                        cecMessage.getDestination(), body);
                if (callback != null) {
                    runOnServiceThread(new Runnable() {
                        @Override
                        public void run() {
                            callback.onSendCompleted(error);
                        }
                    });
                }
            }
        });
    }

    /**
@@ -453,12 +423,15 @@ final class HdmiCecController {
    private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
        byte opcode = body[0];
        byte params[] = Arrays.copyOfRange(body, 1, body.length);
        HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
        final HdmiCecMessage cecMessage = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);

        // Delegate message to main handler so that it handles in main thread.
        Message message = mControlHandler.obtainMessage(
                MSG_RECEIVE_CEC_COMMAND, cecMessage);
        mControlHandler.sendMessage(message);
        runOnServiceThread(new Runnable() {
            @Override
            public void run() {
                onReceiveCommand(cecMessage);
            }
        });
    }

    /**
+24 −11
Original line number Diff line number Diff line
@@ -18,30 +18,25 @@ package com.android.server.hdmi;

import android.annotation.Nullable;
import android.content.Context;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.hardware.hdmi.HdmiCec;
import android.hardware.hdmi.HdmiCecDeviceInfo;
import android.hardware.hdmi.HdmiCecMessage;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.hdmi.IHdmiControlService;
import android.hardware.hdmi.IHdmiHotplugEventListener;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;


import com.android.internal.annotations.GuardedBy;
import com.android.server.SystemService;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

/**
@@ -54,6 +49,20 @@ public final class HdmiControlService extends SystemService {
    // TODO: Rename the permission to HDMI_CONTROL.
    private static final String PERMISSION = "android.permission.HDMI_CEC";

    /**
     * Interface to report send result.
     */
    interface SendMessageCallback {
        /**
         * Called when {@link HdmiControlService#sendCecCommand} is completed.
         *
         * @param error result of send request. 0 if succeed. Otherwise it will be
         *        negative value
         */
        // TODO: define error code as constants and update javadoc.
        void onSendCompleted(int error);
    }

    // A thread to handle synchronous IO of CEC and MHL control service.
    // Since all of CEC and MHL HAL interfaces processed in short time (< 200ms)
    // and sparse call it shares a thread to handle IO operations.
@@ -205,10 +214,14 @@ public final class HdmiControlService extends SystemService {
     * Transmit a CEC command to CEC bus.
     *
     * @param command CEC command to send out
     * @return {@code true} if succeeds to send command
     * @param callback interface used to the result of send command
     */
    boolean sendCecCommand(HdmiCecMessage command) {
        return mCecController.sendCommand(command);
    void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
        mCecController.sendCommand(command, callback);
    }

    void sendCecCommand(HdmiCecMessage command) {
        mCecController.sendCommand(command, null);
    }

    /**
+19 −11
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.hdmi;

import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;

/**
@@ -37,17 +38,24 @@ final class RequestArcInitiationAction extends RequestArcAction {

    @Override
    boolean start() {
        if (sendCommand(
                HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress, mAvrAddress))) {
        HdmiCecMessage command = HdmiCecMessageBuilder.buildRequestArcInitiation(mSourceAddress,
                mAvrAddress);
        sendCommand(command, new HdmiControlService.SendMessageCallback() {
            @Override
            public void onSendCompleted(int error) {
                // success.
                if (error == 0) {
                    mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
                    addTimer(mState, TIMEOUT_MS);
                } else {
                    Slog.w(TAG, "Failed to send <Request ARC Initiation>");
            // If failed to send <Request ARC Initiation>, start "Disabled" ARC transmission
            // action.
                    // If failed to send <Request ARC Initiation>, start "Disabled"
                    // ARC transmission action.
                    disableArcTransmission();
                    finish();
                }
            }
        });
        return true;
    }
}
+18 −11
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.hdmi;

import android.hardware.hdmi.HdmiCecMessage;
import android.util.Slog;

/**
@@ -37,17 +38,23 @@ final class RequestArcTerminationAction extends RequestArcAction {

    @Override
    boolean start() {
        if (sendCommand(
                HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress))) {
        HdmiCecMessage command =
                HdmiCecMessageBuilder.buildRequestArcTermination(mSourceAddress, mAvrAddress);
        sendCommand(command, new HdmiControlService.SendMessageCallback() {
            @Override
            public void onSendCompleted(int error) {
                if (error == 0) {
                    mState = STATE_WATING_FOR_REQUEST_ARC_REQUEST_RESPONSE;
                    addTimer(mState, TIMEOUT_MS);
                } else {
                    Slog.w(TAG, "Failed to send <Request ARC Initiation>");
            // If failed to send <Request ARC Termination>, start "Disabled" ARC transmission
            // action.
                    // If failed to send <Request ARC Termination>, start "Disabled" ARC
                    // transmission action.
                    disableArcTransmission();
                    finish();
                }
            }
        });
        return true;
    }
}
Loading