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

Commit 9e922ca9 authored by Wonsik Kim's avatar Wonsik Kim
Browse files

Fix native calls to null pointer

Since DEVICE_AVAILABLE event fires right after initialization, it
could happen before TvInputHal.mPtr is set, which causes calls to
null pointer. Fix the bug by allowing TvInputHal to wait for mPtr to
be set before calling native methods.

Change-Id: Id07f15130beb69f77c16bf3c735285c31ae4a3a4
parent 3722c328
Loading
Loading
Loading
Loading
+80 −28
Original line number Diff line number Diff line
@@ -20,12 +20,19 @@ import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvStreamConfig;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.Surface;
import android.util.Slog;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Provides access to the low-level TV input hardware abstraction layer.
 */
final class TvInputHal {
final class TvInputHal implements Handler.Callback {
    private final static String TAG = TvInputHal.class.getSimpleName();

    public final static int SUCCESS = 0;
    public final static int ERROR_NO_INIT = -1;
    public final static int ERROR_STALE_CONFIG = -2;
@@ -35,6 +42,12 @@ final class TvInputHal {
    public static final int TYPE_BUILT_IN_TUNER = 2;
    public static final int TYPE_PASSTHROUGH = 3;

    public static final int EVENT_OPEN = 0;
    // Below should be in sync with hardware/libhardware/include/hardware/tv_input.h
    public static final int EVENT_DEVICE_AVAILABLE = 1;
    public static final int EVENT_DEVICE_UNAVAILABLE = 2;
    public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;

    public interface Callback {
        public void onDeviceAvailable(
                TvInputHardwareInfo info, TvStreamConfig[] configs);
@@ -50,7 +63,7 @@ final class TvInputHal {
            int generation);
    private static native void nativeClose(long ptr);

    private long mPtr = 0l;
    private volatile long mPtr = 0;
    private final Callback mCallback;
    private final HandlerThread mThread = new HandlerThread("TV input HAL event thread");
    private final Handler mHandler;
@@ -60,21 +73,23 @@ final class TvInputHal {
    public TvInputHal(Callback callback) {
        mCallback = callback;
        mThread.start();
        mHandler = new Handler(mThread.getLooper());
        mHandler = new Handler(mThread.getLooper(), this);
    }

    public void init() {
        mPtr = nativeOpen();
        mHandler.sendEmptyMessage(EVENT_OPEN);
    }

    public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) {
        if (mPtr == 0) {
        long ptr = mPtr;
        if (ptr == 0) {
            return ERROR_NO_INIT;
        }
        if (mStreamConfigGeneration != streamConfig.getGeneration()) {
            return ERROR_STALE_CONFIG;
        }
        if (nativeSetSurface(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
        if (nativeSetSurface(ptr, deviceId, streamConfig.getStreamId(), surface) == 0) {
            return SUCCESS;
        } else {
            return ERROR_UNKNOWN;
@@ -82,44 +97,81 @@ final class TvInputHal {
    }

    public void close() {
        if (mPtr != 0l) {
            nativeClose(mPtr);
        long ptr = mPtr;
        if (ptr != 0l) {
            nativeClose(ptr);
            mThread.quitSafely();
        }
    }

    private synchronized void retrieveStreamConfigs(int deviceId) {
    private synchronized void retrieveStreamConfigs(long ptr, int deviceId) {
        ++mStreamConfigGeneration;
        mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration);
        mStreamConfigs = nativeGetStreamConfigs(ptr, deviceId, mStreamConfigGeneration);
    }

    // Called from native
    private void deviceAvailableFromNative(final TvInputHardwareInfo info) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                retrieveStreamConfigs(info.getDeviceId());
                mCallback.onDeviceAvailable(info, mStreamConfigs);
    private void deviceAvailableFromNative(TvInputHardwareInfo info) {
        mHandler.sendMessage(
                mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info));
    }
        });

    private void deviceUnavailableFromNative(int deviceId) {
        mHandler.sendMessage(
                mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0));
    }

    private void deviceUnavailableFromNative(final int deviceId) {
        mHandler.post(new Runnable() {
    private void streamConfigsChangedFromNative(int deviceId) {
        mHandler.sendMessage(
                mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0));
    }

    // Handler.Callback implementation

    private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();

    @Override
            public void run() {
                mCallback.onDeviceUnavailable(deviceId);
    public boolean handleMessage(Message msg) {
        long ptr = mPtr;
        if (ptr == 0) {
            mPendingMessageQueue.add(msg);
            return true;
        }
        });
        while (!mPendingMessageQueue.isEmpty()) {
            handleMessageInternal(ptr, mPendingMessageQueue.remove());
        }
        handleMessageInternal(ptr, msg);
        return true;
    }

    private void streamConfigsChangedFromNative(final int deviceId) {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                retrieveStreamConfigs(deviceId);
    private void handleMessageInternal(long ptr, Message msg) {
        switch (msg.what) {
            case EVENT_OPEN:
                // No-op
                break;

            case EVENT_DEVICE_AVAILABLE: {
                TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
                retrieveStreamConfigs(ptr, info.getDeviceId());
                mCallback.onDeviceAvailable(info, mStreamConfigs);
                break;
            }

            case EVENT_DEVICE_UNAVAILABLE: {
                int deviceId = msg.arg1;
                mCallback.onDeviceUnavailable(deviceId);
                break;
            }

            case EVENT_STREAM_CONFIGURATION_CHANGED: {
                int deviceId = msg.arg1;
                retrieveStreamConfigs(ptr, deviceId);
                mCallback.onStreamConfigurationChanged(deviceId, mStreamConfigs);
                break;
            }

            default:
                Slog.e(TAG, "Unknown event: " + msg);
                break;
        }
        });
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -75,7 +75,6 @@ public:
    static JTvInputHal* createInstance(JNIEnv* env, jobject thiz);

    int setSurface(int deviceId, int streamId, const sp<Surface>& surface);
    void getStreamConfigs(int deviceId, jobjectArray* array);
    const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);

private: