1 /*
   2  * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "JavaInputStreamCallbacks.h"
  27 #include "JniUtils.h"
  28 #include <Common/VSMemory.h>
  29 #if TARGET_OS_LINUX
  30 #include <stdlib.h>
  31 #include <string.h>
  32 #endif // TARGET_OS_LINUX
  33 
  34 jfieldID  CJavaInputStreamCallbacks::m_BufferFID = 0;
  35 jmethodID CJavaInputStreamCallbacks::m_NeedBufferMID = 0;
  36 jmethodID CJavaInputStreamCallbacks::m_ReadNextBlockMID = 0;
  37 jmethodID CJavaInputStreamCallbacks::m_ReadBlockMID = 0;
  38 jmethodID CJavaInputStreamCallbacks::m_IsSeekableMID = 0;
  39 jmethodID CJavaInputStreamCallbacks::m_IsRandomAccessMID = 0;
  40 jmethodID CJavaInputStreamCallbacks::m_SeekMID = 0;
  41 jmethodID CJavaInputStreamCallbacks::m_CloseConnectionMID = 0;
  42 jmethodID CJavaInputStreamCallbacks::m_PropertyMID = 0;
  43 jmethodID CJavaInputStreamCallbacks::m_GetStreamSizeMID = 0;
  44 
  45 CJavaInputStreamCallbacks::CJavaInputStreamCallbacks()
  46     : m_ConnectionHolder(0)
  47 {}
  48 
  49 CJavaInputStreamCallbacks::~CJavaInputStreamCallbacks()
  50 {}
  51 
  52 bool CJavaInputStreamCallbacks::Init(JNIEnv *env, jobject jLocator)
  53 {
  54     env->GetJavaVM(&m_jvm);
  55     CJavaEnvironment javaEnv(m_jvm);
  56 
  57     static jmethodID createConnectionHolder = 0;
  58     if (0 == createConnectionHolder)
  59     {
  60         jclass klass = env->GetObjectClass(jLocator);
  61         createConnectionHolder = env->GetMethodID(klass, "createConnectionHolder", "()Lcom/sun/media/jfxmedia/locator/ConnectionHolder;");
  62         env->DeleteLocalRef(klass);
  63     }
  64 
  65     m_ConnectionHolder = env->NewGlobalRef(env->CallObjectMethod(jLocator, createConnectionHolder));
  66     if (NULL == m_ConnectionHolder)
  67     {
  68         javaEnv.reportException();
  69         return false;
  70     }
  71 
  72     static bool methodIDsInitialized = false;
  73     if (!methodIDsInitialized)
  74     {
  75         // Get the parent abstract class. It's wrong to get method ids from the concrete implementation
  76         // because it crashes jvm when it tries to call virtual methods.
  77         // See https://javafx-jira.kenai.com/browse/RT-37115
  78         jclass klass = env->FindClass("com/sun/media/jfxmedia/locator/ConnectionHolder");
  79 
  80         m_BufferFID = env->GetFieldID(klass, "buffer", "Ljava/nio/ByteBuffer;");
  81         m_NeedBufferMID = env->GetMethodID(klass, "needBuffer", "()Z");
  82         m_ReadNextBlockMID = env->GetMethodID(klass, "readNextBlock", "()I");
  83         m_ReadBlockMID = env->GetMethodID(klass, "readBlock", "(JI)I");
  84         m_IsSeekableMID = env->GetMethodID(klass, "isSeekable", "()Z");
  85         m_IsRandomAccessMID = env->GetMethodID(klass, "isRandomAccess", "()Z");
  86         m_SeekMID = env->GetMethodID(klass, "seek", "(J)J");
  87         m_CloseConnectionMID = env->GetMethodID(klass, "closeConnection", "()V");
  88         m_PropertyMID = env->GetMethodID(klass, "property", "(II)I");
  89         m_GetStreamSizeMID = env->GetMethodID(klass, "getStreamSize", "()I");
  90 
  91         methodIDsInitialized = true;
  92         env->DeleteLocalRef(klass);
  93     }
  94 
  95     javaEnv.reportException();
  96     return true;
  97 }
  98 
  99 bool CJavaInputStreamCallbacks::NeedBuffer()
 100 {
 101     bool result = false;
 102     CJavaEnvironment javaEnv(m_jvm);
 103     JNIEnv *pEnv = javaEnv.getEnvironment();
 104 
 105     if (pEnv) {
 106         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 107         if (connection) {
 108             result = (pEnv->CallBooleanMethod(connection, m_NeedBufferMID) == JNI_TRUE);
 109             pEnv->DeleteLocalRef(connection);
 110         }
 111 
 112         javaEnv.reportException();
 113     }
 114 
 115     return result;
 116 }
 117 
 118 int CJavaInputStreamCallbacks::ReadNextBlock()
 119 {
 120     int result = -1;
 121     CJavaEnvironment javaEnv(m_jvm);
 122     JNIEnv *pEnv = javaEnv.getEnvironment();
 123 
 124     if (pEnv) {
 125         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 126         if (connection) {
 127             result = pEnv->CallIntMethod(connection, m_ReadNextBlockMID);
 128             pEnv->DeleteLocalRef(connection);
 129         }
 130 
 131         if (javaEnv.clearException()) {
 132             result = -2;
 133         }
 134     }
 135 
 136     return result;
 137 }
 138 
 139 int CJavaInputStreamCallbacks::ReadBlock(int64_t position, int size)
 140 {
 141     int result = -1;
 142     CJavaEnvironment javaEnv(m_jvm);
 143     JNIEnv *pEnv = javaEnv.getEnvironment();
 144 
 145     if (pEnv) {
 146         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 147         if (connection) {
 148             result = pEnv->CallIntMethod(connection, m_ReadBlockMID, position, size);
 149             pEnv->DeleteLocalRef(connection);
 150         }
 151 
 152         if (javaEnv.clearException()) {
 153             result = -2;
 154         }
 155     }
 156 
 157     return result;
 158 }
 159 
 160 void CJavaInputStreamCallbacks::CopyBlock(void* destination, int size)
 161 {
 162     CJavaEnvironment javaEnv(m_jvm);
 163     JNIEnv *pEnv = javaEnv.getEnvironment();
 164     if (pEnv) {
 165         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 166         if (connection) {
 167             jobject buffer = pEnv->GetObjectField(connection, m_BufferFID);
 168             void *data = pEnv->GetDirectBufferAddress(buffer);
 169 
 170             memcpy(destination, data, size);
 171             pEnv->DeleteLocalRef(buffer);
 172             pEnv->DeleteLocalRef(connection);
 173         }
 174     }
 175  }
 176 
 177 bool CJavaInputStreamCallbacks::IsSeekable()
 178 {
 179     CJavaEnvironment javaEnv(m_jvm);
 180     JNIEnv *pEnv = javaEnv.getEnvironment();
 181     bool result = false;
 182 
 183     if (pEnv) {
 184         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 185         if (connection) {
 186             result = (pEnv->CallBooleanMethod(connection, m_IsSeekableMID) == JNI_TRUE);
 187             pEnv->DeleteLocalRef(connection);
 188         }
 189 
 190         javaEnv.reportException();
 191     }
 192 
 193     return result;
 194 }
 195 
 196 bool CJavaInputStreamCallbacks::IsRandomAccess()
 197 {
 198     CJavaEnvironment javaEnv(m_jvm);
 199     JNIEnv *pEnv = javaEnv.getEnvironment();
 200     bool result = false;
 201 
 202     if (pEnv) {
 203         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 204         if (connection) {
 205             result = (pEnv->CallBooleanMethod(connection, m_IsRandomAccessMID) == JNI_TRUE);
 206             pEnv->DeleteLocalRef(connection);
 207         }
 208 
 209         javaEnv.reportException();
 210     }
 211 
 212     return result;
 213 }
 214 
 215 int64_t CJavaInputStreamCallbacks::Seek(int64_t position)
 216 {
 217     CJavaEnvironment javaEnv(m_jvm);
 218     JNIEnv *pEnv = javaEnv.getEnvironment();
 219     jlong result = -1;
 220 
 221     if (pEnv) {
 222         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 223         if (connection) {
 224             result = pEnv->CallLongMethod(connection, m_SeekMID, (jlong)position);
 225             pEnv->DeleteLocalRef(connection);
 226         }
 227 
 228         javaEnv.reportException();
 229     }
 230 
 231     return (int64_t)result;
 232 }
 233 
 234 void CJavaInputStreamCallbacks::CloseConnection()
 235 {
 236     CJavaEnvironment javaEnv(m_jvm);
 237     JNIEnv *pEnv = javaEnv.getEnvironment();
 238 
 239     if (pEnv) {
 240         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 241         if (connection) {
 242             pEnv->CallVoidMethod(connection, m_CloseConnectionMID);
 243             pEnv->DeleteLocalRef(connection);
 244 
 245             javaEnv.reportException();
 246         }
 247 
 248         pEnv->DeleteGlobalRef(m_ConnectionHolder);
 249         m_ConnectionHolder = NULL;
 250     }
 251 }
 252 
 253 int CJavaInputStreamCallbacks::Property(int prop, int value)
 254 {
 255     CJavaEnvironment javaEnv(m_jvm);
 256     JNIEnv *pEnv = javaEnv.getEnvironment();
 257     int result = 0;
 258 
 259     if (pEnv) {
 260         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 261         if (connection) {
 262             result = pEnv->CallIntMethod(connection, m_PropertyMID, (jint)prop, (jint)value);
 263             pEnv->DeleteLocalRef(connection);
 264         }
 265 
 266         javaEnv.reportException();
 267     }
 268 
 269     return result;
 270 }
 271 
 272 int CJavaInputStreamCallbacks::GetStreamSize()
 273 {
 274     CJavaEnvironment javaEnv(m_jvm);
 275     JNIEnv *pEnv = javaEnv.getEnvironment();
 276     int result = 0;
 277 
 278     if (pEnv) {
 279         jobject connection = pEnv->NewLocalRef(m_ConnectionHolder);
 280         if (connection) {
 281             result = pEnv->CallIntMethod(connection, m_GetStreamSizeMID);
 282             pEnv->DeleteLocalRef(connection);
 283         }
 284 
 285         javaEnv.reportException();
 286     }
 287 
 288     return result;
 289 }