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 "core_jni_helpers.h"
26 #include <android_runtime/android_view_Surface.h>
27 #include <android_runtime/Log.h>
28 
29 #include <binder/IServiceManager.h>
30 
31 #include <gui/IGraphicBufferProducer.h>
32 
33 #include <media/IMediaPlayerService.h>
34 #include <media/IRemoteDisplay.h>
35 #include <media/IRemoteDisplayClient.h>
36 
37 #include <utils/Log.h>
38 
39 #include <ScopedUtfChars.h>
40 
41 namespace android {
42 
43 static struct {
44     jmethodID notifyDisplayConnected;
45     jmethodID notifyDisplayDisconnected;
46     jmethodID notifyDisplayError;
47 } gRemoteDisplayClassInfo;
48 
49 // ----------------------------------------------------------------------------
50 
51 class NativeRemoteDisplayClient : public BnRemoteDisplayClient {
52 public:
NativeRemoteDisplayClient(JNIEnv * env,jobject remoteDisplayObj)53     NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
54             mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) {
55     }
56 
57 protected:
~NativeRemoteDisplayClient()58     ~NativeRemoteDisplayClient() {
59         JNIEnv* env = AndroidRuntime::getJNIEnv();
60         env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
61     }
62 
63 public:
onDisplayConnected(const sp<IGraphicBufferProducer> & bufferProducer,uint32_t width,uint32_t height,uint32_t flags,uint32_t session)64     virtual void onDisplayConnected(const sp<IGraphicBufferProducer>& bufferProducer,
65             uint32_t width, uint32_t height, uint32_t flags, uint32_t session) {
66         JNIEnv* env = AndroidRuntime::getJNIEnv();
67 
68         jobject surfaceObj = android_view_Surface_createFromIGraphicBufferProducer(env, bufferProducer);
69         if (surfaceObj == NULL) {
70             ALOGE("Could not create Surface from surface texture %p provided by media server.",
71                   bufferProducer.get());
72             return;
73         }
74 
75         env->CallVoidMethod(mRemoteDisplayObjGlobal,
76                 gRemoteDisplayClassInfo.notifyDisplayConnected,
77                 surfaceObj, width, height, flags, session);
78         env->DeleteLocalRef(surfaceObj);
79         checkAndClearExceptionFromCallback(env, "notifyDisplayConnected");
80     }
81 
onDisplayDisconnected()82     virtual void onDisplayDisconnected() {
83         JNIEnv* env = AndroidRuntime::getJNIEnv();
84 
85         env->CallVoidMethod(mRemoteDisplayObjGlobal,
86                 gRemoteDisplayClassInfo.notifyDisplayDisconnected);
87         checkAndClearExceptionFromCallback(env, "notifyDisplayDisconnected");
88     }
89 
onDisplayError(int32_t error)90     virtual void onDisplayError(int32_t error) {
91         JNIEnv* env = AndroidRuntime::getJNIEnv();
92 
93         env->CallVoidMethod(mRemoteDisplayObjGlobal,
94                 gRemoteDisplayClassInfo.notifyDisplayError, error);
95         checkAndClearExceptionFromCallback(env, "notifyDisplayError");
96     }
97 
98 private:
99     jobject mRemoteDisplayObjGlobal;
100 
checkAndClearExceptionFromCallback(JNIEnv * env,const char * methodName)101     static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
102         if (env->ExceptionCheck()) {
103             ALOGE("An exception was thrown by callback '%s'.", methodName);
104             LOGE_EX(env);
105             env->ExceptionClear();
106         }
107     }
108 };
109 
110 class NativeRemoteDisplay {
111 public:
NativeRemoteDisplay(const sp<IRemoteDisplay> & display,const sp<NativeRemoteDisplayClient> & client)112     NativeRemoteDisplay(const sp<IRemoteDisplay>& display,
113             const sp<NativeRemoteDisplayClient>& client) :
114             mDisplay(display), mClient(client) {
115     }
116 
~NativeRemoteDisplay()117     ~NativeRemoteDisplay() {
118         mDisplay->dispose();
119     }
120 
pause()121     void pause() {
122         mDisplay->pause();
123     }
124 
resume()125     void resume() {
126         mDisplay->resume();
127     }
128 
129 private:
130     sp<IRemoteDisplay> mDisplay;
131     sp<NativeRemoteDisplayClient> mClient;
132 };
133 
134 
135 // ----------------------------------------------------------------------------
136 
nativeListen(JNIEnv * env,jobject remoteDisplayObj,jstring ifaceStr,jstring opPackageNameStr)137 static jlong nativeListen(JNIEnv* env, jobject remoteDisplayObj, jstring ifaceStr,
138         jstring opPackageNameStr) {
139     ScopedUtfChars iface(env, ifaceStr);
140     ScopedUtfChars opPackageName(env, opPackageNameStr);
141 
142     sp<IServiceManager> sm = defaultServiceManager();
143     sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(
144             sm->getService(String16("media.player")));
145     if (service == NULL) {
146         ALOGE("Could not obtain IMediaPlayerService from service manager");
147         return 0;
148     }
149 
150     sp<NativeRemoteDisplayClient> client(new NativeRemoteDisplayClient(env, remoteDisplayObj));
151     sp<IRemoteDisplay> display = service->listenForRemoteDisplay(String16(opPackageName.c_str()),
152             client, String8(iface.c_str()));
153     if (display == NULL) {
154         ALOGE("Media player service rejected request to listen for remote display '%s'.",
155                 iface.c_str());
156         return 0;
157     }
158 
159     NativeRemoteDisplay* wrapper = new NativeRemoteDisplay(display, client);
160     return reinterpret_cast<jlong>(wrapper);
161 }
162 
nativePause(JNIEnv * env,jobject remoteDisplayObj,jlong ptr)163 static void nativePause(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
164     NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
165     wrapper->pause();
166 }
167 
nativeResume(JNIEnv * env,jobject remoteDisplayObj,jlong ptr)168 static void nativeResume(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
169     NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
170     wrapper->resume();
171 }
172 
nativeDispose(JNIEnv * env,jobject remoteDisplayObj,jlong ptr)173 static void nativeDispose(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
174     NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
175     delete wrapper;
176 }
177 
178 // ----------------------------------------------------------------------------
179 
180 static const JNINativeMethod gMethods[] = {
181     {"nativeListen", "(Ljava/lang/String;Ljava/lang/String;)J",
182             (void*)nativeListen },
183     {"nativeDispose", "(J)V",
184             (void*)nativeDispose },
185     {"nativePause", "(J)V",
186             (void*)nativePause },
187     {"nativeResume", "(J)V",
188             (void*)nativeResume },
189 };
190 
register_android_media_RemoteDisplay(JNIEnv * env)191 int register_android_media_RemoteDisplay(JNIEnv* env)
192 {
193     int err = RegisterMethodsOrDie(env, "android/media/RemoteDisplay", gMethods, NELEM(gMethods));
194 
195     jclass clazz = FindClassOrDie(env, "android/media/RemoteDisplay");
196     gRemoteDisplayClassInfo.notifyDisplayConnected = GetMethodIDOrDie(env,
197             clazz, "notifyDisplayConnected", "(Landroid/view/Surface;IIII)V");
198     gRemoteDisplayClassInfo.notifyDisplayDisconnected = GetMethodIDOrDie(env,
199             clazz, "notifyDisplayDisconnected", "()V");
200     gRemoteDisplayClassInfo.notifyDisplayError = GetMethodIDOrDie(env,
201             clazz, "notifyDisplayError", "(I)V");
202     return err;
203 }
204 
205 };
206