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