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.media.tv.TvInputHardwareInfo;
     20 import android.media.tv.TvStreamConfig;
     21 import android.os.Handler;
     22 import android.os.Message;
     23 import android.os.MessageQueue;
     24 import android.util.Slog;
     25 import android.util.SparseArray;
     26 import android.util.SparseIntArray;
     27 import android.view.Surface;
     28 
     29 import java.util.LinkedList;
     30 import java.util.Queue;
     31 
     32 /**
     33  * Provides access to the low-level TV input hardware abstraction layer.
     34  */
     35 final class TvInputHal implements Handler.Callback {
     36     private final static boolean DEBUG = false;
     37     private final static String TAG = TvInputHal.class.getSimpleName();
     38 
     39     public final static int SUCCESS = 0;
     40     public final static int ERROR_NO_INIT = -1;
     41     public final static int ERROR_STALE_CONFIG = -2;
     42     public final static int ERROR_UNKNOWN = -3;
     43 
     44     public static final int EVENT_DEVICE_AVAILABLE = 1;
     45     public static final int EVENT_DEVICE_UNAVAILABLE = 2;
     46     public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;
     47     public static final int EVENT_FIRST_FRAME_CAPTURED = 4;
     48 
     49     public interface Callback {
     50         void onDeviceAvailable(TvInputHardwareInfo info, TvStreamConfig[] configs);
     51         void onDeviceUnavailable(int deviceId);
     52         void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
     53         void onFirstFrameCaptured(int deviceId, int streamId);
     54     }
     55 
     56     private native long nativeOpen(MessageQueue queue);
     57 
     58     private static native int nativeAddOrUpdateStream(long ptr, int deviceId, int streamId,
     59             Surface surface);
     60     private static native int nativeRemoveStream(long ptr, int deviceId, int streamId);
     61     private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
     62             int generation);
     63     private static native void nativeClose(long ptr);
     64 
     65     private final Object mLock = new Object();
     66     private long mPtr = 0;
     67     private final Callback mCallback;
     68     private final Handler mHandler;
     69     private final SparseIntArray mStreamConfigGenerations = new SparseIntArray();
     70     private final SparseArray<TvStreamConfig[]> mStreamConfigs = new SparseArray<>();
     71 
     72     public TvInputHal(Callback callback) {
     73         mCallback = callback;
     74         mHandler = new Handler(this);
     75     }
     76 
     77     public void init() {
     78         synchronized (mLock) {
     79             mPtr = nativeOpen(mHandler.getLooper().getQueue());
     80         }
     81     }
     82 
     83     public int addOrUpdateStream(int deviceId, Surface surface, TvStreamConfig streamConfig) {
     84         synchronized (mLock) {
     85             if (mPtr == 0) {
     86                 return ERROR_NO_INIT;
     87             }
     88             int generation = mStreamConfigGenerations.get(deviceId, 0);
     89             if (generation != streamConfig.getGeneration()) {
     90                 return ERROR_STALE_CONFIG;
     91             }
     92             if (nativeAddOrUpdateStream(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
     93                 return SUCCESS;
     94             } else {
     95                 return ERROR_UNKNOWN;
     96             }
     97         }
     98     }
     99 
    100     public int removeStream(int deviceId, TvStreamConfig streamConfig) {
    101         synchronized (mLock) {
    102             if (mPtr == 0) {
    103                 return ERROR_NO_INIT;
    104             }
    105             int generation = mStreamConfigGenerations.get(deviceId, 0);
    106             if (generation != streamConfig.getGeneration()) {
    107                 return ERROR_STALE_CONFIG;
    108             }
    109             if (nativeRemoveStream(mPtr, deviceId, streamConfig.getStreamId()) == 0) {
    110                 return SUCCESS;
    111             } else {
    112                 return ERROR_UNKNOWN;
    113             }
    114         }
    115     }
    116 
    117     public void close() {
    118         synchronized (mLock) {
    119             if (mPtr != 0L) {
    120                 nativeClose(mPtr);
    121             }
    122         }
    123     }
    124 
    125     private void retrieveStreamConfigsLocked(int deviceId) {
    126         int generation = mStreamConfigGenerations.get(deviceId, 0) + 1;
    127         mStreamConfigs.put(deviceId, nativeGetStreamConfigs(mPtr, deviceId, generation));
    128         mStreamConfigGenerations.put(deviceId, generation);
    129     }
    130 
    131     // Called from native
    132     private void deviceAvailableFromNative(TvInputHardwareInfo info) {
    133         if (DEBUG) {
    134             Slog.d(TAG, "deviceAvailableFromNative: info = " + info);
    135         }
    136         mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info).sendToTarget();
    137     }
    138 
    139     private void deviceUnavailableFromNative(int deviceId) {
    140         mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0).sendToTarget();
    141     }
    142 
    143     private void streamConfigsChangedFromNative(int deviceId) {
    144         mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0).sendToTarget();
    145     }
    146 
    147     private void firstFrameCapturedFromNative(int deviceId, int streamId) {
    148         mHandler.sendMessage(
    149                 mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, streamId));
    150     }
    151 
    152     // Handler.Callback implementation
    153 
    154     private final Queue<Message> mPendingMessageQueue = new LinkedList<>();
    155 
    156     @Override
    157     public boolean handleMessage(Message msg) {
    158         switch (msg.what) {
    159             case EVENT_DEVICE_AVAILABLE: {
    160                 TvStreamConfig[] configs;
    161                 TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
    162                 synchronized (mLock) {
    163                     retrieveStreamConfigsLocked(info.getDeviceId());
    164                     if (DEBUG) {
    165                         Slog.d(TAG, "EVENT_DEVICE_AVAILABLE: info = " + info);
    166                     }
    167                     configs = mStreamConfigs.get(info.getDeviceId());
    168                 }
    169                 mCallback.onDeviceAvailable(info, configs);
    170                 break;
    171             }
    172 
    173             case EVENT_DEVICE_UNAVAILABLE: {
    174                 int deviceId = msg.arg1;
    175                 if (DEBUG) {
    176                     Slog.d(TAG, "EVENT_DEVICE_UNAVAILABLE: deviceId = " + deviceId);
    177                 }
    178                 mCallback.onDeviceUnavailable(deviceId);
    179                 break;
    180             }
    181 
    182             case EVENT_STREAM_CONFIGURATION_CHANGED: {
    183                 TvStreamConfig[] configs;
    184                 int deviceId = msg.arg1;
    185                 synchronized (mLock) {
    186                     if (DEBUG) {
    187                         Slog.d(TAG, "EVENT_STREAM_CONFIGURATION_CHANGED: deviceId = " + deviceId);
    188                     }
    189                     retrieveStreamConfigsLocked(deviceId);
    190                     configs = mStreamConfigs.get(deviceId);
    191                 }
    192                 mCallback.onStreamConfigurationChanged(deviceId, configs);
    193                 break;
    194             }
    195 
    196             case EVENT_FIRST_FRAME_CAPTURED: {
    197                 int deviceId = msg.arg1;
    198                 int streamId = msg.arg2;
    199                 mCallback.onFirstFrameCaptured(deviceId, streamId);
    200                 break;
    201             }
    202 
    203             default:
    204                 Slog.e(TAG, "Unknown event: " + msg);
    205                 return false;
    206         }
    207 
    208         return true;
    209     }
    210 }
    211