core/java/android/service/wallpaper/IWallpaperEngine.aidl \
core/java/android/service/wallpaper/IWallpaperService.aidl \
core/java/android/tv/ITvInputClient.aidl \
+ core/java/android/tv/ITvInputHardware.aidl \
+ core/java/android/tv/ITvInputHardwareCallback.aidl \
core/java/android/tv/ITvInputManager.aidl \
core/java/android/tv/ITvInputService.aidl \
core/java/android/tv/ITvInputServiceCallback.aidl \
--- /dev/null
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+import android.tv.TvStreamConfig;
+import android.view.KeyEvent;
+import android.view.Surface;
+
+/**
+ * TvInputService representing a physical port should connect to HAL through this interface.
+ * Framework will take care of communication among system services including TvInputManagerService,
+ * HdmiControlService, AudioService, etc.
+ *
+ * @hide
+ */
+interface ITvInputHardware {
+ /**
+ * Make the input render on the surface according to the config. In case of HDMI, this will
+ * trigger CEC commands for adjusting active HDMI source. Returns true on success.
+ */
+ boolean setSurface(in Surface surface, in TvStreamConfig config);
+ /**
+ * Set volume for this stream via AudioGain. (TBD)
+ */
+ void setVolume(float volume);
+
+ /**
+ * Dispatch key event to HDMI service. The events would be automatically converted to
+ * HDMI CEC commands. If the hardware is not representing an HDMI port, this method will fail.
+ */
+ boolean dispatchKeyEventToHdmi(in KeyEvent event);
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+import android.tv.TvStreamConfig;
+
+/**
+ * @hide
+ */
+oneway interface ITvInputHardwareCallback {
+ void onReleased();
+ void onStreamConfigChanged(in TvStreamConfig[] configs);
+}
import android.content.ComponentName;
import android.graphics.Rect;
import android.net.Uri;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
import android.tv.ITvInputClient;
+import android.tv.TvInputHardwareInfo;
import android.tv.TvInputInfo;
import android.view.Surface;
int userId);
void relayoutOverlayView(in IBinder sessionToken, in Rect frame, int userId);
void removeOverlayView(in IBinder sessionToken, int userId);
+
+ // For TV input hardware binding
+ List<TvInputHardwareInfo> getHardwareList();
+ ITvInputHardware acquireTvInputHardware(int deviceId, in ITvInputHardwareCallback callback,
+ int userId);
+ void releaseTvInputHardware(int deviceId, in ITvInputHardware hardware, int userId);
}
--- /dev/null
+/*
+ *
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+parcelable TvInputHardwareInfo;
--- /dev/null
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Simple container for information about TV input hardware.
+ * Not for third-party developers.
+ *
+ * @hide
+ */
+public final class TvInputHardwareInfo implements Parcelable {
+ static final String TAG = "TvInputHardwareInfo";
+
+ // Match hardware/libhardware/include/hardware/tv_input.h
+ public static final int TV_INPUT_TYPE_HDMI = 1;
+ public static final int TV_INPUT_TYPE_BUILT_IN_TUNER = 2;
+ public static final int TV_INPUT_TYPE_PASSTHROUGH = 3;
+
+ public static final Parcelable.Creator<TvInputHardwareInfo> CREATOR =
+ new Parcelable.Creator<TvInputHardwareInfo>() {
+ @Override
+ public TvInputHardwareInfo createFromParcel(Parcel source) {
+ try {
+ TvInputHardwareInfo info = new TvInputHardwareInfo();
+ info.readFromParcel(source);
+ return info;
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvInputHardwareInfo from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvInputHardwareInfo[] newArray(int size) {
+ return new TvInputHardwareInfo[size];
+ }
+ };
+
+ private int mDeviceId;
+ private int mType;
+ // TODO: Add audio port & audio address for audio service.
+ // TODO: Add HDMI handle for HDMI service.
+
+ public TvInputHardwareInfo() { }
+
+ public TvInputHardwareInfo(int deviceId, int type) {
+ mDeviceId = deviceId;
+ mType = type;
+ }
+
+ public int getDeviceId() {
+ return mDeviceId;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mDeviceId);
+ dest.writeInt(mType);
+ }
+
+ public void readFromParcel(Parcel source) {
+ mDeviceId = source.readInt();
+ mType = source.readInt();
+ }
+}
--- /dev/null
+/*
+ *
+ * Copyright 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+parcelable TvStreamConfig;
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.tv;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class TvStreamConfig implements Parcelable {
+ static final String TAG = TvStreamConfig.class.getSimpleName();
+
+ public final static int STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE = 1;
+ public final static int STREAM_TYPE_BUFFER_PRODUCER = 2;
+
+ private int mStreamId;
+ private int mType;
+ // TODO: Revisit if max widht/height really make sense.
+ private int mMaxWidth;
+ private int mMaxHeight;
+ /**
+ * Generations are incremented once framework receives STREAM_CONFIGURATION_CHANGED event from
+ * HAL module. Framework should throw away outdated configurations and get new configurations
+ * via tv_input_device::get_stream_configurations().
+ */
+ private int mGeneration;
+
+ public static final Parcelable.Creator<TvStreamConfig> CREATOR =
+ new Parcelable.Creator<TvStreamConfig>() {
+ @Override
+ public TvStreamConfig createFromParcel(Parcel source) {
+ try {
+ return new Builder().
+ streamId(source.readInt()).
+ type(source.readInt()).
+ maxWidth(source.readInt()).
+ maxHeight(source.readInt()).
+ generation(source.readInt()).build();
+ } catch (Exception e) {
+ Log.e(TAG, "Exception creating TvStreamConfig from parcel", e);
+ return null;
+ }
+ }
+
+ @Override
+ public TvStreamConfig[] newArray(int size) {
+ return new TvStreamConfig[size];
+ }
+ };
+
+ private TvStreamConfig() {}
+
+ public int getStreamId() {
+ return mStreamId;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public int getMaxWidth() {
+ return mMaxWidth;
+ }
+
+ public int getMaxHeight() {
+ return mMaxHeight;
+ }
+
+ public int getGeneration() {
+ return mGeneration;
+ }
+
+ // Parcelable
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mStreamId);
+ dest.writeInt(mType);
+ dest.writeInt(mMaxWidth);
+ dest.writeInt(mMaxHeight);
+ dest.writeInt(mGeneration);
+ }
+
+ /**
+ * A helper class for creating a TvStreamConfig object.
+ */
+ public static final class Builder {
+ private Integer mStreamId;
+ private Integer mType;
+ private Integer mMaxWidth;
+ private Integer mMaxHeight;
+ private Integer mGeneration;
+
+ public Builder() {
+ }
+
+ public Builder streamId(int streamId) {
+ mStreamId = streamId;
+ return this;
+ }
+
+ public Builder type(int type) {
+ mType = type;
+ return this;
+ }
+
+ public Builder maxWidth(int maxWidth) {
+ mMaxWidth = maxWidth;
+ return this;
+ }
+
+ public Builder maxHeight(int maxHeight) {
+ mMaxHeight = maxHeight;
+ return this;
+ }
+
+ public Builder generation(int generation) {
+ mGeneration = generation;
+ return this;
+ }
+
+ public TvStreamConfig build() {
+ if (mStreamId == null || mType == null || mMaxWidth == null || mMaxHeight == null
+ || mGeneration == null) {
+ throw new UnsupportedOperationException();
+ }
+
+ TvStreamConfig config = new TvStreamConfig();
+ config.mStreamId = mStreamId;
+ config.mType = mType;
+ config.mMaxWidth = mMaxWidth;
+ config.mMaxHeight = mMaxHeight;
+ config.mGeneration = mGeneration;
+ return config;
+ }
+ }
+}
\ No newline at end of file
android:description="@string/permdesc_sim_communication"
android:protectionLevel="dangerous" />
+ <!-- Allows TvInputService to access underlying TV input hardware such as
+ built-in tuners and HDMI-in's.
+ @hide This should only be used by OEM's TvInputService's.
+ -->
+ <permission android:name="android.permission.TV_INPUT_HARDWARE"
+ android:protectionLevel="signatureOrSystem" />
+
<!-- =========================================== -->
<!-- Permissions associated with audio capture -->
<!-- =========================================== -->
--- /dev/null
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tv;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.tv.TvInputHardwareInfo;
+import android.tv.TvStreamConfig;
+import android.view.Surface;
+
+/**
+ * Provides access to the low-level TV input hardware abstraction layer.
+ */
+final class TvInputHal {
+ public final static int SUCCESS = 0;
+ public final static int ERROR_NO_INIT = -1;
+ public final static int ERROR_STALE_CONFIG = -2;
+ public final static int ERROR_UNKNOWN = -3;
+
+ public static final int TYPE_HDMI = 1;
+ public static final int TYPE_BUILT_IN_TUNER = 2;
+ public static final int TYPE_PASSTHROUGH = 3;
+
+ public interface Callback {
+ public void onDeviceAvailable(
+ TvInputHardwareInfo info, TvStreamConfig[] configs);
+ public void onDeviceUnavailable(int deviceId);
+ public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs);
+ }
+
+ private native long nativeOpen();
+
+ private static native int nativeSetSurface(long ptr, int deviceId, int streamId,
+ Surface surface);
+ private static native TvStreamConfig[] nativeGetStreamConfigs(long ptr, int deviceId,
+ int generation);
+ private static native void nativeClose(long ptr);
+
+ private long mPtr = 0l;
+ private final Callback mCallback;
+ private final HandlerThread mThread = new HandlerThread("TV input HAL event thread");
+ private final Handler mHandler;
+ private int mStreamConfigGeneration = 0;
+ private TvStreamConfig[] mStreamConfigs;
+
+ public TvInputHal(Callback callback) {
+ mCallback = callback;
+ mThread.start();
+ mHandler = new Handler(mThread.getLooper());
+ }
+
+ public void init() {
+ mPtr = nativeOpen();
+ }
+
+ public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) {
+ if (mPtr == 0) {
+ return ERROR_NO_INIT;
+ }
+ if (mStreamConfigGeneration != streamConfig.getGeneration()) {
+ return ERROR_STALE_CONFIG;
+ }
+ if (nativeSetSurface(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
+ return SUCCESS;
+ } else {
+ return ERROR_UNKNOWN;
+ }
+ }
+
+ public void close() {
+ if (mPtr != 0l) {
+ nativeClose(mPtr);
+ mThread.quitSafely();
+ }
+ }
+
+ private synchronized void retrieveStreamConfigs(int deviceId) {
+ ++mStreamConfigGeneration;
+ mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration);
+ }
+
+ // Called from native
+ private void deviceAvailableFromNative(int deviceId, int type) {
+ final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type);
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ retrieveStreamConfigs(info.getDeviceId());
+ mCallback.onDeviceAvailable(info, mStreamConfigs);
+ }
+ });
+ }
+
+ private void deviceUnavailableFromNative(int deviceId) {
+ final int id = deviceId;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mCallback.onDeviceUnavailable(id);
+ }
+ });
+ }
+
+ private void streamConfigsChangedFromNative(int deviceId) {
+ final int id = deviceId;
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ retrieveStreamConfigs(id);
+ mCallback.onStreamConfigurationChanged(id, mStreamConfigs);
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.tv;
+
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
+import android.tv.TvInputHardwareInfo;
+import android.tv.TvStreamConfig;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.view.KeyEvent;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A helper class for TvInputManagerService to handle TV input hardware.
+ *
+ * This class does a basic connection management and forwarding calls to TvInputHal which eventually
+ * calls to tv_input HAL module.
+ *
+ * @hide
+ */
+class TvInputHardwareManager implements TvInputHal.Callback {
+ private static final String TAG = TvInputHardwareManager.class.getSimpleName();
+ private final TvInputHal mHal = new TvInputHal(this);
+ private final SparseArray<Connection> mConnections = new SparseArray<Connection>();
+ private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>();
+ private final Context mContext;
+ private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
+
+ private final Object mLock = new Object();
+
+ public TvInputHardwareManager(Context context) {
+ mContext = context;
+ // TODO(hdmi): mHdmiManager = mContext.getSystemService(...);
+ // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient();
+ mHal.init();
+ }
+
+ @Override
+ public void onDeviceAvailable(
+ TvInputHardwareInfo info, TvStreamConfig[] configs) {
+ synchronized (mLock) {
+ Connection connection = new Connection(info);
+ connection.updateConfigsLocked(configs);
+ mConnections.put(info.getDeviceId(), connection);
+ buildInfoListLocked();
+ // TODO: notify if necessary
+ }
+ }
+
+ private void buildInfoListLocked() {
+ mInfoList.clear();
+ for (int i = 0; i < mConnections.size(); ++i) {
+ mInfoList.add(mConnections.valueAt(i).getInfoLocked());
+ }
+ }
+
+ @Override
+ public void onDeviceUnavailable(int deviceId) {
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "onDeviceUnavailable: Cannot find a connection with " + deviceId);
+ return;
+ }
+ connection.resetLocked(null, null, null, null);
+ mConnections.remove(deviceId);
+ buildInfoListLocked();
+ // TODO: notify if necessary
+ }
+ }
+
+ @Override
+ public void onStreamConfigurationChanged(int deviceId, TvStreamConfig[] configs) {
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "StreamConfigurationChanged: Cannot find a connection with "
+ + deviceId);
+ return;
+ }
+ connection.updateConfigsLocked(configs);
+ try {
+ connection.getCallbackLocked().onStreamConfigChanged(configs);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "onStreamConfigurationChanged: " + e);
+ }
+ }
+ }
+
+ public List<TvInputHardwareInfo> getHardwareList() {
+ synchronized (mLock) {
+ return mInfoList;
+ }
+ }
+
+ /**
+ * Create a TvInputHardware object with a specific deviceId. One service at a time can access
+ * the object, and if more than one process attempts to create hardware with the same deviceId,
+ * the latest service will get the object and all the other hardware are released. The
+ * release is notified via ITvInputHardwareCallback.onReleased().
+ */
+ public ITvInputHardware acquireHardware(int deviceId, ITvInputHardwareCallback callback,
+ int callingUid, int resolvedUserId) {
+ if (callback == null) {
+ throw new NullPointerException();
+ }
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "Invalid deviceId : " + deviceId);
+ return null;
+ }
+ if (connection.getCallingUidLocked() != callingUid
+ || connection.getResolvedUserIdLocked() != resolvedUserId) {
+ TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
+ try {
+ callback.asBinder().linkToDeath(connection, 0);
+ } catch (RemoteException e) {
+ hardware.release();
+ return null;
+ }
+ connection.resetLocked(hardware, callback, callingUid, resolvedUserId);
+ }
+ return connection.getHardwareLocked();
+ }
+ }
+
+ /**
+ * Release the specified hardware.
+ */
+ public void releaseHardware(int deviceId, ITvInputHardware hardware, int callingUid,
+ int resolvedUserId) {
+ synchronized (mLock) {
+ Connection connection = mConnections.get(deviceId);
+ if (connection == null) {
+ Slog.e(TAG, "Invalid deviceId : " + deviceId);
+ return;
+ }
+ if (connection.getHardwareLocked() != hardware
+ || connection.getCallingUidLocked() != callingUid
+ || connection.getResolvedUserIdLocked() != resolvedUserId) {
+ return;
+ }
+ connection.resetLocked(null, null, null, null);
+ }
+ }
+
+ private class Connection implements IBinder.DeathRecipient {
+ private final TvInputHardwareInfo mInfo;
+ private TvInputHardwareImpl mHardware = null;
+ private ITvInputHardwareCallback mCallback;
+ private TvStreamConfig[] mConfigs = null;
+ private Integer mCallingUid = null;
+ private Integer mResolvedUserId = null;
+
+ public Connection(TvInputHardwareInfo info) {
+ mInfo = info;
+ }
+
+ // *Locked methods assume TvInputHardwareManager.mLock is held.
+
+ public void resetLocked(TvInputHardwareImpl hardware,
+ ITvInputHardwareCallback callback, Integer callingUid, Integer resolvedUserId) {
+ if (mHardware != null) {
+ try {
+ mCallback.onReleased();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Connection::resetHardware: " + e);
+ }
+ mHardware.release();
+ }
+ mHardware = hardware;
+ mCallback = callback;
+ mCallingUid = callingUid;
+ mResolvedUserId = resolvedUserId;
+
+ if (mHardware != null && mCallback != null) {
+ try {
+ mCallback.onStreamConfigChanged(getConfigsLocked());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Connection::resetHardware: " + e);
+ }
+ }
+ }
+
+ public void updateConfigsLocked(TvStreamConfig[] configs) {
+ mConfigs = configs;
+ }
+
+ public TvInputHardwareInfo getInfoLocked() {
+ return mInfo;
+ }
+
+ public ITvInputHardware getHardwareLocked() {
+ return mHardware;
+ }
+
+ public ITvInputHardwareCallback getCallbackLocked() {
+ return mCallback;
+ }
+
+ public TvStreamConfig[] getConfigsLocked() {
+ return mConfigs;
+ }
+
+ public int getCallingUidLocked() {
+ return mCallingUid;
+ }
+
+ public int getResolvedUserIdLocked() {
+ return mResolvedUserId;
+ }
+
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ resetLocked(null, null, null, null);
+ }
+ }
+ }
+
+ private class TvInputHardwareImpl extends ITvInputHardware.Stub {
+ private final TvInputHardwareInfo mInfo;
+ private boolean mReleased = false;
+ private final Object mImplLock = new Object();
+
+ public TvInputHardwareImpl(TvInputHardwareInfo info) {
+ mInfo = info;
+ }
+
+ public void release() {
+ synchronized (mImplLock) {
+ mReleased = true;
+ }
+ }
+
+ @Override
+ public boolean setSurface(Surface surface, TvStreamConfig config)
+ throws RemoteException {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ throw new IllegalStateException("Device already released.");
+ }
+ if (mInfo.getType() == TvInputHal.TYPE_HDMI) {
+ if (surface != null) {
+ // Set "Active Source" for HDMI.
+ // TODO(hdmi): mHdmiClient.deviceSelect(...);
+ mActiveHdmiSources.add(mInfo.getDeviceId());
+ } else {
+ mActiveHdmiSources.remove(mInfo.getDeviceId());
+ if (mActiveHdmiSources.size() == 0) {
+ // Tell HDMI that no HDMI source is active
+ // TODO(hdmi): mHdmiClient.portSelect(null);
+ }
+ }
+ }
+ return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS;
+ }
+ }
+
+ @Override
+ public void setVolume(float volume) throws RemoteException {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ throw new IllegalStateException("Device already released.");
+ }
+ }
+ // TODO
+ }
+
+ @Override
+ public boolean dispatchKeyEventToHdmi(KeyEvent event) throws RemoteException {
+ synchronized (mImplLock) {
+ if (mReleased) {
+ throw new IllegalStateException("Device already released.");
+ }
+ }
+ if (mInfo.getType() != TvInputHal.TYPE_HDMI) {
+ return false;
+ }
+ // TODO(hdmi): mHdmiClient.sendKeyEvent(event);
+ return false;
+ }
+ }
+}
import android.os.UserHandle;
import android.provider.TvContract;
import android.tv.ITvInputClient;
+import android.tv.ITvInputHardware;
+import android.tv.ITvInputHardwareCallback;
import android.tv.ITvInputManager;
import android.tv.ITvInputService;
import android.tv.ITvInputServiceCallback;
import android.tv.ITvInputSession;
import android.tv.ITvInputSessionCallback;
+import android.tv.TvInputHardwareInfo;
import android.tv.TvInputInfo;
import android.tv.TvInputService;
import android.util.Slog;
private static final String TAG = "TvInputManagerService";
private final Context mContext;
+ private final TvInputHardwareManager mTvInputHardwareManager;
private final ContentResolver mContentResolver;
mContentResolver = context.getContentResolver();
mLogHandler = new LogHandler(IoThread.get().getLooper());
+ mTvInputHardwareManager = new TvInputHardwareManager(context);
registerBroadcastReceivers();
synchronized (mLock) {
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public List<TvInputHardwareInfo> getHardwareList() throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mTvInputHardwareManager.getHardwareList();
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public ITvInputHardware acquireTvInputHardware(int deviceId,
+ ITvInputHardwareCallback callback, int userId) throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return null;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "acquireTvInputHardware");
+ try {
+ return mTvInputHardwareManager.acquireHardware(
+ deviceId, callback, callingUid, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void releaseTvInputHardware(int deviceId, ITvInputHardware hardware, int userId)
+ throws RemoteException {
+ if (mContext.checkCallingPermission(
+ android.Manifest.permission.TV_INPUT_HARDWARE)
+ != PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+
+ final long identity = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), callingUid,
+ userId, "releaseTvInputHardware");
+ try {
+ mTvInputHardwareManager.releaseHardware(
+ deviceId, hardware, callingUid, resolvedUserId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
}
private static final class UserState {
$(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
$(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
--- /dev/null
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "TvInputHal"
+
+//#define LOG_NDEBUG 0
+
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
+#include "JNIHelp.h"
+#include "jni.h"
+
+#include <gui/Surface.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/Log.h>
+#include <utils/NativeHandle.h>
+#include <hardware/tv_input.h>
+
+namespace android {
+
+static struct {
+ jmethodID deviceAvailable;
+ jmethodID deviceUnavailable;
+ jmethodID streamConfigsChanged;
+} gTvInputHalClassInfo;
+
+static struct {
+ jclass clazz;
+} gTvStreamConfigClassInfo;
+
+static struct {
+ jclass clazz;
+
+ jmethodID constructor;
+ jmethodID streamId;
+ jmethodID type;
+ jmethodID maxWidth;
+ jmethodID maxHeight;
+ jmethodID generation;
+ jmethodID build;
+} gTvStreamConfigBuilderClassInfo;
+
+////////////////////////////////////////////////////////////////////////////////
+
+class JTvInputHal {
+public:
+ ~JTvInputHal();
+
+ 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:
+ class Connection {
+ public:
+ Connection() : mStreamId(0) {}
+
+ sp<Surface> mSurface;
+ sp<NativeHandle> mSourceHandle;
+ int mStreamId;
+ };
+
+ JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* dev);
+
+ static void notify(
+ tv_input_device_t* dev,tv_input_event_t* event, void* data);
+
+ void onDeviceAvailable(const tv_input_device_info_t& info);
+ void onDeviceUnavailable(int deviceId);
+ void onStreamConfigurationsChanged(int deviceId);
+
+ jweak mThiz;
+ tv_input_device_t* mDevice;
+ tv_input_callback_ops_t mCallback;
+
+ KeyedVector<int, Connection> mConnections;
+};
+
+JTvInputHal::JTvInputHal(JNIEnv* env, jobject thiz, tv_input_device_t* device) {
+ mThiz = env->NewWeakGlobalRef(thiz);
+ mDevice = device;
+ mCallback.notify = &JTvInputHal::notify;
+
+ mDevice->initialize(mDevice, &mCallback, this);
+}
+
+JTvInputHal::~JTvInputHal() {
+ mDevice->common.close((hw_device_t*)mDevice);
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ env->DeleteWeakGlobalRef(mThiz);
+ mThiz = NULL;
+}
+
+JTvInputHal* JTvInputHal::createInstance(JNIEnv* env, jobject thiz) {
+ tv_input_module_t* module = NULL;
+ status_t err = hw_get_module(TV_INPUT_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&module);
+ if (err) {
+ ALOGE("Couldn't load %s module (%s)",
+ TV_INPUT_HARDWARE_MODULE_ID, strerror(-err));
+ return 0;
+ }
+
+ tv_input_device_t* device = NULL;
+ err = module->common.methods->open(
+ (hw_module_t*)module,
+ TV_INPUT_DEFAULT_DEVICE,
+ (hw_device_t**)&device);
+ if (err) {
+ ALOGE("Couldn't open %s device (%s)",
+ TV_INPUT_DEFAULT_DEVICE, strerror(-err));
+ return 0;
+ }
+
+ return new JTvInputHal(env, thiz, device);
+}
+
+int JTvInputHal::setSurface(int deviceId, int streamId, const sp<Surface>& surface) {
+ Connection& connection = mConnections.editValueFor(deviceId);
+ if (connection.mStreamId == streamId && connection.mSurface == surface) {
+ // Nothing to do
+ return NO_ERROR;
+ }
+ if (Surface::isValid(connection.mSurface)) {
+ connection.mSurface.clear();
+ }
+ if (surface == NULL) {
+ if (connection.mSurface != NULL) {
+ connection.mSurface->setSidebandStream(NULL);
+ connection.mSurface.clear();
+ }
+ if (connection.mSourceHandle != NULL) {
+ // Need to reset streams
+ if (mDevice->close_stream(
+ mDevice, deviceId, connection.mStreamId) != 0) {
+ ALOGE("Couldn't remove stream");
+ return BAD_VALUE;
+ }
+ connection.mSourceHandle.clear();
+ }
+ return NO_ERROR;
+ }
+ connection.mSurface = surface;
+ if (connection.mSourceHandle == NULL) {
+ // Need to configure stream
+ int numConfigs = 0;
+ const tv_stream_config_t* configs = NULL;
+ if (mDevice->get_stream_configurations(
+ mDevice, deviceId, &numConfigs, &configs) != 0) {
+ ALOGE("Couldn't get stream configs");
+ return UNKNOWN_ERROR;
+ }
+ int configIndex = -1;
+ for (int i = 0; i < numConfigs; ++i) {
+ if (configs[i].stream_id == streamId) {
+ configIndex = i;
+ break;
+ }
+ }
+ if (configIndex == -1) {
+ ALOGE("Cannot find a config with given stream ID: %d", streamId);
+ return BAD_VALUE;
+ }
+ // TODO: handle buffer producer profile.
+ if (configs[configIndex].type !=
+ TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
+ ALOGE("Profiles other than independent video source is not yet "
+ "supported : type = %d", configs[configIndex].type);
+ return INVALID_OPERATION;
+ }
+ tv_stream_t stream;
+ stream.stream_id = configs[configIndex].stream_id;
+ if (mDevice->open_stream(mDevice, deviceId, &stream) != 0) {
+ ALOGE("Couldn't add stream");
+ return UNKNOWN_ERROR;
+ }
+ connection.mSourceHandle = NativeHandle::create(
+ stream.sideband_stream_source_handle, false);
+ connection.mStreamId = stream.stream_id;
+ connection.mSurface->setSidebandStream(connection.mSourceHandle);
+ }
+ return NO_ERROR;
+}
+
+const tv_stream_config_t* JTvInputHal::getStreamConfigs(int deviceId, int* numConfigs) {
+ const tv_stream_config_t* configs = NULL;
+ if (mDevice->get_stream_configurations(
+ mDevice, deviceId, numConfigs, &configs) != 0) {
+ ALOGE("Couldn't get stream configs");
+ return NULL;
+ }
+ return configs;
+}
+
+
+// static
+void JTvInputHal::notify(
+ tv_input_device_t* dev, tv_input_event_t* event, void* data) {
+ JTvInputHal* thiz = (JTvInputHal*)data;
+ switch (event->type) {
+ case TV_INPUT_EVENT_DEVICE_AVAILABLE: {
+ thiz->onDeviceAvailable(event->device_info);
+ } break;
+ case TV_INPUT_EVENT_DEVICE_UNAVAILABLE: {
+ thiz->onDeviceUnavailable(event->device_info.device_id);
+ } break;
+ case TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED: {
+ thiz->onStreamConfigurationsChanged(event->device_info.device_id);
+ } break;
+ default:
+ ALOGE("Unrecognizable event");
+ }
+}
+
+void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ mConnections.add(info.device_id, Connection());
+ env->CallVoidMethod(
+ mThiz,
+ gTvInputHalClassInfo.deviceAvailable,
+ info.device_id,
+ info.type);
+}
+
+void JTvInputHal::onDeviceUnavailable(int deviceId) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ mConnections.removeItem(deviceId);
+ env->CallVoidMethod(
+ mThiz,
+ gTvInputHalClassInfo.deviceUnavailable,
+ deviceId);
+}
+
+void JTvInputHal::onStreamConfigurationsChanged(int deviceId) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ mConnections.removeItem(deviceId);
+ env->CallVoidMethod(
+ mThiz,
+ gTvInputHalClassInfo.streamConfigsChanged,
+ deviceId);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static jlong nativeOpen(JNIEnv* env, jobject thiz) {
+ return (jlong)JTvInputHal::createInstance(env, thiz);
+}
+
+static int nativeSetSurface(JNIEnv* env, jclass clazz,
+ jlong ptr, jint deviceId, jint streamId, jobject jsurface) {
+ JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+ sp<Surface> surface(
+ jsurface
+ ? android_view_Surface_getSurface(env, jsurface)
+ : NULL);
+ return tvInputHal->setSurface(deviceId, streamId, surface);
+}
+
+static jobjectArray nativeGetStreamConfigs(JNIEnv* env, jclass clazz,
+ jlong ptr, jint deviceId, jint generation) {
+ JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+ int numConfigs = 0;
+ const tv_stream_config_t* configs = tvInputHal->getStreamConfigs(deviceId, &numConfigs);
+
+ jobjectArray result = env->NewObjectArray(numConfigs, gTvStreamConfigClassInfo.clazz, NULL);
+ for (int i = 0; i < numConfigs; ++i) {
+ jobject builder = env->NewObject(
+ gTvStreamConfigBuilderClassInfo.clazz,
+ gTvStreamConfigBuilderClassInfo.constructor);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.streamId, configs[i].stream_id);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.type, configs[i].type);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.maxWidth, configs[i].max_video_width);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.maxHeight, configs[i].max_video_height);
+ env->CallObjectMethod(
+ builder, gTvStreamConfigBuilderClassInfo.generation, generation);
+
+ jobject config = env->CallObjectMethod(builder, gTvStreamConfigBuilderClassInfo.build);
+
+ env->SetObjectArrayElement(result, i, config);
+
+ env->DeleteLocalRef(config);
+ env->DeleteLocalRef(builder);
+ }
+ return result;
+}
+
+static void nativeClose(JNIEnv* env, jclass clazz, jlong ptr) {
+ JTvInputHal* tvInputHal = (JTvInputHal*)ptr;
+ delete tvInputHal;
+}
+
+static JNINativeMethod gTvInputHalMethods[] = {
+ /* name, signature, funcPtr */
+ { "nativeOpen", "()J",
+ (void*) nativeOpen },
+ { "nativeSetSurface", "(JIILandroid/view/Surface;)I",
+ (void*) nativeSetSurface },
+ { "nativeGetStreamConfigs", "(JII)[Landroid/tv/TvStreamConfig;",
+ (void*) nativeGetStreamConfigs },
+ { "nativeClose", "(J)V",
+ (void*) nativeClose },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className)
+
+#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
+ var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find method" methodName)
+
+int register_android_server_tv_TvInputHal(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/tv/TvInputHal",
+ gTvInputHalMethods, NELEM(gTvInputHalMethods));
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+ jclass clazz;
+ FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
+
+ GET_METHOD_ID(
+ gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V");
+ GET_METHOD_ID(
+ gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
+ GET_METHOD_ID(
+ gTvInputHalClassInfo.streamConfigsChanged, clazz,
+ "streamConfigsChangedFromNative", "(I)V");
+
+ FIND_CLASS(gTvStreamConfigClassInfo.clazz, "android/tv/TvStreamConfig");
+ gTvStreamConfigClassInfo.clazz = jclass(env->NewGlobalRef(gTvStreamConfigClassInfo.clazz));
+
+ FIND_CLASS(gTvStreamConfigBuilderClassInfo.clazz, "android/tv/TvStreamConfig$Builder");
+ gTvStreamConfigBuilderClassInfo.clazz =
+ jclass(env->NewGlobalRef(gTvStreamConfigBuilderClassInfo.clazz));
+
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.constructor,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "<init>", "()V");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.streamId,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "streamId", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.type,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "type", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.maxWidth,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "maxWidth", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.maxHeight,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "maxHeight", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.generation,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "generation", "(I)Landroid/tv/TvStreamConfig$Builder;");
+ GET_METHOD_ID(
+ gTvStreamConfigBuilderClassInfo.build,
+ gTvStreamConfigBuilderClassInfo.clazz,
+ "build", "()Landroid/tv/TvStreamConfig;");
+
+ return 0;
+}
+
+} /* namespace android */
int register_android_server_hdmi_HdmiCecController(JNIEnv* env);
int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
int register_android_server_hdmi_HdmiMhlController(JNIEnv* env);
+int register_android_server_tv_TvInputHal(JNIEnv* env);
};
using namespace android;
// TODO: remove this once replaces HdmiCecService with HdmiControlService.
register_android_server_hdmi_HdmiCecService(env);
register_android_server_hdmi_HdmiMhlController(env);
+ register_android_server_tv_TvInputHal(env);
return JNI_VERSION_1_4;
}