Home | History | Annotate | Download | only in jni
      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 
     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:
     53     NativeRemoteDisplayClient(JNIEnv* env, jobject remoteDisplayObj) :
     54             mRemoteDisplayObjGlobal(env->NewGlobalRef(remoteDisplayObj)) {
     55     }
     56 
     57 protected:
     58     ~NativeRemoteDisplayClient() {
     59         JNIEnv* env = AndroidRuntime::getJNIEnv();
     60         env->DeleteGlobalRef(mRemoteDisplayObjGlobal);
     61     }
     62 
     63 public:
     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 
     98 private:
     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 
    110 class NativeRemoteDisplay {
    111 public:
    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 
    129 private:
    130     sp<IRemoteDisplay> mDisplay;
    131     sp<NativeRemoteDisplayClient> mClient;
    132 };
    133 
    134 
    135 // ----------------------------------------------------------------------------
    136 
    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 
    163 static void nativePause(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
    164     NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
    165     wrapper->pause();
    166 }
    167 
    168 static void nativeResume(JNIEnv* env, jobject remoteDisplayObj, jlong ptr) {
    169     NativeRemoteDisplay* wrapper = reinterpret_cast<NativeRemoteDisplay*>(ptr);
    170     wrapper->resume();
    171 }
    172 
    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 
    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