Revert "Revert "Add physical TV input handling to TvInputManagerService""

This reverts commit 1940e197a8de186df5edf0b78e0907ae539bd215.

Bug: 14118245, Bug: 15197740
Change-Id: Ia308f16d2ed8ec55112a4d21c180ccb97e8d7c6a
This commit is contained in:
Wonsik Kim
2014-05-26 02:26:04 +00:00
parent 41b170d606
commit c22dbb6919
15 changed files with 1271 additions and 0 deletions

View File

@ -216,6 +216,8 @@ LOCAL_SRC_FILES += \
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 \

View File

@ -0,0 +1,46 @@
/*
* 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);
}

View File

@ -0,0 +1,27 @@
/*
* 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);
}

View File

@ -19,7 +19,10 @@ package android.tv;
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;
@ -46,4 +49,10 @@ interface ITvInputManager {
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);
}

View File

@ -0,0 +1,20 @@
/*
*
* 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;

View File

@ -0,0 +1,93 @@
/*
* 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();
}
}

View File

@ -0,0 +1,20 @@
/*
*
* 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;

View File

@ -0,0 +1,157 @@
/*
* 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;
}
}
}

View File

@ -1014,6 +1014,13 @@
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 -->
<!-- =========================================== -->

View File

@ -0,0 +1,128 @@
/*
* 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);
}
});
}
}

View File

@ -0,0 +1,308 @@
/*
* 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;
}
}
}

View File

@ -42,11 +42,14 @@ import android.os.RemoteException;
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;
@ -71,6 +74,7 @@ public final class TvInputManagerService extends SystemService {
private static final String TAG = "TvInputManagerService";
private final Context mContext;
private final TvInputHardwareManager mTvInputHardwareManager;
private final ContentResolver mContentResolver;
@ -92,6 +96,7 @@ public final class TvInputManagerService extends SystemService {
mContentResolver = context.getContentResolver();
mLogHandler = new LogHandler(IoThread.get().getLooper());
mTvInputHardwareManager = new TvInputHardwareManager(context);
registerBroadcastReceivers();
synchronized (mLock) {
@ -730,6 +735,64 @@ public final class TvInputManagerService extends SystemService {
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 {

View File

@ -23,6 +23,7 @@ LOCAL_SRC_FILES += \
$(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 \

View File

@ -0,0 +1,388 @@
/*
* 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 */

View File

@ -41,6 +41,7 @@ int register_android_server_dreams_McuHal(JNIEnv* env);
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;
@ -78,6 +79,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
// 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;
}