RemoteDisplay其是具体Display的业务实现,其包含JAVA层与JNI,library三个层。
在android 4.2当中主要是WifiDisplayAdapter使用了RemoteDisplay。
也就是RemoteDisplay其是基础,WifiDisplayAdapter只是使用了其的功能!!!
pizza\frameworks\base\media\java\android\media\RemoteDisplay.java
private native int nativeListen(String iface);
private native int nativeStartRecord(String iface);
private native int nativePauseRecord(int ptr);
private native int nativeResumeRecord(int ptr);
private native int nativeStopRecord(int ptr);
private native void nativeDispose(int ptr);
public static RemoteDisplay recordScreen(String outfile, Listener listener, Handler handler) {
if (outfile == null) {
throw new IllegalArgumentException("iface must not be null");
}
if (listener == null) {
throw new IllegalArgumentException("listener must not be null");
}
if (handler == null) {
throw new IllegalArgumentException("handler must not be null");
}RemoteDisplay display = new RemoteDisplay(listener, handler);
display.startRecording(outfile);
return display;
}
private void startListening(String iface) {
mPtr = nativeListen(iface);
if (mPtr == 0) {
throw new IllegalStateException("Could not start listening for "
+ "remote display connection on \"" + iface + "\"");
}
mGuard.open("dispose");
mGuard.open("stopRecording");
}
public void startRecording(String outfile) {
Log.e(TAG, "### startRecording ###");
mPtr = nativeStartRecord(outfile);
if (mPtr == 0) {
throw new IllegalStateException("Could not start listening for "
+ "remote display connection on \"" + outfile + "\"");
}
}public void pauseRecording() {
Log.e(TAG, "### pauseRecording ###");
if(mPtr != 0)
{
nativePauseRecord(mPtr);
}
}
public void resumeRecording() {
Log.e(TAG, "### resumeRecording ###");
if(mPtr != 0)
{
nativeResumeRecord(mPtr);
}
}public void stopRecording() {
Log.e(TAG, "### stopRecording ###");
stopRecording(false);
}private void stopRecording(boolean finalized) {
if(mPtr != 0)
{
if (mGuardRecScr != null) {
if (finalized) {
mGuardRecScr.warnIfOpen();
} else {
mGuardRecScr.close();
}
}
nativeStopRecord(mPtr);
mPtr = 0;
}
}// Called from native.
private void notifyRecordScreen(final byte[] buffer, final int len) {
Log.e(TAG, "### notifyRecordScreen ###");
mHandler.post(new Runnable() {
@Override
public void run() {
mListener.onRecordScreen(buffer, len);
}
});
}/**
* Listener invoked when the remote display connection changes state.
*/
public interface Listener {
void onDisplayConnected(Surface surface, int width, int height, int flags);
void onDisplayDisconnected();
void onDisplayError(int error);
void onRecordScreen(byte[] buffer, int len);
}
pizza\frameworks\base\core\jni\android_media_RemoteDisplay.cpp
namespace android {
static struct {
jmethodID notifyDisplayConnected;
jmethodID notifyDisplayDisconnected;
jmethodID notifyDisplayError;
jmethodID notifyRecordScreen;
} gRemoteDisplayClassInfo;// ----------------------------------------------------------------------------
class NativeRemoteDisplayClient : public BnRemoteDisplayClient {
public:
NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) {
}protected:
~NativeRemoteDisplayClient() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
}public:
virtual void onDisplayConnected(const sp<ISurfaceTexture>& surfaceTexture,
uint32_t width, uint32_t height, uint32_t flags) {
JNIEnv* env = AndroidRuntime::getJNIEnv();jobject surfaceObj = android_view_Surface_createFromISurfaceTexture(env, surfaceTexture);
if (surfaceObj == NULL) {
ALOGE("Could not create Surface from surface texture %p provided by media server.",
surfaceTexture.get());
return;
}env->CallVoidMethod(mRemoteDisplayObjGlobal,
gRemoteDisplayClassInfo.notifyDisplayConnected,
surfaceObj, width, height, flags);
env->DeleteLocalRef(surfaceObj);
checkAndClearExceptionFromCallback(env, "notifyDisplayConnected");
}virtual void onDisplayDisconnected() {
JNIEnv* env = AndroidRuntime::getJNIEnv();env->CallVoidMethod(mRemoteDisplayObjGlobal,
gRemoteDisplayClassInfo.notifyDisplayDisconnected);
checkAndClearExceptionFromCallback(env, "notifyDisplayDisconnected");
}virtual void onDisplayError(int32_t error) {
JNIEnv* env = AndroidRuntime::getJNIEnv();env->CallVoidMethod(mRemoteDisplayObjGlobal,
gRemoteDisplayClassInfo.notifyDisplayError, error);
checkAndClearExceptionFromCallback(env, "notifyDisplayError");
}virtual void onRecordScreen(char *buffer, int len) {
}private:
jobject mRemoteDisplayObjGlobal;static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
ALOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
}
}
};class NativeRemoteDisplay {
public:
NativeRemoteDisplay(const sp<IRemoteDisplay>& display,
const sp<NativeRemoteDisplayClient>& client) :
mDisplay(display), mClient(client) {
}~NativeRemoteDisplay() {
mDisplay->dispose();
}private:
sp<IRemoteDisplay> mDisplay;
sp<NativeRemoteDisplayClient> mClient;
};// ----------------------------------------------------------------------------
struct RemoteDisplayClient : public BnRemoteDisplayClient {
RemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj);virtual void onDisplayConnected(
const sp<ISurfaceTexture> &surfaceTexture,
uint32_t width,
uint32_t height,
uint32_t flags);virtual void onDisplayDisconnected();
virtual void onDisplayError(int32_t error);
virtual void onRecordScreen(char *buffer, int len);void waitUntilDone();
protected:
virtual ~RemoteDisplayClient();private:
JNIEnv* mEnv;Mutex mLock;
Condition mCondition;bool mDone;
// 有了这个几个Display才能够真正的镜像显示数据
sp<SurfaceComposerClient> mComposerClient;
sp<ISurfaceTexture> mSurfaceTexture;
sp<IBinder> mDisplayBinder;
jobject mRemoteDisplayObjGlobal;
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
ALOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
}
}DISALLOW_EVIL_CONSTRUCTORS(RemoteDisplayClient);
};static status_t enableAudioSubmix(bool enable) {
status_t err = AudioSystem::setDeviceConnectionState(
AUDIO_DEVICE_IN_REMOTE_SUBMIX,
enable
? AUDIO_POLICY_DEVICE_STATE_AVAILABLE
: AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
NULL /* device_address */);if (err != OK) {
return err;
}err = AudioSystem::setDeviceConnectionState(
AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
enable
? AUDIO_POLICY_DEVICE_STATE_AVAILABLE
: AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
NULL /* device_address */);return err;
}RemoteDisplayClient::RemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
mEnv(env),
mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)),
mDone(false) {
mComposerClient = new SurfaceComposerClient;
mComposerClient->initCheck();
//CHECK_EQ(mComposerClient->initCheck(), (status_t)OK);
}RemoteDisplayClient::~RemoteDisplayClient() {
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
}void RemoteDisplayClient::onDisplayConnected(
const sp<ISurfaceTexture> &surfaceTexture,
uint32_t width,
uint32_t height,
uint32_t flags) {
ALOGI("#### onDisplayConnected width=%u, height=%u, flags = 0x%08x",
width, height, flags);
if(999 == flags)
{
onRecordScreen((char *)width, (int)height);
return;
}
mSurfaceTexture = surfaceTexture;// 内部通过SurfaceComposerClient,最终调用SurfaceFlinger来创建一个DisplayDeviceState,添加到
// DisplayDeviceState info(DisplayDevice::DISPLAY_VIRTUAL); –> 所有createDisplay均是DISPLAY_VIRTUAL类型
//mCurrentState.displays.add(token,info); 在SurfaceFlinger当中添加了一个显示设备,SurfaceFlinger就会将这些数据
// 写到这些显示设备上面
mDisplayBinder = mComposerClient->createDisplay(
String8("foo"), false /* secure */);SurfaceComposerClient::openGlobalTransaction();
// 修改内部DisplayState 定义在LayerState.h当中
// DisplayState &s(getDisplayStateLocked(token));
//s.surface = bufferProducer;
//s.what |= DisplayState::eSurfaceChanged;
mComposerClient->setDisplaySurface(mDisplayBinder, mSurfaceTexture);//Rect layerStackRect(1280, 720); // XXX fix this.
Rect layerStackRect(width, height); // XXX fix this.
//Rect displayRect(1280, 720);
Rect displayRect(width, height);mComposerClient->setDisplayProjection(
mDisplayBinder, 0 /* 0 degree rotation */,
layerStackRect,
displayRect);SurfaceComposerClient::closeGlobalTransaction();
}void RemoteDisplayClient::onDisplayDisconnected() {
enableAudioSubmix(false /* enable */);
//Mutex::Autolock autoLock(mLock);
//mDone = true;
ALOGD("onDisplayDisconnected");
//mCondition.broadcast();
}void RemoteDisplayClient::onDisplayError(int32_t error) {
ALOGI("onDisplayError error=%d", error);Mutex::Autolock autoLock(mLock);
mDone = true;
mCondition.broadcast();
}void RemoteDisplayClient::onRecordScreen(char *buffer, int len) {
//JNIEnv* env = AndroidRuntime::getJNIEnv();
JNIEnv* env = mEnv;
jbyteArray data = env->NewByteArray(len);
env->SetByteArrayRegion(data, 0, len,
reinterpret_cast<const jbyte*>(buffer));ALOGE("### onRecordScreen in android_media_RemoteDisplay.cpp ###");
env->CallVoidMethod(mRemoteDisplayObjGlobal,
gRemoteDisplayClassInfo.notifyRecordScreen, data, len);
checkAndClearExceptionFromCallback(env, "notifyRecordScreen");
}void RemoteDisplayClient::waitUntilDone() {
Mutex::Autolock autoLock(mLock);
while (!mDone) {
mCondition.wait(mLock);
}
}class NativeRemoteDisplayEx {
public:
NativeRemoteDisplayEx(const sp<IRemoteDisplay>& display,
const sp<RemoteDisplayClient>& client) :
mDisplay(display), mClient(client) {
}~NativeRemoteDisplayEx()
{
ALOGI("### ~NativeRemoteDisplayEx 01 ###");
if(mDisplay != NULL)
{
ALOGI("### ~NativeRemoteDisplayEx 02 ###");
mDisplay->dispose();
}
}void pauseRecord()
{
ALOGI("### pauseRecord 01 ###");
if(mDisplay != NULL)
{
ALOGI("### pauseRecord 02 ###");
mDisplay->pause();
}
}void resumeRecord()
{
ALOGI("### resumeRecord 01 ###");
if(mDisplay != NULL)
{
ALOGI("### resumeRecord 02 ###");
mDisplay->resume();
}
}void stopRecord()
{
ALOGI("### stopRecord 01 ###");
if(mDisplay != NULL)
{
ALOGI("### stopRecord 02 ###");
mDisplay->dispose();
}
}private:
sp<IRemoteDisplay> mDisplay;
sp<RemoteDisplayClient> mClient;
};static jint nativeStartRecord(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
ScopedUtfChars iface(env, ifaceStr);
ALOGI("### nativeStartRecord ###");
sp<IServiceManager> sm = defaultServiceManager();
sp<IBinder> binder = sm->getService(String16("media.player"));
sp<IMediaPlayerService> service =
interface_cast<IMediaPlayerService>(binder);CHECK(service.get() != NULL);
enableAudioSubmix(true /* enable */);
sp<RemoteDisplayClient> client = new RemoteDisplayClient(env, remoteDisplayObj);
sp<IRemoteDisplay> display = service->listenForRemoteDisplay(client, String8(iface.c_str()));#if 0 //{
if(wrapper != NULL)
{
//delete wrapper;
//wrapper = NULL;
}
#endif //}
NativeRemoteDisplayEx* wrapper = new NativeRemoteDisplayEx(display, client);return reinterpret_cast<jint>(wrapper);
}static jint nativePauseRecord(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
ALOGI("### nativePauseRecord ###");
NativeRemoteDisplayEx* wrapper = reinterpret_cast<NativeRemoteDisplayEx*>(ptr);
wrapper->pauseRecord();
return 0;
}static jint nativeResumeRecord(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
ALOGI("### nativeResumeRecord ###");
NativeRemoteDisplayEx* wrapper = reinterpret_cast<NativeRemoteDisplayEx*>(ptr);
wrapper->resumeRecord();
return 0;
}static jint nativeStopRecord(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
ALOGI("### nativeStopRecord ###");
NativeRemoteDisplayEx* wrapper = reinterpret_cast<NativeRemoteDisplayEx*>(ptr);
delete wrapper;
//wrapper->stopRecord();
return 0;
}static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) {
ScopedUtfChars iface(env, ifaceStr);sp<IServiceManager> sm = defaultServiceManager();
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(
sm->getService(String16("media.player")));
if (service == NULL) {
ALOGE("Could not obtain IMediaPlayerService from service manager");
return 0;
}sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj));
sp<IRemoteDisplay> display = service->listenForRemoteDisplay(
client, String8(iface.c_str()));
if (display == NULL) {
ALOGE("Media player service rejected request to listen for remote display '%s'.",
iface.c_str());
return 0;
}NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);
return reinterpret_cast<jint>(wrapper);
}static void nativeDispose(JNIEnv* env, jobject remoteDisplayObj, jint ptr) {
NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
delete wrapper;
}// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
{"nativeListen", "(Ljava/lang/String;)I",
(void*)nativeListen },
{"nativeStartRecord", "(Ljava/lang/String;)I",
(void*)nativeStartRecord },
{"nativePauseRecord", "(I)I",
(void*)nativePauseRecord },
{"nativeResumeRecord", "(I)I",
(void*)nativeResumeRecord },
{"nativeStopRecord", "(I)I",
(void*)nativeStopRecord },
{"nativeDispose", "(I)V",
(void*)nativeDispose },
};int register_android_media_RemoteDisplay(JNIEnv* env)
{
int err = AndroidRuntime::registerNativeMethods(env, "android/media/RemoteDisplay",
gMethods, NELEM(gMethods));jclass clazz = env->FindClass("android/media/RemoteDisplay");
gRemoteDisplayClassInfo.notifyDisplayConnected =
env->GetMethodID(clazz, "notifyDisplayConnected",
"(Landroid/view/Surface;III)V");
gRemoteDisplayClassInfo.notifyDisplayDisconnected =
env->GetMethodID(clazz, "notifyDisplayDisconnected", "()V");
gRemoteDisplayClassInfo.notifyDisplayError =
env->GetMethodID(clazz, "notifyDisplayError", "(I)V");
gRemoteDisplayClassInfo.notifyRecordScreen =
env->GetMethodID(clazz, "notifyRecordScreen", "([BI)V");
return err;
}
pizza\frameworks\base\core\java\android\hardware\display\IDisplayManager.aidl
DisplayManager负责管理所有的Display对象,给JAVA用户负责使用的接口
// No permissions required.
void startScreenRecord(String outfile);// No permissions required.
void pauseScreenRecord();// No permissions required.
void resumeScreenRecord();// No permissions required.
void stopScreenRecord();// No permissions required.
boolean canRecordScreen();// No permissions required.
boolean canUseWfd();
pizza\frameworks\base\services\java\com\android\server\display\DisplayManagerService.java
public void startScreenRecord(String outfile) {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestStartRecordScreenLocked(outfile);
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}@Override // Binder call
public void pauseScreenRecord() {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestPauseRecordScreenLocked();
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override // Binder call
public void resumeScreenRecord() {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestResumeRecordScreenLocked();
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}@Override // Binder call
public void stopScreenRecord() {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
if (mWifiDisplayAdapter != null) {
mWifiDisplayAdapter.requestStopRecordScreenLocked();
}
}
} finally {
Binder.restoreCallingIdentity(token);
}
}@Override // Binder call
public boolean canRecordScreen() {
if (mWifiDisplayAdapter != null) {
return mWifiDisplayAdapter.requestCanRecordScreenLocked();
}
return false;
}@Override // Binder call
public boolean canUseWfd() {
if (mWifiDisplayAdapter != null) {
return mWifiDisplayAdapter.requestCanUseWfdLocked();
}
return false;
}@Override // Binder call
RemoteDisplay 其被有三个部分WifiDisplayAdapter,WifiDisplayController.java, WifiDisplayDevice集成起来使用。
pizza\frameworks\base\services\java\com\android\server\display\WifiDisplayAdapter.java
public void requestStartRecordScreenLocked(final String outfile) {
if (DEBUG) {
Slog.d(TAG, "requestStartRecordScreenLocked");
}getHandler().post(new Runnable() {
@Override
public void run() {
if (mDisplayController != null) {
mDisplayController.requestStartRecordScreen(outfile);
}
}
});
}public void requestPauseRecordScreenLocked() {
if (DEBUG) {
Slog.d(TAG, "requestPauseRecordScreenLocked");
}getHandler().post(new Runnable() {
@Override
public void run() {
if (mDisplayController != null) {
mDisplayController.requestPauseRecordScreen();
}
}
});
}public void requestResumeRecordScreenLocked() {
if (DEBUG) {
Slog.d(TAG, "requestResumeRecordScreenLocked");
}getHandler().post(new Runnable() {
@Override
public void run() {
if (mDisplayController != null) {
mDisplayController.requestResumeRecordScreen();
}
}
});
}public void requestStopRecordScreenLocked() {
if (DEBUG) {
Slog.d(TAG, "requestStopRecordScreenLocked");
}getHandler().post(new Runnable() {
@Override
public void run() {
if (mDisplayController != null) {
mDisplayController.requestStopRecordScreen();
}
}
});
}public boolean requestCanRecordScreenLocked() {
if (mDisplayController != null) {
return mDisplayController.requestCanRecordScreen();
}
return false;
}public boolean requestCanUseWfdLocked() {
if (mDisplayController != null) {
return mDisplayController.requestCanUseWfd();
}
return false;
}
pizza\frameworks\base\services\java\com\android\server\display\WifiDisplayController.java
private static final int RECSCR_STATUS_UNKNOWN = -1;
private static final int RECSCR_STATUS_RECORDING = 1;
private static final int RECSCR_STATUS_PAUSED = 2;
private static final int RECSCR_STATUS_STOPPED = 3;private RemoteDisplay mRecordScreen;
public void requestStartRecordScreen(String outfile) {
if(RECSCR_STATUS_UNKNOWN == mRecordStatus || RECSCR_STATUS_STOPPED == mRecordStatus)
{
if(mRemoteDisplay != null)
{
Slog.e(TAG, "### requestStartRecordScreen mRemoteDisplay != null ###");
return;
}
Slog.d(TAG, "### requestStartRecordScreen outfile: " + outfile + " ###");
mRecordScreen = RemoteDisplay.recordScreen(outfile, new RemoteDisplay.Listener() {
@Override
public void onDisplayConnected(Surface surface,
int width, int height, int flags) {
Slog.d(TAG, "### onDisplayConnected ###");
}@Override
public void onDisplayDisconnected() {
Slog.d(TAG, "### onDisplayDisconnected ###");
}@Override
public void onDisplayError(int error) {
Slog.d(TAG, "### onDisplayError ###");
}@Override
public void onRecordScreen(byte[] buffer, int len) {
Slog.d(TAG, "### onRecordScreen ###");
}
}, mHandler);
mHandler.postDelayed(mRtspTimeout, RTSP_TIMEOUT_SECONDS * 1000 * 1000);
mRecordStatus = RECSCR_STATUS_RECORDING;
}
}public void requestPauseRecordScreen() {
if(RECSCR_STATUS_RECORDING == mRecordStatus && mRecordScreen != null)
{
Slog.d(TAG, "### requestPauseRecordScreen ###");
mRecordScreen.pauseRecording();
mRecordStatus = RECSCR_STATUS_PAUSED;
}
}public void requestResumeRecordScreen() {
if(RECSCR_STATUS_PAUSED == mRecordStatus && mRecordScreen != null)
{
Slog.d(TAG, "### requestResumeRecordScreen ###");
mRecordScreen.resumeRecording();
mRecordStatus = RECSCR_STATUS_RECORDING;
}
}public void requestStopRecordScreen() {
if(RECSCR_STATUS_RECORDING == mRecordStatus && mRecordScreen != null)
{
Slog.d(TAG, "### requestStopRecordScreen ###");
mRecordScreen.stopRecording();
mRecordStatus = RECSCR_STATUS_STOPPED;
mRecordScreen = null;
}
}public boolean requestCanRecordScreen() {
Slog.d(TAG, "### requestCanRecordScreen ###");
if(mRemoteDisplay == null)
{
Slog.d(TAG, "### requestCanRecordScreen true ###");
return true;
}
return false;
}public boolean requestCanUseWfd() {
Slog.d(TAG, "### requestCanUseWfd ###");
if(mRecordScreen == null)
{
Slog.d(TAG, "### requestCanUseWfd true ###");
return true;
}
return false;
}