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 <nativehelper/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 <nativehelper/ScopedUtfChars.h>
40
41namespace android {
42
43static struct {
44    jmethodID notifyDisplayConnected;
45    jmethodID notifyDisplayDisconnected;
46    jmethodID notifyDisplayError;
47} gRemoteDisplayClassInfo;
48
49// ----------------------------------------------------------------------------
50
51class NativeRemoteDisplayClient : public BnRemoteDisplayClient {
52public:
53    NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
54            mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) {
55    }
56
57protected:
58    ~NativeRemoteDisplayClient() {
59        JNIEnv* env = AndroidRuntime::getJNIEnv();
60        env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
61    }
62
63public:
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
82    virtual void onDisplayDisconnected() {
83        JNIEnv* env = AndroidRuntime::getJNIEnv();
84
85        env->CallVoidMethod(mRemoteDisplayObjGlobal,
86                gRemoteDisplayClassInfo.notifyDisplayDisconnected);
87        checkAndClearExceptionFromCallback(env, "notifyDisplayDisconnected");
88    }
89
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
98private:
99    jobject mRemoteDisplayObjGlobal;
100
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
110class NativeRemoteDisplay {
111public:
112    NativeRemoteDisplay(const sp<IRemoteDisplay>& display,
113            const sp<NativeRemoteDisplayClient>& client) :
114            mDisplay(display), mClient(client) {
115    }
116
117    ~NativeRemoteDisplay() {
118        mDisplay->dispose();
119    }
120
121    void pause() {
122        mDisplay->pause();
123    }
124
125    void resume() {
126        mDisplay->resume();
127    }
128
129private:
130    sp<IRemoteDisplay> mDisplay;
131    sp<NativeRemoteDisplayClient> mClient;
132};
133
134
135// ----------------------------------------------------------------------------
136
137static 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
163static void nativePause(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
164    NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
165    wrapper->pause();
166}
167
168static void nativeResume(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
169    NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
170    wrapper->resume();
171}
172
173static void nativeDispose(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
174    NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
175    delete wrapper;
176}
177
178// ----------------------------------------------------------------------------
179
180static 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
191int 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