Home | History | Annotate | Download | only in tv
      1 /*
      2  * Copyright 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.tv;
     18 
     19 import android.hardware.tv.input.V1_0.Constants;
     20 import android.media.tv.TvInputHardwareInfo;
     21 import android.media.tv.TvStreamConfig;
     22 import android.os.Handler;
     23 import android.os.Message;
     24 import android.os.MessageQueue;
     25 import android.util.Slog;
     26 import android.util.SparseArray;
     27 import android.util.SparseIntArray;
     28 import android.view.Surface;
     29 
     30 import java.util.LinkedList;
     31 import java.util.Queue;
     32 
     33 /**
     34  * Provides access to the low-level TV input hardware abstraction layer.
     35  */
     36 final class TvInputHal implements Handler.Callback {
     37     private final static boolean DEBUG = false;
     38     private final static String TAG = TvInputHal.class.getSimpleName();
     39 
     40     public final static int SUCCESS = 0;
     41     public final static int ERROR_NO_INIT = -1;
     42     public final static int ERROR_STALE_CONFIG = -2;
     43     public final static int ERROR_UNKNOWN = -3;
     44 
     45     public static final int EVENT_DEVICE_AVAILABLE = Constants.EVENT_DEVICE_AVAILABLE;
     46     public static final int EVENT_DEVICE_UNAVAILABLE = Constants.EVENT_DEVICE_UNAVAILABLE;
     47     public static final int EVENT_STREAM_CONFIGURATION_CHANGED =
     48             Constants.EVENT_STREAM_CONFIGURATIONS_CHANGED;
     49     public static final int EVENT_FIRST_FRAME_CAPTURED = 4;
     50 
     51     public interface Callback {
     52         void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs);
     53         void onDeviceUnavailable(int deviceId);
     54         void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
     55         void onFirstFrameCaptured(int deviceId, int streamId);
     56     }
     57 
     58     private native long nativeOpen(MessageQueue queue);
     59 
     60     private static native int nativeAddOrUpdateStream(long ptr, int deviceId, int streamId,
     61             Surface surface);
     62     private static native int nativeRemoveStream(long ptr, int deviceId, int streamId);
     63     private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
     64             int generation);
     65     private static native void nativeClose(long ptr);
     66 
     67     private final Object mLock = new Object();
     68     private long mPtr = 0;
     69     private final Callback mCallback;
     70     private final Handler mHandler;
     71     private final SparseIntArray mStreamConfigGenerations = new SparseIntArray();
     72     private final SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();
     73 
     74     public TvInputHal(Callback callback) {
     75         mCallback = callback;
     76         mHandler = new Handler(this);
     77     }
     78 
     79     public void init() {
     80         synchronized (mLock) {
     81             mPtr = nativeOpen(mHandler.getLooper().getQueue());
     82         }
     83     }
     84 
     85     public int addOrUpdateStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {
     86         synchronized (mLock) {
     87             if (mPtr == 0) {
     88                 return ERROR_NO_INIT;
     89             }
     90             int generation = mStreamConfigGenerations.get(deviceId, 0);
     91             if (generation != streamConfig.getGeneration()) {
     92                 return ERROR_STALE_CONFIG;
     93             }
     94             if (nativeAddOrUpdateStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
     95                 return SUCCESS;
     96             } else {
     97                 return ERROR_UNKNOWN;
     98             }
     99         }
    100     }
    101 
    102     public int removeStream(int deviceId, TvStreamConfig streamConfig) {
    103         synchronized (mLock) {
    104             if (mPtr == 0) {
    105                 return ERROR_NO_INIT;
    106             }
    107             int generation = mStreamConfigGenerations.get(deviceId, 0);
    108             if (generation != streamConfig.getGeneration()) {
    109                 return ERROR_STALE_CONFIG;
    110             }
    111             if (nativeRemoveStream(mPtr, deviceId, streamConfig.getStreamId()) == 0) {
    112                 return SUCCESS;
    113             } else {
    114                 return ERROR_UNKNOWN;
    115             }
    116         }
    117     }
    118 
    119     public void close() {
    120         synchronized (mLock) {
    121             if (mPtr != 0L) {
    122                 nativeClose(mPtr);
    123             }
    124         }
    125     }
    126 
    127     private void retrieveStreamConfigsLocked(int deviceId) {
    128         int generation = mStreamConfigGenerations.get(deviceId, 0) + 1;
    129         mStreamConfigs.put(deviceId, nativeGetStreamConfigs(mPtr, deviceId, generation));
    130         mStreamConfigGenerations.put(deviceId, generation);
    131     }
    132 
    133     // Called from native
    134     private void deviceAvailableFromNative(TvInputHardwareInfo info) {
    135         if (DEBUG) {
    136             Slog.d(TAG, "deviceAvailableFromNative: info = " + info);
    137         }
    138         mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info).sendToTarget();
    139     }
    140 
    141     private void deviceUnavailableFromNative(int deviceId) {
    142         mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0).sendToTarget();
    143     }
    144 
    145     private void streamConfigsChangedFromNative(int deviceId) {
    146         mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget();
    147     }
    148 
    149     private void firstFrameCapturedFromNative(int deviceId, int streamId) {
    150         mHandler.sendMessage(
    151                 mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, streamId));
    152     }
    153 
    154     // Handler.Callback implementation
    155 
    156     private final Queue<Message> mPendingMessageQueue = new LinkedList<>();
    157 
    158     @Override
    159     public boolean handleMessage(Message msg) {
    160         switch (msg.what) {
    161             case EVENT_DEVICE_AVAILABLE: {
    162                 TvStreamConfig[] configs;
    163                 TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
    164                 synchronized (mLock) {
    165                     retrieveStreamConfigsLocked(info.getDeviceId());
    166                     if (DEBUG) {
    167                         Slog.d(TAG, "EVENT_DEVICE_AVAILABLE: info = " + info);
    168                     }
    169                     configs = mStreamConfigs.get(info.getDeviceId());
    170                 }
    171                 mCallback.onDeviceAvailable(info, configs);
    172                 break;
    173             }
    174 
    175             case EVENT_DEVICE_UNAVAILABLE: {
    176                 int deviceId = msg.arg1;
    177                 if (DEBUG) {
    178                     Slog.d(TAG, "EVENT_DEVICE_UNAVAILABLE: deviceId = " + deviceId);
    179                 }
    180                 mCallback.onDeviceUnavailable(deviceId);
    181                 break;
    182             }
    183 
    184             case EVENT_STREAM_CONFIGURATION_CHANGED: {
    185                 TvStreamConfig[] configs;
    186                 int deviceId = msg.arg1;
    187                 synchronized (mLock) {
    188                     if (DEBUG) {
    189                         Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId);
    190                     }
    191                     retrieveStreamConfigsLocked(deviceId);
    192                     configs = mStreamConfigs.get(deviceId);
    193                 }
    194                 mCallback.onStreamConfigurationChanged(deviceId, configs);
    195                 break;
    196             }
    197 
    198             case EVENT_FIRST_FRAME_CAPTURED: {
    199                 int deviceId = msg.arg1;
    200                 int streamId = msg.arg2;
    201                 mCallback.onFirstFrameCaptured(deviceId, streamId);
    202                 break;
    203             }
    204 
    205             default:
    206                 Slog.e(TAG, "Unknown event: " + msg);
    207                 return false;
    208         }
    209 
    210         return true;
    211     }
    212 }
    213