1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "RemoteDisplay" 18 19#include "jni.h" 20#include "JNIHelp.h" 21 22#include "android_os_Parcel.h" 23#include "android_util_Binder.h" 24 25#include <android_runtime/AndroidRuntime.h> 26#include <android_runtime/android_view_Surface.h> 27 28#include <binder/IServiceManager.h> 29 30#include <gui/ISurfaceTexture.h> 31 32#include <media/IMediaPlayerService.h> 33#include <media/IRemoteDisplay.h> 34#include <media/IRemoteDisplayClient.h> 35 36#include <utils/Log.h> 37 38#include <ScopedUtfChars.h> 39 40namespace android { 41 42static struct { 43 jmethodID notifyDisplayConnected; 44 jmethodID notifyDisplayDisconnected; 45 jmethodID notifyDisplayError; 46} gRemoteDisplayClassInfo; 47 48// ---------------------------------------------------------------------------- 49 50class NativeRemoteDisplayClient : public BnRemoteDisplayClient { 51public: 52 NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) : 53 mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) { 54 } 55 56protected: 57 ~NativeRemoteDisplayClient() { 58 JNIEnv* env = AndroidRuntime::getJNIEnv(); 59 env->DeleteGlobalRef(mRemoteDisplayObjGlobal); 60 } 61 62public: 63 virtual void onDisplayConnected(const sp<ISurfaceTexture>& surfaceTexture, 64 uint32_t width, uint32_t height, uint32_t flags) { 65 JNIEnv* env = AndroidRuntime::getJNIEnv(); 66 67 jobject surfaceObj = android_view_Surface_createFromISurfaceTexture(env, surfaceTexture); 68 if (surfaceObj == NULL) { 69 ALOGE("Could not create Surface from surface texture %p provided by media server.", 70 surfaceTexture.get()); 71 return; 72 } 73 74 env->CallVoidMethod(mRemoteDisplayObjGlobal, 75 gRemoteDisplayClassInfo.notifyDisplayConnected, 76 surfaceObj, width, height, flags); 77 env->DeleteLocalRef(surfaceObj); 78 checkAndClearExceptionFromCallback(env, "notifyDisplayConnected"); 79 } 80 81 virtual void onDisplayDisconnected() { 82 JNIEnv* env = AndroidRuntime::getJNIEnv(); 83 84 env->CallVoidMethod(mRemoteDisplayObjGlobal, 85 gRemoteDisplayClassInfo.notifyDisplayDisconnected); 86 checkAndClearExceptionFromCallback(env, "notifyDisplayDisconnected"); 87 } 88 89 virtual void onDisplayError(int32_t error) { 90 JNIEnv* env = AndroidRuntime::getJNIEnv(); 91 92 env->CallVoidMethod(mRemoteDisplayObjGlobal, 93 gRemoteDisplayClassInfo.notifyDisplayError, error); 94 checkAndClearExceptionFromCallback(env, "notifyDisplayError"); 95 } 96 97private: 98 jobject mRemoteDisplayObjGlobal; 99 100 static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { 101 if (env->ExceptionCheck()) { 102 ALOGE("An exception was thrown by callback '%s'.", methodName); 103 LOGE_EX(env); 104 env->ExceptionClear(); 105 } 106 } 107}; 108 109class NativeRemoteDisplay { 110public: 111 NativeRemoteDisplay(const sp<IRemoteDisplay>& display, 112 const sp<NativeRemoteDisplayClient>& client) : 113 mDisplay(display), mClient(client) { 114 } 115 116 ~NativeRemoteDisplay() { 117 mDisplay->dispose(); 118 } 119 120private: 121 sp<IRemoteDisplay> mDisplay; 122 sp<NativeRemoteDisplayClient> mClient; 123}; 124 125 126// ---------------------------------------------------------------------------- 127 128static jint nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr) { 129 ScopedUtfChars iface(env, ifaceStr); 130 131 sp<IServiceManager> sm = defaultServiceManager(); 132 sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>( 133 sm->getService(String16("media.player"))); 134 if (service == NULL) { 135 ALOGE("Could not obtain IMediaPlayerService from service manager"); 136 return 0; 137 } 138 139 sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj)); 140 sp<IRemoteDisplay> display = service->listenForRemoteDisplay( 141 client, String8(iface.c_str())); 142 if (display == NULL) { 143 ALOGE("Media player service rejected request to listen for remote display '%s'.", 144 iface.c_str()); 145 return 0; 146 } 147 148 NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client); 149 return reinterpret_cast<jint>(wrapper); 150} 151 152static void nativeDispose(JNIEnv* env, jobject remoteDisplayObj, jint ptr) { 153 NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr); 154 delete wrapper; 155} 156 157// ---------------------------------------------------------------------------- 158 159static JNINativeMethod gMethods[] = { 160 {"nativeListen", "(Ljava/lang/String;)I", 161 (void*)nativeListen }, 162 {"nativeDispose", "(I)V", 163 (void*)nativeDispose }, 164}; 165 166int register_android_media_RemoteDisplay(JNIEnv* env) 167{ 168 int err = AndroidRuntime::registerNativeMethods(env, "android/media/RemoteDisplay", 169 gMethods, NELEM(gMethods)); 170 171 jclass clazz = env->FindClass("android/media/RemoteDisplay"); 172 gRemoteDisplayClassInfo.notifyDisplayConnected = 173 env->GetMethodID(clazz, "notifyDisplayConnected", 174 "(Landroid/view/Surface;III)V"); 175 gRemoteDisplayClassInfo.notifyDisplayDisconnected = 176 env->GetMethodID(clazz, "notifyDisplayDisconnected", "()V"); 177 gRemoteDisplayClassInfo.notifyDisplayError = 178 env->GetMethodID(clazz, "notifyDisplayError", "(I)V"); 179 return err; 180} 181 182}; 183