/* * Copyright (C) 2014 Haruki Hasegawa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.h6ah4i.android.media.opensl; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.lang.ref.WeakReference; import java.lang.reflect.Field; import java.nio.channels.FileChannel; import android.annotation.SuppressLint; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.net.Uri; import android.os.Handler; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.provider.MediaStore.MediaColumns; import android.util.Log; import com.h6ah4i.android.media.IBasicMediaPlayer; import com.h6ah4i.android.media.compat.AudioAttributes; public class OpenSLMediaPlayer implements IBasicMediaPlayer { private static final String TAG = "OpenSLMediaPlayer"; private static final String WAKELOCK_TAG = "OpenSLMediaPlayerWakeLock"; private static final boolean LOCAL_LOGV = false; private static final boolean LOCAL_LOGD = false; // options public static final int OPTION_USE_FADE = (1 << 0); // fields private static final String[] PROJECTION_MEDIACOLUMNS_DATA = new String[] { MediaColumns.DATA }; private static final Field mField_FileDescriptor_descriptor; private long mNativeHandle; private static final boolean HAS_NATIVE; private int[] mParamIntBuff = new int[1]; private boolean[] mParamBoolBuff = new boolean[1]; private InternalHandler mHandler; private AssetFileDescriptor mContentAssetFd; private WakeLock mWakeLock; private OnCompletionListener mOnCompletionListener; private OnPreparedListener mOnPreparedListener; private OnSeekCompleteListener mOnSeekCompleteListener; private OnBufferingUpdateListener mOnBufferingUpdateListener; private OnInfoListener mOnInfoListener; private OnErrorListener mOnErrorListener; static { // load native library HAS_NATIVE = OpenSLMediaPlayerNativeLibraryLoader.loadLibraries(); // obtain FileDescriptor.descriptor field Field field_descriptor = null; try { Field f = FileDescriptor.class.getDeclaredField("descriptor"); if (f != null) { f.setAccessible(true); field_descriptor = f; } } catch (NoSuchFieldException e) { } catch (Exception e) { } mField_FileDescriptor_descriptor = field_descriptor; } public OpenSLMediaPlayer(OpenSLMediaPlayerContext context, int options) { if (context == null) throw new IllegalArgumentException("The argument 'context' cannot be null"); final long contextHandle = Internal.getNativeHandle(context); if (contextHandle == 0) throw new IllegalStateException("Illegal context state"); try { final int[] iparams = new int[1]; iparams[0] = ((options & OPTION_USE_FADE) != 0) ? 1 : 0; mNativeHandle = createNativeImplHandle( contextHandle, new WeakReference<OpenSLMediaPlayer>(this), iparams); } catch (Throwable e) { } if (mNativeHandle == 0) { throw new IllegalStateException( "Failed to create OpenSLMediaPlayer instance in native layer"); } mHandler = new InternalHandler(this); } @Override protected void finalize() throws Throwable { release(); super.finalize(); } @Override public void setDataSource(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { if (context == null) { throw new NullPointerException(); // throw new // IllegalArgumentException("The argument context cannot be null"); } if (uri == null) { throw new NullPointerException(); // throw new // IllegalArgumentException("The argument uri cannot be null"); } checkNativeImplIsAvailable(); final String scheme = uri.getScheme(); if ("file".equals(scheme)) { setDataSource(uri.getPath()); } else if ("content".equals(scheme)) { setDataSourceInternalContentUri(context, uri); } else { setDataSourceInternalNonContentUri(context, uri); } } @Override public void setDataSource(String path) throws IOException, IllegalArgumentException, IllegalStateException { if (path == null) { throw new NullPointerException(); // throw new // IllegalArgumentException("The argument path cannot be null"); } try { // fix "file://" URI form string final Uri uri = Uri.parse(path); if ("file".equals(uri.getScheme())) { path = uri.getPath(); } } catch (Exception e) { } checkNativeImplIsAvailable(); final int result = setDataSourcePathImplNative(mNativeHandle, path); parseResultAndThrowException(result); } @Override public void setDataSource(FileDescriptor fd) throws IOException, IllegalArgumentException, IllegalStateException { final int nativeFD = checkAndObtainNativeFileDescriptor(fd); checkNativeImplIsAvailable(); final int result = setDataSourceFdImplNative(mNativeHandle, nativeFD); parseResultAndThrowException(result); } @Override public void setDataSource(FileDescriptor fd, long offset, long length) throws IOException, IllegalArgumentException, IllegalStateException { if (length < 0) throw new IllegalArgumentException("The argument length must positive or zero"); final int nativeFD = checkAndObtainNativeFileDescriptor(fd); final long fileSize = getFileSize(fd); if (fileSize < 0) throw new IOException("Can't obtain file size"); if (offset < 0 || offset > fileSize) throw new IllegalArgumentException("File offset is invalid"); if ((fileSize - offset) > length) throw new IllegalArgumentException( "Specified file range is larger than actual file size"); checkNativeImplIsAvailable(); final int result = setDataSourceFdImplNative(mNativeHandle, nativeFD, offset, length); parseResultAndThrowException(result); } @Override public void prepare() throws IOException, IllegalStateException { checkNativeImplIsAvailable(); final int result = prepareImplNative(mNativeHandle); parseResultAndThrowException(result); } @Override public void prepareAsync() throws IllegalStateException { checkNativeImplIsAvailable(); final int result = prepareAsyncImplNative(mNativeHandle); parseResultAndThrowExceptForIOExceptions(result); } @Override public void start() throws IllegalStateException { checkNativeImplIsAvailable(); stayAwake(true); final int result = startImplNative(mNativeHandle); parseResultAndThrowNoExceptions(result); } @Override public void stop() throws IllegalStateException { if (LOCAL_LOGD) { Log.d(TAG, "stop()"); } checkNativeImplIsAvailable(); stayAwake(false); final int result = stopImplNative(mNativeHandle); parseResultAndThrowNoExceptions(result); } @Override public void pause() throws IllegalStateException { if (LOCAL_LOGD) { Log.d(TAG, "pause()"); } checkNativeImplIsAvailable(); stayAwake(false); final int result = pauseImplNative(mNativeHandle); parseResultAndThrowNoExceptions(result); if (mHandler != null) { mHandler.clearBufferingUpdateMessage(); } } @Override public void reset() { if (LOCAL_LOGD) { Log.d(TAG, "reset()"); } checkNativeImplIsAvailable(); stayAwake(false); if (mNativeHandle != 0) { final int result = resetImplNative(mNativeHandle); parseResultAndThrowExceptForIOExceptions(result); } if (mHandler != null) { mHandler.clearPendingMessages(); } releaseOpenedContentFileDescriptor(); } @Override public void release() { stayAwake(false); if (mHandler != null) { mHandler.release(); mHandler = null; } mOnCompletionListener = null; mOnPreparedListener = null; mOnSeekCompleteListener = null; mOnBufferingUpdateListener = null; mOnInfoListener = null; mOnErrorListener = null; try { if (HAS_NATIVE && mNativeHandle != 0) { deleteNativeImplHandle(mNativeHandle); mNativeHandle = 0; } } catch (Exception e) { Log.e(TAG, "release()", e); } releaseOpenedContentFileDescriptor(); } @Override public void seekTo(int msec) throws IllegalStateException { checkNativeImplIsAvailable(); if (mNativeHandle != 0) { final int result = seekToImplNative(mNativeHandle, msec); parseResultAndThrowNoExceptions(result); } } @Override public int getAudioSessionId() { checkNativeImplIsAvailable(); // NOTE: // This method always returns 0 when using OpenSL sink backend. if (mNativeHandle != 0) { getAudioSessionIdImplNative(mNativeHandle, mParamIntBuff); return mParamIntBuff[0]; } else { return 0; } } @Override public void setAudioSessionId(int sessionId) throws IllegalArgumentException, IllegalStateException { checkNativeImplIsAvailable(); throw new IllegalStateException("This method is not supported"); } @Override public int getDuration() { checkNativeImplIsAvailable(); if (mNativeHandle != 0) { getDurationImplNative(mNativeHandle, mParamIntBuff); return mParamIntBuff[0]; } return 0; } @Override public void setLooping(boolean looping) { checkNativeImplIsAvailable(); try { final int result = setLoopingImplNative(mNativeHandle, looping); parseResultAndThrowException(result); } catch (Exception e) { Log.e(TAG, "An error occurred in setLooping(looping = " + looping + ")"); } } @Override public int getCurrentPosition() { checkNativeImplIsAvailable(); try { getCurrentPositionImplNative(mNativeHandle, mParamIntBuff); return mParamIntBuff[0]; } catch (Exception e) { Log.e(TAG, "An error occurred in getCurrentPosition()"); } return 0; } @Override public boolean isLooping() { checkNativeImplIsAvailable(); try { final int result = isLoopingImplNative(mNativeHandle, mParamBoolBuff); parseResultAndThrowException(result); return mParamBoolBuff[0]; } catch (Exception e) { Log.e(TAG, "An error occurred in getCurrentPosition()"); } return false; } @Override public boolean isPlaying() throws IllegalStateException { checkNativeImplIsAvailable(); try { final int result = isPlayingImplNative(mNativeHandle, mParamBoolBuff); parseResultAndThrowException(result); return mParamBoolBuff[0]; } catch (Exception e) { Log.e(TAG, "An error occurred in getCurrentPosition()"); } return false; } @Override public void attachAuxEffect(int effectId) throws IllegalArgumentException, IllegalStateException { checkNativeImplIsAvailable(); try { final int result = attachAuxEffectImplNative(mNativeHandle, effectId); parseResultAndThrowNoExceptions(result); } catch (Exception e) { Log.e(TAG, "An error occurred in attachAuxEffect(effectId = " + effectId + ")"); } } @Override public void setVolume(float leftVolume, float rightVolume) { checkNativeImplIsAvailable(); try { final int result = setVolumeImplNative(mNativeHandle, leftVolume, rightVolume); parseResultAndThrowNoExceptions(result); } catch (Exception e) { Log.e(TAG, "An error occurred in setVolume(leftVolume = " + leftVolume + ", rightVolume = " + rightVolume + ")"); } } @Override public void setAudioStreamType(int streamtype) { checkNativeImplIsAvailable(); try { final int result = setAudioStreamTypeImplNative(mNativeHandle, streamtype); parseResultAndThrowNoExceptions(result); } catch (Exception e) { Log.e(TAG, "An error occurred in setAudioStreamType(streamtype = " + streamtype + ")"); } } @Override public void setAuxEffectSendLevel(float level) { checkNativeImplIsAvailable(); try { final int result = setAuxEffectSendLevelImplNative(mNativeHandle, level); parseResultAndThrowNoExceptions(result); } catch (Exception e) { Log.e(TAG, "An error occurred in setAuxEffectSendLevel(level = " + level + ")"); } } @Override public void setWakeMode(Context context, int mode) { checkNativeImplIsAvailable(); if (context == null) { throw new IllegalArgumentException("The argument context must not be null"); } // re-create the wake lock object final boolean wasHeld = releaseWakeLockObject(); createWakeLockObject(context, mode); if (wasHeld) { stayAwake(true); } } @Override public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) { if (!isNativeImplIsAvailable()) { return; } mOnBufferingUpdateListener = listener; } @Override public void setOnCompletionListener(OnCompletionListener listener) { if (!isNativeImplIsAvailable()) { return; } mOnCompletionListener = listener; } @Override public void setOnErrorListener(OnErrorListener listener) { if (!isNativeImplIsAvailable()) { return; } mOnErrorListener = listener; } @Override public void setOnInfoListener(OnInfoListener listener) { if (!isNativeImplIsAvailable()) { return; } mOnInfoListener = listener; } @Override public void setOnPreparedListener(OnPreparedListener listener) { if (!isNativeImplIsAvailable()) { return; } mOnPreparedListener = listener; } @Override public void setOnSeekCompleteListener(OnSeekCompleteListener listener) { if (!isNativeImplIsAvailable()) { return; } mOnSeekCompleteListener = listener; } @Override public void setNextMediaPlayer(IBasicMediaPlayer next) { checkNativeImplIsAvailable(); if (next != null && !(next instanceof OpenSLMediaPlayer)) { throw new IllegalArgumentException("Not OpenSLMediaPlayer instance"); } OpenSLMediaPlayer next2 = (OpenSLMediaPlayer) next; if (next2 == this) { throw new IllegalArgumentException("Can't assign the self instance as a next player"); } if ((next2 != null) && next2.mNativeHandle == 0) { throw new IllegalStateException("The next player has already been released."); } final long nextHandle = (next2 == null) ? 0 : (next2.mNativeHandle); try { final int result = setNextMediaPlayerImplNative(mNativeHandle, nextHandle); parseResultAndThrowExceptForIOExceptions(result); } catch (IllegalArgumentException e) { throw e; } catch (IllegalStateException e) { throw e; } catch (Exception e) { Log.e(TAG, "An error occurred in setNextMediaPlayer(nextHandle = " + nextHandle + ")"); } } @Override public void setAudioAttributes(AudioAttributes attributes) { if (attributes == null) { throw new IllegalArgumentException(); } checkNativeImplIsAvailable(); if (LOCAL_LOGV) { Log.v(TAG, "setAudioAttributes() is not supported, just ignored"); } } // // setDataSource(Context, Uri) implementation // private void setDataSourceInternalNonContentUri(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { String strUri = uri.toString(); final int result = setDataSourceUriImplNative(mNativeHandle, strUri); parseResultAndThrowException(result); } private void setDataSourceInternalContentUri(Context context, Uri uri) throws IOException, IllegalArgumentException, SecurityException, IllegalStateException { final ContentResolver cr = context.getContentResolver(); AssetFileDescriptor afd = cr.openAssetFileDescriptor(uri, "r"); FileDescriptor fd = afd.getFileDescriptor(); final int nativeFD; try { nativeFD = checkAndObtainNativeFileDescriptor(fd); } catch (IllegalArgumentException e) { closeQuietly(afd); throw e; } final int result; final long declLength = afd.getDeclaredLength(); final long startOffset = afd.getStartOffset(); if (declLength < 0) { result = setDataSourceFdImplNative(mNativeHandle, nativeFD); } else { result = setDataSourceFdImplNative( mNativeHandle, nativeFD, startOffset, declLength); } parseResultAndThrowException(result); mContentAssetFd = afd; } private void releaseOpenedContentFileDescriptor() { if (mContentAssetFd == null) { return; } closeQuietly(mContentAssetFd); mContentAssetFd = null; } // // Wake lock // private void createWakeLockObject(Context context, int mode) { PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); WakeLock wakelock = pm.newWakeLock( mode | PowerManager.ON_AFTER_RELEASE, WAKELOCK_TAG); wakelock.setReferenceCounted(false); mWakeLock = wakelock; } private boolean releaseWakeLockObject() { if (mWakeLock == null) return false; final boolean wasHeld = mWakeLock.isHeld(); mWakeLock.release(); mWakeLock = null; return wasHeld; } @SuppressLint("Wakelock") private void stayAwake(boolean awake) { if (mWakeLock == null) return; if (awake) { mWakeLock.acquire(); } else { mWakeLock.release(); } } // // Utilities // private boolean isNativeImplIsAvailable() { return (mNativeHandle != 0); } private void checkNativeImplIsAvailable() throws IllegalStateException { if (!isNativeImplIsAvailable()) { throw new IllegalStateException("Native implemenation handle is not present"); } } private static int checkAndObtainNativeFileDescriptor(FileDescriptor fd) { if (fd == null) throw new IllegalArgumentException("The argument fd cannot be null"); if (!fd.valid()) throw new IllegalArgumentException("File descriptor is not vailed"); final int nativeFD = getNativeFD(fd); if (nativeFD == 0) throw new IllegalArgumentException("File descriptor is not vailed"); return nativeFD; } private static int getNativeFD(FileDescriptor fd) { final Field descField = mField_FileDescriptor_descriptor; if (fd == null) return 0; if (descField == null) return 0; try { return descField.getInt(fd); } catch (IllegalAccessException e) { return 0; } catch (IllegalArgumentException e) { return 0; } } private static long getFileSize(FileDescriptor fd) { FileInputStream stream = null; FileChannel channel = null; long fileSize = -1; try { stream = new FileInputStream(fd); channel = stream.getChannel(); fileSize = channel.size(); } catch (Exception e) { } finally { if (channel != null) { try { channel.close(); channel = null; stream = null; // stream is already closed } catch (Exception e) { } } if (stream != null) { try { stream.close(); stream = null; } catch (Exception e) { } } } return fileSize; } /* * NOTE: The AssetFileDescriptor does not implement  * Closeable interface until API level 19 (issue #8) */ static void closeQuietly(AssetFileDescriptor afd) { if (afd != null) { try { afd.close(); } catch (IOException e) { Log.w(TAG, "closeQuietly() " + e.getStackTrace()); } } } // // Event handlers // private void handleOnCompletion() { stayAwake(false); if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(this); } } private void handleOnPrepared() { if (mOnPreparedListener != null) { mOnPreparedListener.onPrepared(this); } } private void handleOnSeekComplete() { if (mOnSeekCompleteListener != null) { mOnSeekCompleteListener.onSeekComplete(this); } } private void handleOnBufferingUpdate(int percent) { if (mOnBufferingUpdateListener != null) { mOnBufferingUpdateListener.onBufferingUpdate(this, percent); } } private void handleOnInfo(int what, int extra) { // boolean handled = false; if (mOnInfoListener != null) { /* handled = */mOnInfoListener.onInfo(this, what, extra); } } private void handleOnError(int what, int extra) { boolean handled = false; stayAwake(false); if (mOnErrorListener != null) { handled = mOnErrorListener.onError(this, what, extra); } if (!handled) { if (mOnCompletionListener != null) { mOnCompletionListener.onCompletion(this); } } } // // Internal methods accessor // /** @hide */ public static class Internal { // result codes public static final int RESULT_SUCCESS = 0; public static final int RESULT_ERROR = -1; public static final int RESULT_INVALID_HANDLE = -2; public static final int RESULT_ILLEGAL_STATE = -3; public static final int RESULT_ILLEGAL_ARGUMENT = -4; public static final int RESULT_INTERNAL_ERROR = -5; public static final int RESULT_MEMORY_ALLOCATION_FAILED = -6; public static final int RESULT_RESOURCE_ALLOCATION_FAILED = -7; public static final int RESULT_CONTENT_NOT_FOUND = -8; public static final int RESULT_CONTENT_UNSUPPORTED = -9; public static final int RESULT_IO_ERROR = -10; public static final int RESULT_PERMISSION_DENIED = -11; public static final int RESULT_TIMED_OUT = -12; public static final int RESULT_IN_ERROR_STATE = -13; public static final int RESULT_CONTROL_LOST = -14; public static final int RESULT_DEAD_OBJECT = -15; // constants public static final int AUX_EFFECT_NULL = 0; public static final int AUX_EFFECT_ENVIRONMENTAL_REVERB = 1; public static final int AUX_EFFECT_PRESET_REVERB = 2; public static void parseResultAndThrowException(int result) throws IOException { OpenSLMediaPlayer.parseResultAndThrowException(result); } public static void parseResultAndThrowExceptForIOExceptions(int result) { OpenSLMediaPlayer.parseResultAndThrowExceptForIOExceptions(result); } public static long getNativeHandle(OpenSLMediaPlayer player) { return (player != null) ? player.mNativeHandle : 0; } public static long getNativeHandle(OpenSLMediaPlayerContext context) { return (context != null) ? context.getNativeHandle() : 0; } public static int getAudioSessionId(OpenSLMediaPlayerContext context) { return (context != null) ? context.getAudioSessionId() : 0; } } // // Internal Handler // static class InternalHandler extends Handler { private WeakReference<OpenSLMediaPlayer> mRefPlayer; public InternalHandler(OpenSLMediaPlayer player) { super(); mRefPlayer = new WeakReference<OpenSLMediaPlayer>(player); } public void clearBufferingUpdateMessage() { removeMessages(MESSAGE_ON_BUFFERING_UPDATE); } public void clearPendingMessages() { removeMessages(MESSAGE_NOP); removeMessages(MESSAGE_ON_COMPLETION); removeMessages(MESSAGE_ON_PREPARED); removeMessages(MESSAGE_ON_SEEK_COMPLETE); removeMessages(MESSAGE_ON_BUFFERING_UPDATE); removeMessages(MESSAGE_ON_INFO); removeMessages(MESSAGE_ON_ERROR); } public void release() { mRefPlayer.clear(); clearPendingMessages(); } @Override public void handleMessage(Message msg) { final OpenSLMediaPlayer mp = mRefPlayer.get(); if (mp == null) return; mp.handleMessage(msg); } } // // JNI binder internal methods // private static final int MESSAGE_NOP = 0; private static final int MESSAGE_ON_COMPLETION = 1; private static final int MESSAGE_ON_PREPARED = 2; private static final int MESSAGE_ON_SEEK_COMPLETE = 3; private static final int MESSAGE_ON_BUFFERING_UPDATE = 4; private static final int MESSAGE_ON_INFO = 5; private static final int MESSAGE_ON_ERROR = 6; private static String toStringMessageCode(int code) { switch (code) { case MESSAGE_NOP: return "MESSAGE_NOP"; case MESSAGE_ON_COMPLETION: return "MESSAGE_ON_COMPLETION"; case MESSAGE_ON_PREPARED: return "MESSAGE_ON_PREPARED"; case MESSAGE_ON_SEEK_COMPLETE: return "MESSAGE_ON_SEEK_COMPLETE"; case MESSAGE_ON_BUFFERING_UPDATE: return "MESSAGE_ON_BUFFERING_UPDATE"; case MESSAGE_ON_INFO: return "MESSAGE_ON_INFO"; case MESSAGE_ON_ERROR: return "MESSAGE_ON_ERROR"; default: return "FIXME"; } } private void handleMessage(Message msg) { if (LOCAL_LOGD) { Log.d(TAG, "handleMessage(msg = " + toStringMessageCode(msg.what) + ")"); } switch (msg.what) { case MESSAGE_NOP: break; case MESSAGE_ON_COMPLETION: handleOnCompletion(); break; case MESSAGE_ON_PREPARED: handleOnPrepared(); break; case MESSAGE_ON_SEEK_COMPLETE: handleOnSeekComplete(); break; case MESSAGE_ON_BUFFERING_UPDATE: handleOnBufferingUpdate(msg.arg1); break; case MESSAGE_ON_INFO: handleOnInfo(msg.arg1, msg.arg2); break; case MESSAGE_ON_ERROR: handleOnError(msg.arg1, msg.arg2); break; } } @SuppressWarnings("unchecked") private static void postEventFromNative( Object ref, int what, int arg1, int arg2, Object obj) { final WeakReference<OpenSLMediaPlayer> mpref = ((WeakReference<OpenSLMediaPlayer>) ref); final OpenSLMediaPlayer mp = mpref.get(); if (mp == null) return; final InternalHandler handler = mp.mHandler; if (handler == null) return; final Message msg = handler.obtainMessage(); msg.what = what; msg.arg1 = arg1; msg.arg2 = arg2; msg.obj = obj; handler.sendMessage(msg); if (LOCAL_LOGD) { Log.d(TAG, "postEventFromNative(msg = " + toStringMessageCode(msg.what) + ")"); } } private static boolean parseResultAndThrowNoExceptions(int result) { try { parseResultAndThrowException(result); return true; } catch (Exception e) { Log.w(TAG, "An error occurred", e); return false; } } private static void parseResultAndThrowExceptForIOExceptions(int result) { try { parseResultAndThrowException(result); } catch (IOException e) { throw new IllegalStateException("An unexpected IOException occurred"); } } private static void parseResultAndThrowException(int result) throws IOException { switch (result) { case Internal.RESULT_SUCCESS: break; case Internal.RESULT_ERROR: throw new IllegalStateException("General error"); case Internal.RESULT_INVALID_HANDLE: throw new IllegalStateException("Invalid handle"); case Internal.RESULT_ILLEGAL_STATE: throw new IllegalStateException("Method is called in unexpected state"); case Internal.RESULT_ILLEGAL_ARGUMENT: throw new IllegalArgumentException("Illegal argument error occurred in native layer"); case Internal.RESULT_INTERNAL_ERROR: throw new IllegalStateException("Internal error"); case Internal.RESULT_MEMORY_ALLOCATION_FAILED: throw new IllegalStateException("Failed to allocate memory in native layer"); case Internal.RESULT_RESOURCE_ALLOCATION_FAILED: throw new IllegalStateException("Failed to allocate resources in native layer"); case Internal.RESULT_CONTENT_NOT_FOUND: throw new IOException("Content not found"); case Internal.RESULT_CONTENT_UNSUPPORTED: throw new IOException("Unsupported content"); case Internal.RESULT_IO_ERROR: throw new IOException("I/O error"); case Internal.RESULT_PERMISSION_DENIED: throw new IOException("Permission denied"); case Internal.RESULT_TIMED_OUT: throw new IllegalStateException("Timed out"); case Internal.RESULT_CONTROL_LOST: throw new UnsupportedOperationException("Control lost"); case Internal.RESULT_IN_ERROR_STATE: /* don't throw any exceptions */ break; case Internal.RESULT_DEAD_OBJECT: throw new IllegalStateException("Dead object"); default: throw new IllegalStateException( "Unexpected error (0x" + Integer.toHexString(result) + ")"); } } // // Native methods // private static native long createNativeImplHandle( long contextHandle, WeakReference<OpenSLMediaPlayer> weak_thiz, int[] params); private static native void deleteNativeImplHandle(long handle); private static native int setDataSourcePathImplNative(long handle, String path); private static native int setDataSourceUriImplNative(long handle, String uri); private static native int setDataSourceFdImplNative(long handle, int fd); private static native int setDataSourceFdImplNative(long handle, int fd, long offset, long length); private static native int attachAuxEffectImplNative(long handle, int effectId); private static native int setAuxEffectSendLevelImplNative(long handle, float level); private static native int prepareImplNative(long handle); private static native int prepareAsyncImplNative(long handle); private static native int startImplNative(long handle); private static native int stopImplNative(long handle); private static native int pauseImplNative(long handle); private static native int resetImplNative(long handle); private static native int setVolumeImplNative(long handle, float leftVolume, float rightVolume); private static native int getAudioSessionIdImplNative(long handle, int[] audioSessionId); private static native int getDurationImplNative(long handle, int[] duration); private static native int getCurrentPositionImplNative(long handle, int[] position); private static native int seekToImplNative(long handle, int msec); private static native int setLoopingImplNative(long handle, boolean looping); private static native int isLoopingImplNative(long handle, boolean[] looping); private static native int isPlayingImplNative(long handle, boolean[] playing); private static native int setAudioStreamTypeImplNative(long handle, int streamtype); private static native int setNextMediaPlayerImplNative(long handle, long nextHandle); }