From 9e922ca97097cb1aa67ff53219d874ea2503a80d Mon Sep 17 00:00:00 2001 From: Wonsik Kim Date: Fri, 20 Jun 2014 17:27:32 +0900 Subject: [PATCH] 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 --- .../java/com/android/server/tv/TvInputHal.java | 108 +++++++++++++++------ .../core/jni/com_android_server_tv_TvInputHal.cpp | 1 - 2 files changed, 80 insertions(+), 29 deletions(-) diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java index 1535e7a9a934..23c0a4ccd984 100644 --- a/services/core/java/com/android/server/tv/TvInputHal.java +++ b/services/core/java/com/android/server/tv/TvInputHal.java @@ -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()); + 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 streamConfigsChangedFromNative(int deviceId) { + mHandler.sendMessage( + mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0)); + } + + // Handler.Callback implementation + + private Queue mPendingMessageQueue = new LinkedList(); + + @Override + 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 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; } - }); - } - private void deviceUnavailableFromNative(final int deviceId) { - mHandler.post(new Runnable() { - @Override - public void run() { + case EVENT_DEVICE_UNAVAILABLE: { + int deviceId = msg.arg1; mCallback.onDeviceUnavailable(deviceId); + break; } - }); - } - private void streamConfigsChangedFromNative(final int deviceId) { - mHandler.post(new Runnable() { - @Override - public void run() { - retrieveStreamConfigs(deviceId); + 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; + } } } diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp index 9cecdf0612d9..7b8e6fdb4d06 100644 --- a/services/core/jni/com_android_server_tv_TvInputHal.cpp +++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp @@ -75,7 +75,6 @@ public: static JTvInputHal* createInstance(JNIEnv* env, jobject thiz); int setSurface(int deviceId, int streamId, const sp& surface); - void getStreamConfigs(int deviceId, jobjectArray* array); const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs); private: -- 2.11.0