OSDN Git Service

Fix native calls to null pointer
authorWonsik Kim <wonsik@google.com>
Fri, 20 Jun 2014 08:27:32 +0000 (17:27 +0900)
committerWonsik Kim <wonsik@google.com>
Mon, 23 Jun 2014 05:06:19 +0000 (14:06 +0900)
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

services/core/java/com/android/server/tv/TvInputHal.java
services/core/jni/com_android_server_tv_TvInputHal.cpp

index 1535e7a..23c0a4c 100644 (file)
@@ -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<Message> mPendingMessageQueue = new LinkedList<Message>();
+
+    @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;
+        }
     }
 }
index 9cecdf0..7b8e6fd 100644 (file)
@@ -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: