diff options
66 files changed, 3814 insertions, 899 deletions
diff --git a/api/current.xml b/api/current.xml index 64dd3a8..7a86d05 100644 --- a/api/current.xml +++ b/api/current.xml @@ -4717,6 +4717,17 @@ visibility="public" > </field> +<field name="immersive" + type="int" + transient="false" + volatile="false" + value="16843457" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="inAnimation" type="int" transient="false" @@ -5916,17 +5927,6 @@ visibility="public" > </field> -<field name="kraken_resource_pad64" - type="int" - transient="false" - volatile="false" - value="16843457" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="kraken_resource_pad7" type="int" transient="false" diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index e980b17..fb45971 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -452,6 +452,7 @@ public final class ViewRoot extends Handler implements ViewParent, ((RootViewSurfaceTaker)view).willYouTakeTheSurface(); if (mSurfaceHolderCallback != null) { mSurfaceHolder = new TakenSurfaceHolder(); + mSurfaceHolder.setFormat(PixelFormat.UNKNOWN); } } Resources resources = mView.getContext().getResources(); @@ -754,7 +755,7 @@ public final class ViewRoot extends Handler implements ViewParent, // object is not initialized to its backing store, but soon it // will be (assuming the window is visible). attachInfo.mSurface = mSurface; - attachInfo.mTranslucentWindow = lp.format != PixelFormat.OPAQUE; + attachInfo.mTranslucentWindow = PixelFormat.formatHasAlpha(lp.format); attachInfo.mHasWindowFocus = false; attachInfo.mWindowVisibility = viewVisibility; attachInfo.mRecomputeGlobalAttributes = false; @@ -926,8 +927,6 @@ public final class ViewRoot extends Handler implements ViewParent, if (mSurfaceHolder != null) { mSurfaceHolder.mSurfaceLock.lock(); mDrawingAllowed = true; - lp.format = mSurfaceHolder.getRequestedFormat(); - lp.type = mSurfaceHolder.getRequestedType(); } boolean initialized = false; diff --git a/core/java/android/widget/CursorTreeAdapter.java b/core/java/android/widget/CursorTreeAdapter.java index 7b9b7bd..3fadf4c 100644 --- a/core/java/android/widget/CursorTreeAdapter.java +++ b/core/java/android/widget/CursorTreeAdapter.java @@ -134,14 +134,16 @@ public abstract class CursorTreeAdapter extends BaseExpandableListAdapter implem /** * Sets the group Cursor. * - * @param cursor The Cursor to set for the group. + * @param cursor The Cursor to set for the group. If there is an existing cursor + * it will be closed. */ public void setGroupCursor(Cursor cursor) { mGroupCursorHelper.changeCursor(cursor, false); } /** - * Sets the children Cursor for a particular group. + * Sets the children Cursor for a particular group. If there is an existing cursor + * it will be closed. * <p> * This is useful when asynchronously querying to prevent blocking the UI. * @@ -476,7 +478,7 @@ public abstract class CursorTreeAdapter extends BaseExpandableListAdapter implem mCursor.unregisterContentObserver(mContentObserver); mCursor.unregisterDataSetObserver(mDataSetObserver); - mCursor.deactivate(); + mCursor.close(); mCursor = null; } diff --git a/core/java/android/widget/SimpleCursorTreeAdapter.java b/core/java/android/widget/SimpleCursorTreeAdapter.java index a1c65f0..a033542 100644 --- a/core/java/android/widget/SimpleCursorTreeAdapter.java +++ b/core/java/android/widget/SimpleCursorTreeAdapter.java @@ -38,6 +38,10 @@ import android.view.View; * binding can be found, an {@link IllegalStateException} is thrown. */ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter { + + /** The name of the columns that contain the data to display for a group. */ + private String[] mGroupFromNames; + /** The indices of columns that contain data to display for a group. */ private int[] mGroupFrom; /** @@ -46,6 +50,9 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter */ private int[] mGroupTo; + /** The name of the columns that contain the data to display for a child. */ + private String[] mChildFromNames; + /** The indices of columns that contain data to display for a child. */ private int[] mChildFrom; /** @@ -171,38 +178,12 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter private void init(String[] groupFromNames, int[] groupTo, String[] childFromNames, int[] childTo) { + + mGroupFromNames = groupFromNames; mGroupTo = groupTo; + mChildFromNames = childFromNames; mChildTo = childTo; - - // Get the group cursor column indices, the child cursor column indices will come - // when needed - initGroupFromColumns(groupFromNames); - - // Get a temporary child cursor to init the column indices - if (getGroupCount() > 0) { - MyCursorHelper tmpCursorHelper = getChildrenCursorHelper(0, true); - if (tmpCursorHelper != null) { - initChildrenFromColumns(childFromNames, tmpCursorHelper.getCursor()); - deactivateChildrenCursorHelper(0); - } - } - } - - private void initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns) { - for (int i = fromColumnNames.length - 1; i >= 0; i--) { - fromColumns[i] = cursor.getColumnIndexOrThrow(fromColumnNames[i]); - } - } - - private void initGroupFromColumns(String[] groupFromNames) { - mGroupFrom = new int[groupFromNames.length]; - initFromColumns(mGroupCursorHelper.getCursor(), groupFromNames, mGroupFrom); - } - - private void initChildrenFromColumns(String[] childFromNames, Cursor childCursor) { - mChildFrom = new int[childFromNames.length]; - initFromColumns(childCursor, childFromNames, mChildFrom); } /** @@ -257,13 +238,29 @@ public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter } } + private void initFromColumns(Cursor cursor, String[] fromColumnNames, int[] fromColumns) { + for (int i = fromColumnNames.length - 1; i >= 0; i--) { + fromColumns[i] = cursor.getColumnIndexOrThrow(fromColumnNames[i]); + } + } + @Override protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { + if (mChildFrom == null) { + mChildFrom = new int[mChildFromNames.length]; + initFromColumns(cursor, mChildFromNames, mChildFrom); + } + bindView(view, context, cursor, mChildFrom, mChildTo); } @Override protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { + if (mGroupFrom == null) { + mGroupFrom = new int[mGroupFromNames.length]; + initFromColumns(cursor, mGroupFromNames, mGroupFrom); + } + bindView(view, context, cursor, mGroupFrom, mGroupTo); } diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index af61b80..54a9c2a 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -21,7 +21,8 @@ #include <dlfcn.h> #include <android_runtime/AndroidRuntime.h> -#include <android/native_activity.h> +#include <android_runtime/android_view_Surface.h> +#include <android_runtime/android_app_NativeActivity.h> #include <surfaceflinger/Surface.h> #include <ui/egl/android_natives.h> #include <ui/InputTransport.h> @@ -31,7 +32,6 @@ #include "android_os_MessageQueue.h" #include "android_view_InputChannel.h" #include "android_view_KeyEvent.h" -#include "android_view_Surface.h" namespace android { @@ -46,6 +46,48 @@ static struct { // ------------------------------------------------------------------------ +struct ActivityWork { + int32_t cmd; + int32_t arg1; + int32_t arg2; +}; + +enum { + CMD_DEF_KEY = 1, + CMD_SET_WINDOW_FORMAT, + CMD_SET_WINDOW_FLAGS, +}; + +static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) { + ActivityWork work; + work.cmd = cmd; + work.arg1 = arg1; + work.arg2 = arg2; + +restart: + int res = write(fd, &work, sizeof(work)); + if (res < 0 && errno == EINTR) { + goto restart; + } + + if (res == sizeof(work)) return; + + if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno)); + else LOGW("Truncated writing to work fd: %d", res); +} + +static bool read_work(int fd, ActivityWork* outWork) { + int res = read(fd, outWork, sizeof(ActivityWork)); + // no need to worry about EINTR, poll loop will just come back again. + if (res == sizeof(ActivityWork)) return true; + + if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno)); + else LOGW("Truncated reading work fd: %d", res); + return false; +} + +// ------------------------------------------------------------------------ + /* * Specialized input queue that allows unhandled key events to be dispatched * back to the native activity's Java framework code. @@ -59,8 +101,7 @@ struct MyInputQueue : AInputQueue { mLock.lock(); LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { - int8_t cmd = 1; - write(mWorkWrite, &cmd, sizeof(cmd)); + write_work(mWorkWrite, CMD_DEF_KEY); } mPendingKeys.add(keyEvent); mLock.unlock(); @@ -90,9 +131,9 @@ struct MyInputQueue : AInputQueue { /* * Native state for interacting with the NativeActivity class. */ -struct NativeCode { +struct NativeCode : public ANativeActivity { NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { - memset(&activity, sizeof(activity), 0); + memset((ANativeActivity*)this, sizeof(ANativeActivity), 0); memset(&callbacks, sizeof(callbacks), 0); dlhandle = _dlhandle; createActivityFunc = _createFunc; @@ -103,8 +144,11 @@ struct NativeCode { } ~NativeCode() { - if (activity.env != NULL && activity.clazz != NULL) { - activity.env->DeleteGlobalRef(activity.clazz); + if (callbacks.onDestroy != NULL) { + callbacks.onDestroy(this); + } + if (env != NULL && clazz != NULL) { + env->DeleteGlobalRef(clazz); } if (pollLoop != NULL && mainWorkRead >= 0) { pollLoop->removeCallback(mainWorkRead); @@ -114,9 +158,6 @@ struct NativeCode { } setSurface(NULL); setInputChannel(NULL); - if (callbacks.onDestroy != NULL) { - callbacks.onDestroy(&activity); - } if (mainWorkRead >= 0) close(mainWorkRead); if (mainWorkWrite >= 0) close(mainWorkWrite); if (dlhandle != NULL) { @@ -129,7 +170,7 @@ struct NativeCode { void setSurface(jobject _surface) { if (_surface != NULL) { - nativeWindow = android_Surface_getNativeWindow(activity.env, _surface); + nativeWindow = android_Surface_getNativeWindow(env, _surface); } else { nativeWindow = NULL; } @@ -138,14 +179,14 @@ struct NativeCode { status_t setInputChannel(jobject _channel) { if (inputChannel != NULL) { delete nativeInputQueue; - activity.env->DeleteGlobalRef(inputChannel); + env->DeleteGlobalRef(inputChannel); } inputChannel = NULL; nativeInputQueue = NULL; if (_channel != NULL) { - inputChannel = activity.env->NewGlobalRef(_channel); + inputChannel = env->NewGlobalRef(_channel); sp<InputChannel> ic = - android_view_InputChannel_getInputChannel(activity.env, _channel); + android_view_InputChannel_getInputChannel(env, _channel); if (ic != NULL) { nativeInputQueue = new MyInputQueue(ic, mainWorkWrite); if (nativeInputQueue->getConsumer().initialize() != android::OK) { @@ -160,7 +201,6 @@ struct NativeCode { return OK; } - ANativeActivity activity; ANativeActivityCallbacks callbacks; void* dlhandle; @@ -179,6 +219,18 @@ struct NativeCode { sp<PollLoop> pollLoop; }; +void android_NativeActivity_setWindowFormat( + ANativeActivity* activity, int32_t format) { + NativeCode* code = static_cast<NativeCode*>(activity); + write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format); +} + +void android_NativeActivity_setWindowFlags( + ANativeActivity* activity, int32_t values, int32_t mask) { + NativeCode* code = static_cast<NativeCode*>(activity); + write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask); +} + // ------------------------------------------------------------------------ /* @@ -186,19 +238,40 @@ struct NativeCode { */ static bool mainWorkCallback(int fd, int events, void* data) { NativeCode* code = (NativeCode*)data; - if ((events & POLLIN) != 0) { - KeyEvent* keyEvent; - while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) { - jobject inputEventObj = android_view_KeyEvent_fromNative( - code->activity.env, keyEvent); - code->activity.env->CallVoidMethod(code->activity.clazz, - gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); - int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal(); - if (res != OK) { - LOGW("Failed to send finished signal on channel '%s'. status=%d", - code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res); + if ((events & POLLIN) == 0) { + return true; + } + + ActivityWork work; + if (!read_work(code->mainWorkRead, &work)) { + return true; + } + switch (work.cmd) { + case CMD_DEF_KEY: { + KeyEvent* keyEvent; + while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) { + jobject inputEventObj = android_view_KeyEvent_fromNative( + code->env, keyEvent); + code->env->CallVoidMethod(code->clazz, + gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); + int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal(); + if (res != OK) { + LOGW("Failed to send finished signal on channel '%s'. status=%d", + code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res); + } } - } + } break; + case CMD_SET_WINDOW_FORMAT: { + code->env->CallVoidMethod(code->clazz, + gNativeActivityClassInfo.setWindowFormat, work.arg1); + } break; + case CMD_SET_WINDOW_FLAGS: { + code->env->CallVoidMethod(code->clazz, + gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2); + } break; + default: + LOGW("Unknown work command: %d", work.cmd); + break; } return true; @@ -243,28 +316,28 @@ loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQ code->mainWorkWrite = msgpipe[1]; code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); - code->activity.callbacks = &code->callbacks; - if (env->GetJavaVM(&code->activity.vm) < 0) { + code->ANativeActivity::callbacks = &code->callbacks; + if (env->GetJavaVM(&code->vm) < 0) { LOGW("NativeActivity GetJavaVM failed"); delete code; return 0; } - code->activity.env = env; - code->activity.clazz = env->NewGlobalRef(clazz); + code->env = env; + code->clazz = env->NewGlobalRef(clazz); const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL); code->internalDataPath = dirStr; - code->activity.internalDataPath = code->internalDataPath.string(); + code->internalDataPath = code->internalDataPath.string(); env->ReleaseStringUTFChars(path, dirStr); dirStr = env->GetStringUTFChars(externalDataDir, NULL); code->externalDataPath = dirStr; - code->activity.externalDataPath = code->externalDataPath.string(); + code->externalDataPath = code->externalDataPath.string(); env->ReleaseStringUTFChars(path, dirStr); - code->activity.sdkVersion = sdkVersion; + code->sdkVersion = sdkVersion; - code->createActivityFunc(&code->activity, NULL, 0); + code->createActivityFunc(code, NULL, 0); } return (jint)code; @@ -285,7 +358,7 @@ onStart_native(JNIEnv* env, jobject clazz, jint handle) if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onStart != NULL) { - code->callbacks.onStart(&code->activity); + code->callbacks.onStart(code); } } } @@ -296,7 +369,7 @@ onResume_native(JNIEnv* env, jobject clazz, jint handle) if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onResume != NULL) { - code->callbacks.onResume(&code->activity); + code->callbacks.onResume(code); } } } @@ -308,7 +381,7 @@ onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) NativeCode* code = (NativeCode*)handle; if (code->callbacks.onSaveInstanceState != NULL) { size_t len = 0; - code->callbacks.onSaveInstanceState(&code->activity, &len); + code->callbacks.onSaveInstanceState(code, &len); } } } @@ -319,7 +392,7 @@ onPause_native(JNIEnv* env, jobject clazz, jint handle) if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onPause != NULL) { - code->callbacks.onPause(&code->activity); + code->callbacks.onPause(code); } } } @@ -330,7 +403,7 @@ onStop_native(JNIEnv* env, jobject clazz, jint handle) if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onStop != NULL) { - code->callbacks.onStop(&code->activity); + code->callbacks.onStop(code); } } } @@ -341,7 +414,7 @@ onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onLowMemory != NULL) { - code->callbacks.onLowMemory(&code->activity); + code->callbacks.onLowMemory(code); } } } @@ -352,7 +425,7 @@ onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean fo if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->callbacks.onWindowFocusChanged != NULL) { - code->callbacks.onWindowFocusChanged(&code->activity, focused ? 1 : 0); + code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0); } } } @@ -364,7 +437,7 @@ onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface NativeCode* code = (NativeCode*)handle; code->setSurface(surface); if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { - code->callbacks.onNativeWindowCreated(&code->activity, + code->callbacks.onNativeWindowCreated(code, code->nativeWindow.get()); } } @@ -380,11 +453,11 @@ onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface code->setSurface(surface); if (oldNativeWindow != code->nativeWindow) { if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { - code->callbacks.onNativeWindowDestroyed(&code->activity, + code->callbacks.onNativeWindowDestroyed(code, oldNativeWindow.get()); } if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { - code->callbacks.onNativeWindowCreated(&code->activity, + code->callbacks.onNativeWindowCreated(code, code->nativeWindow.get()); } } @@ -397,7 +470,7 @@ onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surfa if (handle != 0) { NativeCode* code = (NativeCode*)handle; if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { - code->callbacks.onNativeWindowDestroyed(&code->activity, + code->callbacks.onNativeWindowDestroyed(code, code->nativeWindow.get()); } code->setSurface(NULL); @@ -416,7 +489,7 @@ onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject ch return; } if (code->callbacks.onInputQueueCreated != NULL) { - code->callbacks.onInputQueueCreated(&code->activity, + code->callbacks.onInputQueueCreated(code, code->nativeInputQueue); } } @@ -429,7 +502,7 @@ onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject NativeCode* code = (NativeCode*)handle; if (code->nativeInputQueue != NULL && code->callbacks.onInputQueueDestroyed != NULL) { - code->callbacks.onInputQueueDestroyed(&code->activity, + code->callbacks.onInputQueueDestroyed(code, code->nativeInputQueue); } code->setInputChannel(NULL); diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp index 961f806..847b5a5 100644 --- a/core/jni/android_os_MessageQueue.cpp +++ b/core/jni/android_os_MessageQueue.cpp @@ -53,7 +53,7 @@ private: NativeMessageQueue::NativeMessageQueue() { mPollLoop = PollLoop::getForThread(); if (mPollLoop == NULL) { - mPollLoop = new PollLoop(); + mPollLoop = new PollLoop(false); PollLoop::setForThread(mPollLoop); } } @@ -62,7 +62,7 @@ NativeMessageQueue::~NativeMessageQueue() { } bool NativeMessageQueue::pollOnce(int timeoutMillis) { - return mPollLoop->pollOnce(timeoutMillis); + return mPollLoop->pollOnce(timeoutMillis) != PollLoop::POLL_TIMEOUT; } void NativeMessageQueue::wake() { diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index a82abc9..7305032 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -33,7 +33,7 @@ #include "jni.h" #include <android_runtime/AndroidRuntime.h> -#include "android_view_Surface.h" +#include <android_runtime/android_view_Surface.h> #include <utils/misc.h> diff --git a/core/jni/com_google_android_gles_jni_EGLImpl.cpp b/core/jni/com_google_android_gles_jni_EGLImpl.cpp index 866c038..941ed63 100644 --- a/core/jni/com_google_android_gles_jni_EGLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_EGLImpl.cpp @@ -16,6 +16,7 @@ */ #include <android_runtime/AndroidRuntime.h> +#include <android_runtime/android_view_Surface.h> #include <utils/misc.h> #include <EGL/egl.h> @@ -25,8 +26,6 @@ #include <SkBitmap.h> #include <SkPixelRef.h> -#include "android_view_Surface.h" - namespace android { static jclass gDisplay_class; diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png Binary files differdeleted file mode 100644 index 2d13237..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_phone_call_bluetooth.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png Binary files differdeleted file mode 100644 index 950713b..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_phone_call_ringing.png +++ /dev/null diff --git a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png Binary files differdeleted file mode 100644 index 2d13237..0000000 --- a/core/res/res/drawable-hdpi/stat_sys_vp_phone_call_bluetooth.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png Binary files differdeleted file mode 100644 index 7abfd19..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_phone_call_bluetooth.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png b/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png Binary files differdeleted file mode 100644 index c44d062..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_phone_call_ringing.png +++ /dev/null diff --git a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png Binary files differdeleted file mode 100644 index 7abfd19..0000000 --- a/core/res/res/drawable-mdpi/stat_sys_vp_phone_call_bluetooth.png +++ /dev/null diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 27cb763..6c13da6 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1246,6 +1246,7 @@ <public type="attr" name="logo" id="0x010102be" /> <public type="attr" name="xlargeScreens" id="0x010102bf" /> <public type="attr" name="heavyWeight" id="0x010102c0" /> + <public type="attr" name="immersive" id="0x010102c1" /> <public-padding type="attr" name="kraken_resource_pad" end="0x01010300" /> <public-padding type="id" name="kraken_resource_pad" end="0x01020040" /> diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd new file mode 100644 index 0000000..81b4ff6 --- /dev/null +++ b/docs/html/sdk/download.jd @@ -0,0 +1,4 @@ +sdk.redirect=true + +@jd:body + diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h new file mode 100644 index 0000000..f808328 --- /dev/null +++ b/include/android_runtime/android_app_NativeActivity.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + +#ifndef _ANDROID_APP_NATIVEACTIVITY_H +#define _ANDROID_APP_NATIVEACTIVITY_H + +#include <android/native_activity.h> + +#include "jni.h" + +namespace android { + +extern void android_NativeActivity_setWindowFormat( + ANativeActivity* activity, int32_t format); + +extern void android_NativeActivity_setWindowFlags( + ANativeActivity* activity, int32_t values, int32_t mask); + + +} // namespace android + +#endif // _ANDROID_APP_NATIVEACTIVITY_H diff --git a/core/jni/android_view_Surface.h b/include/android_runtime/android_view_Surface.h index c37932e..c37932e 100644 --- a/core/jni/android_view_Surface.h +++ b/include/android_runtime/android_view_Surface.h diff --git a/include/media/EffectBassBoostApi.h b/include/media/EffectBassBoostApi.h new file mode 100644 index 0000000..b24a5f4 --- /dev/null +++ b/include/media/EffectBassBoostApi.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + +#ifndef ANDROID_EFFECTBASSBOOSTAPI_H_ +#define ANDROID_EFFECTBASSBOOSTAPI_H_ + +#include <media/EffectApi.h> + +#if __cplusplus +extern "C" { +#endif + +// TODO: include OpenSLES_IID.h instead +static const effect_uuid_t SL_IID_BASSBOOST_ = { 0x0634f220, 0xddd4, 0x11db, 0xa0fc, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; +const effect_uuid_t * const SL_IID_BASSBOOST = &SL_IID_BASSBOOST_; + +/* enumerated parameter settings for BassBoost effect */ +typedef enum +{ + BASSBOOST_PARAM_STRENGTH_SUPPORTED, + BASSBOOST_PARAM_STRENGTH +} t_bassboost_params; + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTBASSBOOSTAPI_H_*/ diff --git a/include/media/EffectReverbApi.h b/include/media/EffectEnvironmentalReverbApi.h index 6371adb..d490f71 100644 --- a/include/media/EffectReverbApi.h +++ b/include/media/EffectEnvironmentalReverbApi.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef ANDROID_EFFECTREVERBAPI_H_ -#define ANDROID_EFFECTREVERBAPI_H_ +#ifndef ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_ +#define ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_ #include <media/EffectApi.h> @@ -27,14 +27,9 @@ extern "C" { static const effect_uuid_t SL_IID_ENVIRONMENTALREVERB_ = { 0xc2e5d5f0, 0x94bd, 0x4763, 0x9cac, { 0x4e, 0x23, 0x4d, 0x6, 0x83, 0x9e } }; const effect_uuid_t * const SL_IID_ENVIRONMENTALREVERB = &SL_IID_ENVIRONMENTALREVERB_; -static const effect_uuid_t SL_IID_PRESETREVERB_ = { 0x47382d60, 0xddd8, 0x11db, 0xbf3a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; -const effect_uuid_t * const SL_IID_PRESETREVERB = &SL_IID_PRESETREVERB_; - -/* enumerated parameter settings for Reverb effect */ +/* enumerated parameter settings for environmental reverb effect */ typedef enum { - REVERB_PARAM_BYPASS, - REVERB_PARAM_PRESET, // Parameters below are as defined in OpenSL ES specification for environmental reverb interface REVERB_PARAM_ROOM_LEVEL, // in millibels, range -6000 to 0 REVERB_PARAM_ROOM_HF_LEVEL, // in millibels, range -4000 to 0 @@ -46,17 +41,9 @@ typedef enum REVERB_PARAM_REVERB_DELAY, // in milliseconds, range 0 to 65 REVERB_PARAM_DIFFUSION, // in permilles, range 0 to 1000 REVERB_PARAM_DENSITY, // in permilles, range 0 to 1000 - REVERB_PARAM_PROPERTIES -} t_reverb_params; - - -typedef enum -{ - REVERB_PRESET_LARGE_HALL, - REVERB_PRESET_HALL, - REVERB_PRESET_CHAMBER, - REVERB_PRESET_ROOM, -} t_reverb_presets; + REVERB_PARAM_PROPERTIES, + REVERB_PARAM_BYPASS +} t_env_reverb_params; //t_reverb_properties is equal to SLEnvironmentalReverbSettings defined in OpenSL ES specification. typedef struct s_reverb_properties { @@ -79,4 +66,4 @@ typedef struct s_reverb_properties { #endif -#endif /*ANDROID_EFFECTREVERBAPI_H_*/ +#endif /*ANDROID_EFFECTENVIRONMENTALREVERBAPI_H_*/ diff --git a/include/media/EffectPresetReverbApi.h b/include/media/EffectPresetReverbApi.h new file mode 100644 index 0000000..34ffffe --- /dev/null +++ b/include/media/EffectPresetReverbApi.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + +#ifndef ANDROID_EFFECTPRESETREVERBAPI_H_ +#define ANDROID_EFFECTPRESETREVERBAPI_H_ + +#include <media/EffectApi.h> + +#if __cplusplus +extern "C" { +#endif + +// TODO: include OpenSLES_IID.h instead + +static const effect_uuid_t SL_IID_PRESETREVERB_ = { 0x47382d60, 0xddd8, 0x11db, 0xbf3a, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; +const effect_uuid_t * const SL_IID_PRESETREVERB = &SL_IID_PRESETREVERB_; + +/* enumerated parameter settings for preset reverb effect */ +typedef enum +{ + REVERB_PARAM_PRESET +} t_preset_reverb_params; + + +typedef enum +{ + REVERB_PRESET_NONE, + REVERB_PRESET_SMALLROOM, + REVERB_PRESET_MEDIUMROOM, + REVERB_PRESET_LARGEROOM, + REVERB_PRESET_MEDIUMHALL, + REVERB_PRESET_LARGEHALL, + REVERB_PRESET_PLATE +} t_reverb_presets; + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTPRESETREVERBAPI_H_*/ diff --git a/include/media/EffectVirtualizerApi.h b/include/media/EffectVirtualizerApi.h new file mode 100644 index 0000000..601c384 --- /dev/null +++ b/include/media/EffectVirtualizerApi.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + +#ifndef ANDROID_EFFECTVIRTUALIZERAPI_H_ +#define ANDROID_EFFECTVIRTUALIZERAPI_H_ + +#include <media/EffectApi.h> + +#if __cplusplus +extern "C" { +#endif + +// TODO: include OpenSLES_IID.h instead +static const effect_uuid_t SL_IID_VIRTUALIZER_ = { 0x37cc2c00, 0xdddd, 0x11db, 0x8577, { 0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b } }; +const effect_uuid_t * const SL_IID_VIRTUALIZER = &SL_IID_VIRTUALIZER_; + +/* enumerated parameter settings for virtualizer effect */ +typedef enum +{ + VIRTUALIZER_PARAM_STRENGTH_SUPPORTED, + VIRTUALIZER_PARAM_STRENGTH +} t_virtualizer_params; + +#if __cplusplus +} // extern "C" +#endif + + +#endif /*ANDROID_EFFECTVIRTUALIZERAPI_H_*/ diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h index 497965c..5e9e368 100644 --- a/include/media/MediaRecorderBase.h +++ b/include/media/MediaRecorderBase.h @@ -48,6 +48,7 @@ struct MediaRecorderBase { virtual status_t close() = 0; virtual status_t reset() = 0; virtual status_t getMaxAmplitude(int *max) = 0; + virtual status_t dump(int fd, const Vector<String16>& args) const = 0; private: MediaRecorderBase(const MediaRecorderBase &); diff --git a/include/media/PVMediaRecorder.h b/include/media/PVMediaRecorder.h index c04105e..f75d80d 100644 --- a/include/media/PVMediaRecorder.h +++ b/include/media/PVMediaRecorder.h @@ -52,6 +52,7 @@ public: virtual status_t close(); virtual status_t reset(); virtual status_t getMaxAmplitude(int *max); + virtual status_t dump(int fd, const Vector<String16>& args) const; private: status_t doStop(); diff --git a/include/ui/Rect.h b/include/ui/Rect.h index a213c09..4e65a2d 100644 --- a/include/ui/Rect.h +++ b/include/ui/Rect.h @@ -20,31 +20,28 @@ #include <utils/TypeHelpers.h> #include <ui/Point.h> +#include <android/rect.h> + namespace android { -class Rect +class Rect : public ARect { public: - int left; - int top; - int right; - int bottom; - - typedef int value_type; + typedef int32_t value_type; // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions inline Rect() { } - inline Rect(int w, int h) - : left(0), top(0), right(w), bottom(h) { + inline Rect(int32_t w, int32_t h) { + left = top = 0; right = w; bottom = h; } - inline Rect(int l, int t, int r, int b) - : left(l), top(t), right(r), bottom(b) { + inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) { + left = l; top = t; right = r; bottom = b; } - inline Rect(const Point& lt, const Point& rb) - : left(lt.x), top(lt.y), right(rb.x), bottom(rb.y) { + inline Rect(const Point& lt, const Point& rb) { + left = lt.x; top = lt.y; right = rb.x; bottom = rb.y; } void makeInvalid(); @@ -68,12 +65,12 @@ public: } // rectangle's width - inline int width() const { + inline int32_t width() const { return right-left; } // rectangle's height - inline int height() const { + inline int32_t height() const { return bottom-top; } @@ -136,12 +133,12 @@ public: const Rect operator + (const Point& rhs) const; const Rect operator - (const Point& rhs) const; - void translate(int dx, int dy) { // legacy, don't use. + void translate(int32_t dx, int32_t dy) { // legacy, don't use. offsetBy(dx, dy); } - Rect& offsetTo(int x, int y); - Rect& offsetBy(int x, int y); + Rect& offsetTo(int32_t x, int32_t y); + Rect& offsetBy(int32_t x, int32_t y); bool intersect(const Rect& with, Rect* result) const; }; diff --git a/include/utils/PollLoop.h b/include/utils/PollLoop.h index b3651ca..81230e8 100644 --- a/include/utils/PollLoop.h +++ b/include/utils/PollLoop.h @@ -42,7 +42,7 @@ protected: virtual ~PollLoop(); public: - PollLoop(); + PollLoop(bool allowNonCallbacks); /** * A callback that it to be invoked when an event occurs on a file descriptor. @@ -54,6 +54,12 @@ public: */ typedef bool (*Callback)(int fd, int events, void* data); + enum { + POLL_CALLBACK = ALOOPER_POLL_CALLBACK, + POLL_TIMEOUT = ALOOPER_POLL_TIMEOUT, + POLL_ERROR = ALOOPER_POLL_ERROR, + }; + /** * Performs a single call to poll() with optional timeout in milliseconds. * Invokes callbacks for all file descriptors on which an event occurred. @@ -61,16 +67,25 @@ public: * If the timeout is zero, returns immediately without blocking. * If the timeout is negative, waits indefinitely until awoken. * - * Returns true if a callback was invoked or if the loop was awoken by wake(). - * Returns false if a timeout or error occurred. + * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOPER_POLL_ERROR if an error occurred. * - * This method must only be called on the main thread. + * Returns a value >= 0 containing a file descriptor if it has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outEvents and outData will contain the poll + * events and data associated with the fd. + * + * This method must only be called on the thread owning the PollLoop. * This method blocks until either a file descriptor is signalled, a timeout occurs, * or wake() is called. * This method does not return until it has finished invoking the appropriate callbacks * for all file descriptors that were signalled. */ - bool pollOnce(int timeoutMillis); + int32_t pollOnce(int timeoutMillis, int* outEvents = NULL, void** outData = NULL); /** * Wakes the loop asynchronously. @@ -81,6 +96,12 @@ public: void wake(); /** + * Control whether this PollLoop instance allows using IDs instead + * of callbacks. + */ + bool getAllowNonCallbacks() const; + + /** * Sets the callback for a file descriptor, replacing the existing one, if any. * It is an error to call this method with events == 0 or callback == NULL. * @@ -95,7 +116,8 @@ public: /** * Like setCallback(), but for the NDK callback function. */ - void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, void* data); + void setLooperCallback(int fd, int events, ALooper_callbackFunc* callback, + void* data); /** * Removes the callback for a file descriptor, if one exists. @@ -141,7 +163,9 @@ private: ALooper_callbackFunc* looperCallback; void* data; }; - + + const bool mAllowNonCallbacks; + Mutex mLock; bool mPolling; uint32_t mWaiters; @@ -155,7 +179,9 @@ private: Vector<RequestedCallback> mRequestedCallbacks; Vector<PendingCallback> mPendingCallbacks; // used privately by pollOnce - + Vector<PendingCallback> mPendingFds; // used privately by pollOnce + size_t mPendingFdsPos; + void openWakePipe(); void closeWakePipe(); diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index 42a7fc6..f809cba 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -54,7 +54,7 @@ static inline nsecs_t now() { InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : mPolicy(policy) { - mPollLoop = new PollLoop(); + mPollLoop = new PollLoop(false); mInboundQueue.head.refCount = -1; mInboundQueue.head.type = EventEntry::TYPE_SENTINEL; diff --git a/libs/ui/Rect.cpp b/libs/ui/Rect.cpp index 66b9576..5694e00 100644 --- a/libs/ui/Rect.cpp +++ b/libs/ui/Rect.cpp @@ -18,11 +18,11 @@ namespace android { -static inline int min(int a, int b) { +static inline int32_t min(int32_t a, int32_t b) { return (a<b) ? a : b; } -static inline int max(int a, int b) { +static inline int32_t max(int32_t a, int32_t b) { return (a>b) ? a : b; } @@ -53,7 +53,7 @@ bool Rect::operator < (const Rect& rhs) const return false; } -Rect& Rect::offsetTo(int x, int y) +Rect& Rect::offsetTo(int32_t x, int32_t y) { right -= left - x; bottom -= top - y; @@ -62,7 +62,7 @@ Rect& Rect::offsetTo(int x, int y) return *this; } -Rect& Rect::offsetBy(int x, int y) +Rect& Rect::offsetBy(int32_t x, int32_t y) { left += x; top += y; diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 58fe141..f740fa0 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -25,8 +25,9 @@ static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER; static bool gHaveTLS = false; static pthread_key_t gTLS = 0; -PollLoop::PollLoop() : - mPolling(false), mWaiters(0) { +PollLoop::PollLoop(bool allowNonCallbacks) : + mAllowNonCallbacks(allowNonCallbacks), mPolling(false), + mWaiters(0), mPendingFdsPos(0) { openWakePipe(); } @@ -106,7 +107,18 @@ void PollLoop::closeWakePipe() { // method is currently only called by the destructor. } -bool PollLoop::pollOnce(int timeoutMillis) { +int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { + // If there are still pending fds from the last call, dispatch those + // first, to avoid an earlier fd from starving later ones. + const size_t pendingFdsCount = mPendingFds.size(); + if (mPendingFdsPos < pendingFdsCount) { + const PendingCallback& pending = mPendingFds.itemAt(mPendingFdsPos); + mPendingFdsPos++; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; + return pending.fd; + } + mLock.lock(); while (mWaiters != 0) { mResume.wait(mLock); @@ -114,7 +126,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { mPolling = true; mLock.unlock(); - bool result; + int32_t result; size_t requestedCount = mRequestedFds.size(); #if DEBUG_POLL_AND_WAKE @@ -131,7 +143,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { #if DEBUG_POLL_AND_WAKE LOGD("%p ~ pollOnce - timeout", this); #endif - result = false; + result = POLL_TIMEOUT; goto Done; } @@ -143,7 +155,7 @@ bool PollLoop::pollOnce(int timeoutMillis) { if (errno != EINTR) { LOGW("Poll failed with an unexpected error, errno=%d", errno); } - result = false; + result = POLL_ERROR; goto Done; } @@ -156,38 +168,44 @@ bool PollLoop::pollOnce(int timeoutMillis) { #endif mPendingCallbacks.clear(); + mPendingFds.clear(); + mPendingFdsPos = 0; + if (outEvents != NULL) *outEvents = 0; + if (outData != NULL) *outData = NULL; + + result = POLL_CALLBACK; for (size_t i = 0; i < requestedCount; i++) { const struct pollfd& requestedFd = mRequestedFds.itemAt(i); short revents = requestedFd.revents; if (revents) { const RequestedCallback& requestedCallback = mRequestedCallbacks.itemAt(i); - Callback callback = requestedCallback.callback; - ALooper_callbackFunc* looperCallback = requestedCallback.looperCallback; - - if (callback || looperCallback) { - PendingCallback pendingCallback; - pendingCallback.fd = requestedFd.fd; - pendingCallback.events = requestedFd.revents; - pendingCallback.callback = callback; - pendingCallback.looperCallback = looperCallback; - pendingCallback.data = requestedCallback.data; - mPendingCallbacks.push(pendingCallback); + PendingCallback pending; + pending.fd = requestedFd.fd; + pending.events = revents; + pending.callback = requestedCallback.callback; + pending.looperCallback = requestedCallback.looperCallback; + pending.data = requestedCallback.data; + + if (pending.callback || pending.looperCallback) { + mPendingCallbacks.push(pending); + } else if (pending.fd != mWakeReadPipeFd) { + if (result == POLL_CALLBACK) { + result = pending.fd; + if (outEvents != NULL) *outEvents = pending.events; + if (outData != NULL) *outData = pending.data; + } else { + mPendingFds.push(pending); + } } else { - if (requestedFd.fd == mWakeReadPipeFd) { #if DEBUG_POLL_AND_WAKE - LOGD("%p ~ pollOnce - awoken", this); -#endif - char buffer[16]; - ssize_t nRead; - do { - nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); - } while (nRead == sizeof(buffer)); - } else { -#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS - LOGD("%p ~ pollOnce - fd %d has no callback!", this, requestedFd.fd); + LOGD("%p ~ pollOnce - awoken", this); #endif - } + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while (nRead == sizeof(buffer)); } respondedCount -= 1; @@ -196,7 +214,6 @@ bool PollLoop::pollOnce(int timeoutMillis) { } } } - result = true; Done: mLock.lock(); @@ -206,7 +223,7 @@ Done: } mLock.unlock(); - if (result) { + if (result == POLL_CALLBACK || result >= 0) { size_t pendingCount = mPendingCallbacks.size(); for (size_t i = 0; i < pendingCount; i++) { const PendingCallback& pendingCallback = mPendingCallbacks.itemAt(i); @@ -247,6 +264,10 @@ void PollLoop::wake() { } } +bool PollLoop::getAllowNonCallbacks() const { + return mAllowNonCallbacks; +} + void PollLoop::setCallback(int fd, int events, Callback callback, void* data) { setCallbackCommon(fd, events, callback, NULL, data); } @@ -263,12 +284,18 @@ void PollLoop::setCallbackCommon(int fd, int events, Callback callback, LOGD("%p ~ setCallback - fd=%d, events=%d", this, fd, events); #endif - if (! events || (! callback && ! looperCallback)) { - LOGE("Invalid attempt to set a callback with no selected poll events or no callback."); + if (! events) { + LOGE("Invalid attempt to set a callback with no selected poll events."); removeCallback(fd); return; } + if (! callback && ! looperCallback && ! mAllowNonCallbacks) { + LOGE("Invalid attempt to set NULL callback but not allowed."); + removeCallback(fd); + return; + } + wakeAndLock(); struct pollfd requestedFd; diff --git a/libs/utils/tests/PollLoop_test.cpp b/libs/utils/tests/PollLoop_test.cpp index 4848c0f..02f1808 100644 --- a/libs/utils/tests/PollLoop_test.cpp +++ b/libs/utils/tests/PollLoop_test.cpp @@ -87,7 +87,7 @@ protected: sp<PollLoop> mPollLoop; virtual void SetUp() { - mPollLoop = new PollLoop(); + mPollLoop = new PollLoop(false); } virtual void TearDown() { @@ -98,26 +98,26 @@ protected: TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeoutAndReturnsFalse) { StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; } TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturnsTrue) { mPollLoop->wake(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because wake() was called before waiting"; - EXPECT_TRUE(result) - << "pollOnce result should be true because loop was awoken"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; } TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturnsTrue) { @@ -125,24 +125,24 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyRe delayedWake->run(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal wake delay"; - EXPECT_TRUE(result) - << "pollOnce result should be true because loop was awoken"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because loop was awoken"; } TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturnsFalse) { StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; } TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturnsFalse) { @@ -152,13 +152,13 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturn handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -171,13 +171,13 @@ TEST_F(PollLoopTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCa handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -193,13 +193,13 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeou handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not have been invoked because FD was not signalled"; } @@ -212,15 +212,15 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_Imme handler.setCallback(mPollLoop, pipe.receiveFd, POLL_IN); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should be approx. zero"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -238,15 +238,15 @@ TEST_F(PollLoopTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_Promp delayedWriteSignal->run(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(1000); + int32_t result = mPollLoop->pollOnce(1000); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal signal delay"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked exactly once"; EXPECT_EQ(pipe.receiveFd, handler.fd) @@ -264,15 +264,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeIn mPollLoop->removeCallback(pipe.receiveFd); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal timeout because FD was no longer registered"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(0, handler.callbackCount) << "callback should not be invoked"; } @@ -287,15 +287,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke pipe.writeSignal(); StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(0); + int32_t result = mPollLoop->pollOnce(0); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because FD was already signalled"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(1, handler.callbackCount) << "callback should be invoked"; @@ -310,8 +310,8 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvoke << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. equal zero because timeout was zero"; - EXPECT_FALSE(result) - << "pollOnce result should be false because timeout occurred"; + EXPECT_EQ(result, PollLoop::POLL_TIMEOUT) + << "pollOnce result should be POLL_TIMEOUT"; EXPECT_EQ(1, handler.callbackCount) << "callback should not be invoked this time"; } @@ -351,15 +351,15 @@ TEST_F(PollLoopTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeI pipe.writeSignal(); // would cause FD to be considered signalled StopWatch stopWatch("pollOnce"); - bool result = mPollLoop->pollOnce(100); + int32_t result = mPollLoop->pollOnce(100); int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime()); ASSERT_EQ(OK, pipe.readSignal()) << "signal should actually have been written"; EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS) << "elapsed time should approx. zero because FD was already signalled"; - EXPECT_TRUE(result) - << "pollOnce result should be true because FD was signalled"; + EXPECT_EQ(result, PollLoop::POLL_CALLBACK) + << "pollOnce result should be POLL_CALLBACK because FD was signalled"; EXPECT_EQ(0, handler1.callbackCount) << "original handler callback should not be invoked because it was replaced"; EXPECT_EQ(1, handler2.callbackCount) diff --git a/media/java/android/media/BassBoost.java b/media/java/android/media/BassBoost.java new file mode 100644 index 0000000..ef4ce05 --- /dev/null +++ b/media/java/android/media/BassBoost.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * 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 android.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import android.media.AudioEffect; + +/** + * Bass boost is an audio effect to boost or amplify low frequencies of the sound. It is comparable + * to an simple equalizer but limited to one band amplification in the low frequency range. + * <p>An application creates a BassBoost object to instantiate and control a bass boost engine + * in the audio framework. + * <p>The methods, parameter types and units exposed by the BassBoost implementation are directly + * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) + * for the SLBassBoostItf interface. Please refer to this specification for more details. + * <p>To attach the BassBoost to a particular AudioTrack or MediaPlayer, specify the audio session + * ID of this AudioTrack or MediaPlayer when constructing the BassBoost. If the audio session ID 0 + * is specified, the BassBoost applies to the main audio output mix. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class BassBoost extends AudioEffect { + + private final static String TAG = "BassBoost"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectBassBoostApi.h + /** + * Is strength parameter supported by bass boost engine. Parameter ID for getParameter(). + */ + public static final int PARAM_STRENGTH_SUPPORTED = 0; + /** + * Bass boost effect strength. Parameter ID for + * {@link android.media.BassBoost.OnParameterChangeListener} + */ + public static final int PARAM_STRENGTH = 1; + + /** + * Indicates if strength parameter is supported by the bass boost engine + */ + private boolean mStrengthSupported = false; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the BassBoost + * engine. As the same engine can be shared by several applications, this parameter indicates + * how much the requesting application needs control of effect parameters. The normal priority + * is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the BassBoost will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the BassBoost will apply to the output mix. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public BassBoost(int priority, int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_BASS_BOOST, EFFECT_TYPE_NULL, priority, audioSession); + + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value)); + mStrengthSupported = (value[0] != 0); + } + + /** + * Indicates whether setting strength is supported. If this method returns false, only one + * strength is supported and the setStrength() method always rounds to that value. + * @return true is strength parameter is supported, false otherwise + */ + public boolean getStrengthSupported() { + return mStrengthSupported; + } + + /** + * Sets the strength of the bass boost effect. If the implementation does not support per mille + * accuracy for setting the strength, it is allowed to round the given strength to the nearest + * supported value. You can use the {@link #getRoundedStrength()} method to query the + * (possibly rounded) value that was actually set. + * @param strength Strength of the effect. The valid range for strength strength is [0, 1000], + * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setStrength(short strength) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_STRENGTH, strength)); + } + + /** + * Gets the current strength of the effect. + * @return The strength of the effect. The valid range for strength is [0, 1000], where 0 per + * mille designates the mildest effect and 1000 per mille the strongest + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoundedStrength() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH, value)); + return value[0]; + } + + /** + * The OnParameterChangeListener interface defines a method called by the BassBoost when a + * parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * BassBoost engine. + * @param effect the BassBoost on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ... + * @param value the new parameter value. + */ + void onParameterChange(BassBoost effect, int status, int param, short value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + short v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = byteArrayToShort(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(BassBoost.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/java/android/media/EnvironmentalReverb.java b/media/java/android/media/EnvironmentalReverb.java new file mode 100644 index 0000000..88230fc --- /dev/null +++ b/media/java/android/media/EnvironmentalReverb.java @@ -0,0 +1,504 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * 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 android.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; + +import android.media.AudioEffect; + +/** + * A sound generated within a room travels in many directions. The listener first hears the + * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound + * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after + * undergoing more and more reflections, individual reflections become indistinguishable and + * the listener hears continuous reverberation that decays over time. + * Reverb is vital for modeling a listener's environment. It can be used in music applications + * to simulate music being played back in various environments, or in games to immerse the + * listener within the game's environment. + * The EnvironmentalReverb class allows an application to control each reverb engine property in a + * global reverb environment and is more suitable for games. For basic control, more suitable for + * music applications, it is recommended to use the + // TODO when PresetReverb is unhidden + // {_at_link android.media.PresetReverb} class. + * <p>An application creates a EnvironmentalReverb object to instantiate and control a reverb engine + * in the audio framework. + * <p>The methods, parameter types and units exposed by the EnvironmentalReverb implementation are + * directly mapping those defined by the OpenSL ES 1.0.1 Specification + * (http://www.khronos.org/opensles/) for the SLEnvironmentalReverbItf interface. + * Please refer to this specification for more details. + * <p>The EnvironmentalReverb is an output mix auxiliary effect and should be created on + * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect, + * they must be explicitely attached to it and a send level must be specified. Use the effect ID + * returned by getId() method to designate this particular effect when attaching it to the + * MediaPlayer or AudioTrack. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling + * audio effects. + * + * {@hide Pending API council review} + */ + +public class EnvironmentalReverb extends AudioEffect { + + private final static String TAG = "EnvironmentalReverb"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectEnvironmentalReverbApi.h + + /** + * Room level. Parameter ID for + * {@link android.media.EnvironmentalReverb.OnParameterChangeListener} + */ + public static final int PARAM_ROOM_LEVEL = 0; + /** + * Room HF level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_ROOM_HF_LEVEL = 1; + /** + * Decay time. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DECAY_TIME = 2; + /** + * Decay HF ratio. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DECAY_HF_RATIO = 3; + /** + * Early reflections level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REFLECTIONS_LEVEL = 4; + /** + * Early reflections delay. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REFLECTIONS_DELAY = 5; + /** + * Reverb level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REVERB_LEVEL = 6; + /** + * Reverb delay. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_REVERB_DELAY = 7; + /** + * Diffusion. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DIFFUSION = 8; + /** + * Density. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_DENSITY = 9; + + /** + * Registered listener for parameter changes + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super + * class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the + * EnvironmentalReverb engine. As the same engine can be shared by several applications, this + * parameter indicates how much the requesting application needs control of effect parameters. + * The normal priority is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the EnvironmentalReverb will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the EnvironmentalReverb will apply to the output mix. + * As the EnvironmentalReverb is an auxiliary effect it is recommended to instantiate it on + * audio session 0 and to attach it to the MediaPLayer auxiliary output. + * + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public EnvironmentalReverb(int priority, int audioSession) + throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_ENV_REVERB, EFFECT_TYPE_NULL, priority, audioSession); + Log.e(TAG, "contructor"); + } + + /** + * Sets the master volume level of the environmental reverb effect. + * @param room Room level in millibels. The valid range is [-9000, 0]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setRoomLevel(short room) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(room); + checkStatus(setParameter(PARAM_ROOM_LEVEL, param)); + } + + /** + * Gets the master volume level of the environmental reverb effect. + * @return the room level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoomLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_ROOM_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the volume level at 5 kHz relative to the volume level at low frequencies of the + * overall reverb effect. + * <p>This controls a low-pass filter that will reduce the level of the high-frequency. + * @param roomHF High frequency attenuation level in millibels. The valid range is [-9000, 0]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setRoomHFLevel(short roomHF) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(roomHF); + checkStatus(setParameter(PARAM_ROOM_HF_LEVEL, param)); + } + + /** + * Gets the room HF level. + * @return the room HF level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoomHFLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_ROOM_HF_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the time taken for the level of reverberation to decay by 60 dB. + * @param decayTime Decay time in milliseconds. The valid range is [100, 20000]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDecayTime(int decayTime) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = intToByteArray(decayTime); + checkStatus(setParameter(PARAM_DECAY_TIME, param)); + } + + /** + * Gets the decay time. + * @return the decay time in milliseconds. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getDecayTime() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[4]; + checkStatus(getParameter(PARAM_DECAY_TIME, param)); + return byteArrayToInt(param); + } + + /** + * Sets the ratio of high frequency decay time (at 5 kHz) relative to the decay time at low + * frequencies. + * @param decayHFRatio High frequency decay ratio using a permille scale. The valid range is + * [100, 2000]. A ratio of 1000 indicates that all frequencies decay at the same rate. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDecayHFRatio(short decayHFRatio) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(decayHFRatio); + checkStatus(setParameter(PARAM_DECAY_HF_RATIO, param)); + } + + /** + * Gets the ratio of high frequency decay time (at 5 kHz) relative to low frequencies. + * @return the decay HF ration. See {@link #setDecayHFRatio(short)} for units. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getDecayHFRatio() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_DECAY_HF_RATIO, param)); + return byteArrayToShort(param); + } + + /** + * Sets the volume level of the early reflections. + * <p>This level is combined with the overall room level + * (set using {@link #setRoomLevel(short)}). + * @param reflectionsLevel Reflection level in millibels. The valid range is [-9000, 1000]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReflectionsLevel(short reflectionsLevel) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(reflectionsLevel); + checkStatus(setParameter(PARAM_REFLECTIONS_LEVEL, param)); + } + + /** + * Gets the volume level of the early reflections. + * @return the early reflections level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getReflectionsLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_REFLECTIONS_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the delay time for the early reflections. + * <p>This method sets the time between when the direct path is heard and when the first + * reflection is heard. + * @param reflectionsDelay Reflections delay in milliseconds. The valid range is [0, 300]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReflectionsDelay(int reflectionsDelay) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = intToByteArray(reflectionsDelay); + checkStatus(setParameter(PARAM_REFLECTIONS_DELAY, param)); + } + + /** + * Gets the reflections delay. + * @return the early reflections delay in milliseconds. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getReflectionsDelay() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[4]; + checkStatus(getParameter(PARAM_REFLECTIONS_DELAY, param)); + return byteArrayToInt(param); + } + + /** + * Sets the volume level of the late reverberation. + * <p>This level is combined with the overall room level (set using {@link #setRoomLevel(short)}). + * @param reverbLevel Reverb level in millibels. The valid range is [-9000, 2000]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReverbLevel(short reverbLevel) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(reverbLevel); + checkStatus(setParameter(PARAM_REVERB_LEVEL, param)); + } + + /** + * Gets the reverb level. + * @return the reverb level in millibels. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getReverbLevel() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_REVERB_LEVEL, param)); + return byteArrayToShort(param); + } + + /** + * Sets the time between the first reflection and the reverberation. + * @param reverbDelay Reverb delay in milliseconds. The valid range is [0, 100]. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setReverbDelay(int reverbDelay) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = intToByteArray(reverbDelay); + checkStatus(setParameter(PARAM_REVERB_DELAY, param)); + } + + /** + * Gets the reverb delay. + * @return the reverb delay in milliseconds. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getReverbDelay() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[4]; + checkStatus(getParameter(PARAM_REVERB_DELAY, param)); + return byteArrayToInt(param); + } + + /** + * Sets the echo density in the late reverberation decay. + * <p>The scale should approximately map linearly to the perceived change in reverberation. + * @param diffusion Diffusion specified using a permille scale. The diffusion valid range is + * [0, 1000]. A value of 1000 o/oo indicates a smooth reverberation decay. + * Values below this level give a more <i>grainy</i> character. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDiffusion(short diffusion) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(diffusion); + checkStatus(setParameter(PARAM_DIFFUSION, param)); + } + + /** + * Gets diffusion level. + * @return the diffusion level. See {@link #setDiffusion(short)} for units. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getDiffusion() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_DIFFUSION, param)); + return byteArrayToShort(param); + } + + + /** + * Controls the modal density of the late reverberation decay. + * <p> The scale should approximately map linearly to the perceived change in reverberation. + * A lower density creates a hollow sound that is useful for simulating small reverberation + * spaces such as bathrooms. + * @param density Density specified using a permille scale. The valid range is [0, 1000]. + * A value of 1000 o/oo indicates a natural sounding reverberation. Values below this level + * produce a more colored effect. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setDensity(short density) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = shortToByteArray(density); + checkStatus(setParameter(PARAM_DENSITY, param)); + } + + /** + * Gets the density level. + * @return the density level. See {@link #setDiffusion(short)} for units. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getDensity() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + byte[] param = new byte[2]; + checkStatus(getParameter(PARAM_DENSITY, param)); + return byteArrayToShort(param); + } + + + /** + * The OnParameterChangeListener interface defines a method called by the EnvironmentalReverb + * when a parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * EnvironmentalReverb engine. + * @param effect the EnvironmentalReverb on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_ROOM_LEVEL} ... + * @param value the new parameter value. + */ + void onParameterChange(EnvironmentalReverb effect, int status, int param, int value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + int v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = (int)byteArrayToShort(value, 0); + } else if (value.length == 4) { + v = byteArrayToInt(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(EnvironmentalReverb.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/java/android/media/Equalizer.java b/media/java/android/media/Equalizer.java new file mode 100644 index 0000000..082f694 --- /dev/null +++ b/media/java/android/media/Equalizer.java @@ -0,0 +1,443 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * 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 android.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import android.media.AudioEffect; + +/** + * An Equalizer is used to alter the frequency response of a particular music source or of the main + * output mix. + * <p>An application creates an Equalizer object to instantiate and control an Equalizer engine + * in the audio framework. The application can either simply use predefined presets or have a more + * precise control of the gain in each frequency band controlled by the equalizer. + * <p>The methods, parameter types and units exposed by the Equalizer implementation are directly + * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) + * for the SLEqualizerItf interface. Please refer to this specification for more details. + * <p>To attach the Equalizer to a particular AudioTrack or MediaPlayer, specify the audio session + * ID of this AudioTrack or MediaPlayer when constructing the Equalizer. If the audio session ID 0 + * is specified, the Equalizer applies to the main audio output mix. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class Equalizer extends AudioEffect { + + private final static String TAG = "Equalizer"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectEqualizerApi.h + /** + * Number of bands. Parameter ID for {@link android.media.Equalizer.OnParameterChangeListener} + */ + public static final int PARAM_NUM_BANDS = 0; + /** + * Band level range. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_LEVEL_RANGE = 1; + /** + * Band level. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_BAND_LEVEL = 2; + /** + * Band center frequency. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_CENTER_FREQ = 3; + /** + * Band frequency range. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_BAND_FREQ_RANGE = 4; + /** + * Band for a given frequency. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_GET_BAND = 5; + /** + * Current preset. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_CURRENT_PRESET = 6; + /** + * Request number of presets. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_GET_NUM_OF_PRESETS = 7; + /** + * Request preset name. Parameter ID for OnParameterChangeListener + */ + public static final int PARAM_GET_PRESET_NAME = 8; + /** + * maximum size for perset name + */ + public static final int PARAM_STRING_SIZE_MAX = 32; + + /** + * Number of presets implemented by Equalizer engine + */ + private int mNumPresets; + /** + * Names of presets implemented by Equalizer engine + */ + private String[] mPresetNames; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the Equalizer + * engine. As the same engine can be shared by several applications, this parameter indicates + * how much the requesting application needs control of effect parameters. The normal priority + * is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the Equalizer will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the Equalizer will apply to the output mix. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public Equalizer(int priority, int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_EQUALIZER, EFFECT_TYPE_NULL, priority, audioSession); + + mNumPresets = (int)getNumberOfPresets(); + + if (mNumPresets != 0) { + mPresetNames = new String[mNumPresets]; + byte[] value = new byte[PARAM_STRING_SIZE_MAX]; + int[] param = new int[2]; + param[0] = PARAM_GET_PRESET_NAME; + for (int i = 0; i < mNumPresets; i++) { + param[1] = i; + checkStatus(getParameter(param, value)); + int length = 0; + while (value[length] != 0) length++; + try { + mPresetNames[i] = new String(value, 0, length, "ISO-8859-1"); + Log.e(TAG, "preset #: "+i+" name: "+mPresetNames[i]+" length: "+length); + } catch (java.io.UnsupportedEncodingException e) { + Log.e(TAG, "preset name decode error"); + } + } + } + } + + /** + * Gets the number of frequency bands supported by the Equalizer engine. + * @return the number of bands + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getNumberOfBands() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_NUM_BANDS; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * Gets the level range for use by {@link #setBandLevel(int,short)}. The level is expressed in + * milliBel. + * @return the band level range in an array of short integers. The first element is the lower + * limit of the range, the second element the upper limit. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short[] getBandLevelRange() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + int[] value = new int[2]; + param[0] = PARAM_LEVEL_RANGE; + checkStatus(getParameter(param, value)); + + short[] result = new short[2]; + + result[0] = (short)value[0]; + result[1] = (short)value[1]; + + return result; + } + + /** + * Sets the given equalizer band to the given gain value. + * @param band Frequency band that will have the new gain. The numbering of the bands starts + * from 0 and ends at (number of bands - 1). See @see #getNumberOfBands(). + * @param level New gain in millibels that will be set to the given band. getBandLevelRange() + * will define the maximum and minimum values. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setBandLevel(int band, short level) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] value = new int[1]; + + param[0] = PARAM_BAND_LEVEL; + param[1] = band; + value[0] = (int)level; + checkStatus(setParameter(param, value)); + } + + /** + * Gets the gain set for the given equalizer band. + * @param band Frequency band whose gain is requested. The numbering of the bands starts + * from 0 and ends at (number of bands - 1). + * @return Gain in millibels of the given band. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getBandLevel(int band) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[1]; + + param[0] = PARAM_BAND_LEVEL; + param[1] = band; + checkStatus(getParameter(param, result)); + + return (short)result[0]; + } + + + /** + * Gets the center frequency of the given band. + * @param band Frequency band whose center frequency is requested. The numbering of the bands + * starts from 0 and ends at (number of bands - 1). + * @return The center frequency in milliHertz + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getCenterFreq(int band) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[1]; + + param[0] = PARAM_CENTER_FREQ; + param[1] = band; + checkStatus(getParameter(param, result)); + + return result[0]; + } + + /** + * Gets the frequency range of the given frequency band. + * @param band Frequency band whose frequency range is requested. The numbering of the bands + * starts from 0 and ends at (number of bands - 1). + * @return The frequency range in millHertz in an array of integers. The first element is the + * lower limit of the range, the second element the upper limit. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int[] getBandFreqRange(int band) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[2]; + param[0] = PARAM_BAND_FREQ_RANGE; + param[1] = band; + checkStatus(getParameter(param, result)); + + return result; + } + + /** + * Gets the band that has the most effect on the given frequency. + * @param frequency Frequency in milliHertz which is to be equalized via the returned band. + * @return Frequency band that has most effect on the given frequency. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public int getBand(int frequency) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[2]; + int[] result = new int[1]; + + param[0] = PARAM_GET_BAND; + param[1] = frequency; + checkStatus(getParameter(param, result)); + + return result[0]; + } + + /** + * Gets current preset. + * @return Preset that is set at the moment. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getCurrentPreset() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_CURRENT_PRESET; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * Sets the equalizer according to the given preset. + * @param preset New preset that will be taken into use. The valid range is [0, + * number of presets-1]. See {@see #getNumberOfPresets()}. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void usePreset(short preset) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_CURRENT_PRESET, preset)); + } + + /** + * Gets the total number of presets the equalizer supports. The presets will have indices + * [0, number of presets-1]. + * @return The number of presets the equalizer supports. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getNumberOfPresets() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_GET_NUM_OF_PRESETS; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * Gets the preset name based on the index. + * @param preset Index of the preset. The valid range is [0, number of presets-1]. + * @return A string containing the name of the given preset. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public String getPresetName(short preset) + { + if (preset >= 0 && preset < mNumPresets) { + return mPresetNames[preset]; + } else { + return ""; + } + } + + /** + * The OnParameterChangeListener interface defines a method called by the Equalizer when a + * parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * Equalizer engine. + * @param effect the Equalizer on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param1 ID of the modified parameter. See {@link #PARAM_BAND_LEVEL} ... + * @param param2 additional parameter qualifier (e.g the band for band level parameter). + * @param value the new parameter value. + */ + void onParameterChange(Equalizer effect, int status, int param1, int param2, int value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p1 = -1; + int p2 = -1; + int v = -1; + + if (param.length >= 4) { + p1 = byteArrayToInt(param, 0); + if (param.length >= 8) { + p2 = byteArrayToInt(param, 4); + } + } + if (value.length == 2) { + v = (int)byteArrayToShort(value, 0);; + } else if (value.length == 4) { + v = byteArrayToInt(value, 0); + } + + if (p1 != -1 && v != -1) { + l.onParameterChange(Equalizer.this, status, p1, p2, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } + +} diff --git a/media/java/android/media/PresetReverb.java b/media/java/android/media/PresetReverb.java new file mode 100644 index 0000000..83a01a4 --- /dev/null +++ b/media/java/android/media/PresetReverb.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * 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 android.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; + +import android.media.AudioEffect; + +/** + * A sound generated within a room travels in many directions. The listener first hears the + * direct sound from the source itself. Later, he or she hears discrete echoes caused by sound + * bouncing off nearby walls, the ceiling and the floor. As sound waves arrive after + * undergoing more and more reflections, individual reflections become indistinguishable and + * the listener hears continuous reverberation that decays over time. + * Reverb is vital for modeling a listener's environment. It can be used in music applications + * to simulate music being played back in various environments, or in games to immerse the + * listener within the game's environment. + * The PresetReverb class allows an application to configure the global reverb using a reverb preset. + * This is primarily used for adding some reverb in a music playback context. Applications + * requiring control over a more advanced environmental reverb are advised to use the + // TODO when EnvironmentalReverb is unhidden + // {_at_link android.media.EnvironmentalReverb} class. + * <p>An application creates a PresetReverb object to instantiate and control a reverb engine in the + * audio framework. + * <p>The methods, parameter types and units exposed by the PresetReverb implementation are + * directly mapping those defined by the OpenSL ES 1.0.1 Specification + * (http://www.khronos.org/opensles/) for the SLPresetReverbItf interface. + * Please refer to this specification for more details. + * <p>The PresetReverb is an output mix auxiliary effect and should be created on + * Audio session 0. In order for a MediaPlayer or AudioTrack to be fed into this effect, + * they must be explicitely attached to it and a send level must be specified. Use the effect ID + * returned by getId() method to designate this particular effect when attaching it to the + * MediaPlayer or AudioTrack. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class PresetReverb extends AudioEffect { + + private final static String TAG = "PresetReverb"; + + // These constants must be synchronized with those in + // frameworks/base/include/media/EffectPresetReverbApi.h + + /** + * Preset. Parameter ID for + * {@link android.media.PresetReverb.OnParameterChangeListener} + */ + public static final int PARAM_PRESET = 0; + + /** + * Room level. Parameter ID for + * {@link android.media.PresetReverb.OnParameterChangeListener} + */ + public static final int PRESET_NONE = 0; + public static final int PRESET_SMALLROOM = 1; + public static final int PRESET_MEDIUMROOM = 2; + public static final int PRESET_LARGEROOM = 3; + public static final int PRESET_MEDIUMHALL = 4; + public static final int PRESET_LARGEHALL = 5; + public static final int PRESET_PLATE = 6; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the + * PresetReverb engine. As the same engine can be shared by several applications, this + * parameter indicates how much the requesting application needs control of effect parameters. + * The normal priority is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the PresetReverb will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the PresetReverb will apply to the output mix. + * As the PresetReverb is an auxiliary effect it is recommended to instantiate it on + * audio session 0 and to attach it to the MediaPLayer auxiliary output. + * + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public PresetReverb(int priority, int audioSession) + throws IllegalArgumentException, UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_PRESET_REVERB, EFFECT_TYPE_NULL, priority, audioSession); + Log.e(TAG, "contructor"); + } + + /** + * Enables a preset on the reverb. + * <p>The reverb PRESET_NONE disables any reverb from the current output but does not free the + * resources associated with the reverb. For an application to signal to the implementation + * to free the resources, it must call the release() method. + * @param preset This must be one of the the preset constants defined in this class. + * e.g. {@link #PRESET_SMALLROOM} + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setPreset(short preset) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_PRESET, preset)); + } + + /** + * Gets current reverb preset. + * @return Preset that is set at the moment. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getPreset() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + int[] param = new int[1]; + param[0] = PARAM_PRESET; + short[] value = new short[1]; + checkStatus(getParameter(param, value)); + return value[0]; + } + + /** + * The OnParameterChangeListener interface defines a method called by the PresetReverb + * when a parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * PresetReverb engine. + * @param effect the PresetReverb on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_PRESET} ... + * @param value the new parameter value. + */ + void onParameterChange(PresetReverb effect, int status, int param, short value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + short v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = byteArrayToShort(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(PresetReverb.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/java/android/media/Virtualizer.java b/media/java/android/media/Virtualizer.java new file mode 100644 index 0000000..9f71297 --- /dev/null +++ b/media/java/android/media/Virtualizer.java @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * 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 android.media; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +import android.media.AudioEffect; + +/** + * An audio virtualizer is a general name for an effect to spatialize audio channels. The exact + * behavior of this effect is dependent on the number of audio input channels and the types and + * number of audio output channels of the device. For example, in the case of a stereo input and + * stereo headphone output, a stereo widening effect is used when this effect is turned on. + * <p>An application creates a Virtualizer object to instantiate and control a virtualizer engine + * in the audio framework. + * <p>The methods, parameter types and units exposed by the Virtualizer implementation are directly + * mapping those defined by the OpenSL ES 1.0.1 Specification (http://www.khronos.org/opensles/) + * for the SLVirtualizerItf interface. Please refer to this specification for more details. + * <p>To attach the Virtualizer to a particular AudioTrack or MediaPlayer, specify the audio session + * ID of this AudioTrack or MediaPlayer when constructing the Virtualizer. If the audio session ID 0 + * is specified, the Virtualizer applies to the main audio output mix. + // TODO when AudioEffect is unhidden + // <p> See {_at_link android.media.AudioEffect} class for more details on controlling audio effects. + * + * {@hide Pending API council review} + */ + +public class Virtualizer extends AudioEffect { + + private final static String TAG = "Virtualizer"; + + // These constants must be synchronized with those in frameworks/base/include/media/EffectVirtualizerApi.h + /** + * Is strength parameter supported by virtualizer engine. Parameter ID for getParameter(). + */ + public static final int PARAM_STRENGTH_SUPPORTED = 0; + /** + * Virtualizer effect strength. Parameter ID for + * {@link android.media.Virtualizer.OnParameterChangeListener} + */ + public static final int PARAM_STRENGTH = 1; + + /** + * Indicates if strength parameter is supported by the virtualizer engine + */ + private boolean mStrengthSupported = false; + + /** + * Registered listener for parameter changes. + */ + private OnParameterChangeListener mParamListener = null; + + /** + * Listener used internally to to receive raw parameter change event from AudioEffect super class + */ + private BaseParameterListener mBaseParamListener = null; + + /** + * Lock for access to mParamListener + */ + private final Object mParamListenerLock = new Object(); + + /** + * Class constructor. + * @param priority the priority level requested by the application for controlling the Virtualizer + * engine. As the same engine can be shared by several applications, this parameter indicates + * how much the requesting application needs control of effect parameters. The normal priority + * is 0, above normal is a positive number, below normal a negative number. + * @param audioSession System wide unique audio session identifier. If audioSession + * is not 0, the Virtualizer will be attached to the MediaPlayer or AudioTrack in the + * same audio session. Otherwise, the Virtualizer will apply to the output mix. + * + * @throws java.lang.IllegalStateException + * @throws java.lang.IllegalArgumentException + * @throws java.lang.UnsupportedOperationException + * @throws java.lang.RuntimeException + */ + public Virtualizer(int priority, int audioSession) + throws IllegalStateException, IllegalArgumentException, + UnsupportedOperationException, RuntimeException { + super(EFFECT_TYPE_VIRTUALIZER, EFFECT_TYPE_NULL, priority, audioSession); + + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH_SUPPORTED, value)); + mStrengthSupported = (value[0] != 0); + } + + /** + * Indicates whether setting strength is supported. If this method returns false, only one + * strength is supported and the setStrength() method always rounds to that value. + * @return true is strength parameter is supported, false otherwise + */ + public boolean getStrengthSupported() { + return mStrengthSupported; + } + + /** + * Sets the strength of the virtualizer effect. If the implementation does not support per mille + * accuracy for setting the strength, it is allowed to round the given strength to the nearest + * supported value. You can use the {@link #getRoundedStrength()} method to query the + * (possibly rounded) value that was actually set. + * @param strength Strength of the effect. The valid range for strength strength is [0, 1000], + * where 0 per mille designates the mildest effect and 1000 per mille designates the strongest. + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public void setStrength(short strength) + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + checkStatus(setParameter(PARAM_STRENGTH, strength)); + } + + /** + * Gets the current strength of the effect. + * @return The strength of the effect. The valid range for strength is [0, 1000], where 0 per + * mille designates the mildest effect and 1000 per mille the strongest + * @throws IllegalStateException + * @throws IllegalArgumentException + * @throws UnsupportedOperationException + */ + public short getRoundedStrength() + throws IllegalStateException, IllegalArgumentException, UnsupportedOperationException { + short[] value = new short[1]; + checkStatus(getParameter(PARAM_STRENGTH, value)); + return value[0]; + } + + /** + * The OnParameterChangeListener interface defines a method called by the Virtualizer when a + * parameter value has changed. + */ + public interface OnParameterChangeListener { + /** + * Method called when a parameter value has changed. The method is called only if the + * parameter was changed by another application having the control of the same + * Virtualizer engine. + * @param effect the Virtualizer on which the interface is registered. + * @param status status of the set parameter operation. + // TODO when AudioEffect is unhidden + // See {_at_link android.media.AudioEffect#setParameter(byte[], byte[])}. + * @param param ID of the modified parameter. See {@link #PARAM_STRENGTH} ... + * @param value the new parameter value. + */ + void onParameterChange(Virtualizer effect, int status, int param, short value); + } + + /** + * Listener used internally to receive unformatted parameter change events from AudioEffect + * super class. + */ + private class BaseParameterListener implements AudioEffect.OnParameterChangeListener { + private BaseParameterListener() { + + } + public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) { + OnParameterChangeListener l = null; + + synchronized (mParamListenerLock) { + if (mParamListener != null) { + l = mParamListener; + } + } + if (l != null) { + int p = -1; + short v = -1; + + if (param.length == 4) { + p = byteArrayToInt(param, 0); + } + if (value.length == 2) { + v = byteArrayToShort(value, 0); + } + if (p != -1 && v != -1) { + l.onParameterChange(Virtualizer.this, status, p, v); + } + } + } + } + + /** + * Registers an OnParameterChangeListener interface. + * @param listener OnParameterChangeListener interface registered + */ + public void setParameterListener(OnParameterChangeListener listener) { + synchronized (mParamListenerLock) { + if (mParamListener == null) { + mParamListener = listener; + mBaseParamListener = new BaseParameterListener(); + super.setParameterListener(mBaseParamListener); + } + } + } +} diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index 02474a4..beb3dfc 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -323,8 +323,8 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t priority, effectCallback, &lpJniStorage->mCallbackData, - 0, - sessionId); + sessionId, + 0); if (lpAudioEffect == NULL) { LOGE("Error creating AudioEffect"); goto setup_failure; diff --git a/media/libeffects/EffectReverb.c b/media/libeffects/EffectReverb.c index ada252c..5c87f23 100644 --- a/media/libeffects/EffectReverb.c +++ b/media/libeffects/EffectReverb.c @@ -57,7 +57,7 @@ static const effect_descriptor_t gInsertEnvReverbDescriptor = { // Google auxiliary preset reverb UUID: 63909320-53a6-11df-bdbd-0002a5d5c51b static const effect_descriptor_t gAuxPresetReverbDescriptor = { - {0x47382d60, 0xddd8, 0x4763, 0x11db, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, {0x63909320, 0x53a6, 0x11df, 0xbdbd, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, EFFECT_API_VERSION, EFFECT_FLAG_TYPE_AUXILIARY, @@ -69,7 +69,7 @@ static const effect_descriptor_t gAuxPresetReverbDescriptor = { // Google insert preset reverb UUID: d93dc6a0-6342-11df-b128-0002a5d5c51b static const effect_descriptor_t gInsertPresetReverbDescriptor = { - {0x47382d60, 0xddd8, 0x4763, 0x11db, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, + {0x47382d60, 0xddd8, 0x11db, 0xbf3a, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, {0xd93dc6a0, 0x6342, 0x11df, 0xb128, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}, EFFECT_API_VERSION, EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST, @@ -196,7 +196,7 @@ static int Reverb_Process(effect_interface_t self, audio_buffer_t *inBuffer, aud pReverb = (reverb_object_t*) &pRvbModule->context; //if bypassed or the preset forces the signal to be completely dry - if (pReverb->m_bBypass) { + if (pReverb->m_bBypass != 0) { if (inBuffer->raw != outBuffer->raw) { int16_t smp; pSrc = inBuffer->s16; @@ -520,7 +520,7 @@ int Reverb_Configure(reverb_module_t *pRvbModule, effect_config_t *pConfig, pReverb->m_bUseNoise = true; // for debugging purposes, allow bypass - pReverb->m_bBypass = false; + pReverb->m_bBypass = 0; pReverb->m_nNextRoom = 1; @@ -662,248 +662,254 @@ int Reverb_getParameter(reverb_object_t *pReverb, int32_t param, size_t *pSize, int32_t temp2; size_t size; - if (pReverb->m_Preset && param != REVERB_PARAM_PRESET) { - return -EINVAL; - } - if (!pReverb->m_Preset && param == REVERB_PARAM_PRESET) { - return -EINVAL; - } - - switch (param) { - case REVERB_PARAM_ROOM_LEVEL: - case REVERB_PARAM_ROOM_HF_LEVEL: - case REVERB_PARAM_DECAY_HF_RATIO: - case REVERB_PARAM_REFLECTIONS_LEVEL: - case REVERB_PARAM_REVERB_LEVEL: - case REVERB_PARAM_DIFFUSION: - case REVERB_PARAM_DENSITY: + if (pReverb->m_Preset) { + if (param != REVERB_PARAM_PRESET || *pSize < sizeof(int16_t)) { + return -EINVAL; + } size = sizeof(int16_t); - break; - - case REVERB_PARAM_BYPASS: - case REVERB_PARAM_PRESET: - case REVERB_PARAM_DECAY_TIME: - case REVERB_PARAM_REFLECTIONS_DELAY: - case REVERB_PARAM_REVERB_DELAY: - size = sizeof(int32_t); - break; - - case REVERB_PARAM_PROPERTIES: - size = sizeof(t_reverb_properties); - break; - - default: - return -EINVAL; - } + pValue16 = (int16_t *)pValue; + // REVERB_PRESET_NONE is mapped to bypass + if (pReverb->m_bBypass != 0) { + *pValue16 = (int16_t)REVERB_PRESET_NONE; + } else { + *pValue16 = (int16_t)(pReverb->m_nNextRoom + 1); + } + LOGV("get REVERB_PARAM_PRESET, preset %d", *pValue16); + } else { + switch (param) { + case REVERB_PARAM_ROOM_LEVEL: + case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_REFLECTIONS_LEVEL: + case REVERB_PARAM_REVERB_LEVEL: + case REVERB_PARAM_DIFFUSION: + case REVERB_PARAM_DENSITY: + size = sizeof(int16_t); + break; - if (*pSize < size) { - return -EINVAL; - } - *pSize = size; - pValue32 = (int32_t *) pValue; - pValue16 = (int16_t *) pValue; - pProperties = (t_reverb_properties *) pValue; + case REVERB_PARAM_BYPASS: + case REVERB_PARAM_DECAY_TIME: + case REVERB_PARAM_REFLECTIONS_DELAY: + case REVERB_PARAM_REVERB_DELAY: + size = sizeof(int32_t); + break; - switch (param) { - case REVERB_PARAM_BYPASS: - *(int32_t *) pValue = (int32_t) pReverb->m_bBypass; - break; - case REVERB_PARAM_PRESET: - *(int32_t *) pValue = (int8_t) pReverb->m_nCurrentRoom; - break; + case REVERB_PARAM_PROPERTIES: + size = sizeof(t_reverb_properties); + break; - case REVERB_PARAM_PROPERTIES: - pValue16 = &pProperties->roomLevel; - /* FALL THROUGH */ + default: + return -EINVAL; + } - case REVERB_PARAM_ROOM_LEVEL: - // Convert m_nRoomLpfFwd to millibels - temp = (pReverb->m_nRoomLpfFwd << 15) - / (32767 - pReverb->m_nRoomLpfFbk); - *pValue16 = Effects_Linear16ToMillibels(temp); + if (*pSize < size) { + return -EINVAL; + } - LOGV("get REVERB_PARAM_ROOM_LEVEL %d, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", *pValue16, temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + pValue32 = (int32_t *) pValue; + pValue16 = (int16_t *) pValue; + pProperties = (t_reverb_properties *) pValue; - if (param == REVERB_PARAM_ROOM_LEVEL) { - break; - } - pValue16 = &pProperties->roomHFLevel; - /* FALL THROUGH */ - - case REVERB_PARAM_ROOM_HF_LEVEL: - // The ratio between linear gain at 0Hz and at 5000Hz for the room low pass is: - // (1 + a1) / sqrt(a1^2 + 2*C*a1 + 1) where: - // - a1 is minus the LP feedback gain: -pReverb->m_nRoomLpfFbk - // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz - - temp = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFbk); - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 %d", temp); - temp2 = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nCosWT_5KHz) - << 1; - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, 2 Cos a1 %d", temp2); - temp = 32767 + temp - temp2; - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 + 2 Cos a1 + 1 %d", temp); - temp = Effects_Sqrt(temp) * 181; - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, SQRT(a1^2 + 2 Cos a1 + 1) %d", temp); - temp = ((32767 - pReverb->m_nRoomLpfFbk) << 15) / temp; - - LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); - - *pValue16 = Effects_Linear16ToMillibels(temp); - - if (param == REVERB_PARAM_ROOM_HF_LEVEL) { + switch (param) { + case REVERB_PARAM_BYPASS: + *pValue32 = (int32_t) pReverb->m_bBypass; break; - } - pValue32 = &pProperties->decayTime; - /* FALL THROUGH */ - case REVERB_PARAM_DECAY_TIME: - // Calculate reverb feedback path gain - temp = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); - temp = Effects_Linear16ToMillibels(temp); + case REVERB_PARAM_PROPERTIES: + pValue16 = &pProperties->roomLevel; + /* FALL THROUGH */ - // Calculate decay time: g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time - temp = (-6000 * pReverb->m_nLateDelay) / temp; + case REVERB_PARAM_ROOM_LEVEL: + // Convert m_nRoomLpfFwd to millibels + temp = (pReverb->m_nRoomLpfFwd << 15) + / (32767 - pReverb->m_nRoomLpfFbk); + *pValue16 = Effects_Linear16ToMillibels(temp); - // Convert samples to ms - *pValue32 = (temp * 1000) / pReverb->m_nSamplingRate; + LOGV("get REVERB_PARAM_ROOM_LEVEL %d, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", *pValue16, temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); - LOGV("get REVERB_PARAM_DECAY_TIME, samples %d, ms %d", temp, *pValue32); - - if (param == REVERB_PARAM_DECAY_TIME) { - break; - } - pValue16 = &pProperties->decayHFRatio; - /* FALL THROUGH */ - - case REVERB_PARAM_DECAY_HF_RATIO: - // If r is the decay HF ratio (r = REVERB_PARAM_DECAY_HF_RATIO/1000) we have: - // DT_5000Hz = DT_0Hz * r - // and G_5000Hz = -6000 * d / DT_5000Hz and G_0Hz = -6000 * d / DT_0Hz in millibels so : - // r = G_0Hz/G_5000Hz in millibels - // The linear gain at 5000Hz is b0 / sqrt(a1^2 + 2*C*a1 + 1) where: - // - a1 is minus the LP feedback gain: -pReverb->m_nRvbLpfFbk - // - b0 is the LP forward gain: pReverb->m_nRvbLpfFwd - // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz - if (pReverb->m_nRvbLpfFbk == 0) { - *pValue16 = 1000; - LOGV("get REVERB_PARAM_DECAY_HF_RATIO, pReverb->m_nRvbLpfFbk == 0, ratio %d", *pValue16); - } else { - temp = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFbk); - temp2 = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nCosWT_5KHz) + if (param == REVERB_PARAM_ROOM_LEVEL) { + break; + } + pValue16 = &pProperties->roomHFLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_HF_LEVEL: + // The ratio between linear gain at 0Hz and at 5000Hz for the room low pass is: + // (1 + a1) / sqrt(a1^2 + 2*C*a1 + 1) where: + // - a1 is minus the LP feedback gain: -pReverb->m_nRoomLpfFbk + // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz + + temp = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFbk); + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 %d", temp); + temp2 = MULT_EG1_EG1(pReverb->m_nRoomLpfFbk, pReverb->m_nCosWT_5KHz) << 1; + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, 2 Cos a1 %d", temp2); temp = 32767 + temp - temp2; + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, a1^2 + 2 Cos a1 + 1 %d", temp); temp = Effects_Sqrt(temp) * 181; - temp = (pReverb->m_nRvbLpfFwd << 15) / temp; - // The linear gain at 0Hz is b0 / (a1 + 1) - temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - - pReverb->m_nRvbLpfFbk); + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, SQRT(a1^2 + 2 Cos a1 + 1) %d", temp); + temp = ((32767 - pReverb->m_nRoomLpfFbk) << 15) / temp; + + LOGV("get REVERB_PARAM_ROOM_HF_LEVEL, gain %d, m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + + *pValue16 = Effects_Linear16ToMillibels(temp); + + if (param == REVERB_PARAM_ROOM_HF_LEVEL) { + break; + } + pValue32 = &pProperties->decayTime; + /* FALL THROUGH */ + case REVERB_PARAM_DECAY_TIME: + // Calculate reverb feedback path gain + temp = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); temp = Effects_Linear16ToMillibels(temp); - temp2 = Effects_Linear16ToMillibels(temp2); - LOGV("get REVERB_PARAM_DECAY_HF_RATIO, gain 5KHz %d mB, gain DC %d mB", temp, temp2); - if (temp == 0) - temp = 1; - temp = (int16_t) ((1000 * temp2) / temp); - if (temp > 1000) - temp = 1000; + // Calculate decay time: g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time + temp = (-6000 * pReverb->m_nLateDelay) / temp; - *pValue16 = temp; - LOGV("get REVERB_PARAM_DECAY_HF_RATIO, ratio %d", *pValue16); - } + // Convert samples to ms + *pValue32 = (temp * 1000) / pReverb->m_nSamplingRate; - if (param == REVERB_PARAM_DECAY_HF_RATIO) { - break; - } - pValue16 = &pProperties->reflectionsLevel; - /* FALL THROUGH */ + LOGV("get REVERB_PARAM_DECAY_TIME, samples %d, ms %d", temp, *pValue32); - case REVERB_PARAM_REFLECTIONS_LEVEL: - *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nEarlyGain); + if (param == REVERB_PARAM_DECAY_TIME) { + break; + } + pValue16 = &pProperties->decayHFRatio; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_HF_RATIO: + // If r is the decay HF ratio (r = REVERB_PARAM_DECAY_HF_RATIO/1000) we have: + // DT_5000Hz = DT_0Hz * r + // and G_5000Hz = -6000 * d / DT_5000Hz and G_0Hz = -6000 * d / DT_0Hz in millibels so : + // r = G_0Hz/G_5000Hz in millibels + // The linear gain at 5000Hz is b0 / sqrt(a1^2 + 2*C*a1 + 1) where: + // - a1 is minus the LP feedback gain: -pReverb->m_nRvbLpfFbk + // - b0 is the LP forward gain: pReverb->m_nRvbLpfFwd + // - C is cos(2piWT) @ 5000Hz: pReverb->m_nCosWT_5KHz + if (pReverb->m_nRvbLpfFbk == 0) { + *pValue16 = 1000; + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, pReverb->m_nRvbLpfFbk == 0, ratio %d", *pValue16); + } else { + temp = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFbk); + temp2 = MULT_EG1_EG1(pReverb->m_nRvbLpfFbk, pReverb->m_nCosWT_5KHz) + << 1; + temp = 32767 + temp - temp2; + temp = Effects_Sqrt(temp) * 181; + temp = (pReverb->m_nRvbLpfFwd << 15) / temp; + // The linear gain at 0Hz is b0 / (a1 + 1) + temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 + - pReverb->m_nRvbLpfFbk); + + temp = Effects_Linear16ToMillibels(temp); + temp2 = Effects_Linear16ToMillibels(temp2); + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, gain 5KHz %d mB, gain DC %d mB", temp, temp2); + + if (temp == 0) + temp = 1; + temp = (int16_t) ((1000 * temp2) / temp); + if (temp > 1000) + temp = 1000; + + *pValue16 = temp; + LOGV("get REVERB_PARAM_DECAY_HF_RATIO, ratio %d", *pValue16); + } - LOGV("get REVERB_PARAM_REFLECTIONS_LEVEL, %d", *pValue16); - if (param == REVERB_PARAM_REFLECTIONS_LEVEL) { - break; - } - pValue32 = &pProperties->reflectionsDelay; - /* FALL THROUGH */ + if (param == REVERB_PARAM_DECAY_HF_RATIO) { + break; + } + pValue16 = &pProperties->reflectionsLevel; + /* FALL THROUGH */ - case REVERB_PARAM_REFLECTIONS_DELAY: - // convert samples to ms - *pValue32 = (pReverb->m_nEarlyDelay * 1000) / pReverb->m_nSamplingRate; + case REVERB_PARAM_REFLECTIONS_LEVEL: + *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nEarlyGain); - LOGV("get REVERB_PARAM_REFLECTIONS_DELAY, samples %d, ms %d", pReverb->m_nEarlyDelay, *pValue32); + LOGV("get REVERB_PARAM_REFLECTIONS_LEVEL, %d", *pValue16); + if (param == REVERB_PARAM_REFLECTIONS_LEVEL) { + break; + } + pValue32 = &pProperties->reflectionsDelay; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REFLECTIONS_DELAY) { - break; - } - pValue16 = &pProperties->reverbLevel; - /* FALL THROUGH */ + case REVERB_PARAM_REFLECTIONS_DELAY: + // convert samples to ms + *pValue32 = (pReverb->m_nEarlyDelay * 1000) / pReverb->m_nSamplingRate; - case REVERB_PARAM_REVERB_LEVEL: - // Convert linear gain to millibels - *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nLateGain << 2); + LOGV("get REVERB_PARAM_REFLECTIONS_DELAY, samples %d, ms %d", pReverb->m_nEarlyDelay, *pValue32); - LOGV("get REVERB_PARAM_REVERB_LEVEL %d", *pValue16); + if (param == REVERB_PARAM_REFLECTIONS_DELAY) { + break; + } + pValue16 = &pProperties->reverbLevel; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REVERB_LEVEL) { - break; - } - pValue32 = &pProperties->reverbDelay; - /* FALL THROUGH */ + case REVERB_PARAM_REVERB_LEVEL: + // Convert linear gain to millibels + *pValue16 = Effects_Linear16ToMillibels(pReverb->m_nLateGain << 2); - case REVERB_PARAM_REVERB_DELAY: - // convert samples to ms - *pValue32 = (pReverb->m_nLateDelay * 1000) / pReverb->m_nSamplingRate; + LOGV("get REVERB_PARAM_REVERB_LEVEL %d", *pValue16); - LOGV("get REVERB_PARAM_REVERB_DELAY, samples %d, ms %d", pReverb->m_nLateDelay, *pValue32); + if (param == REVERB_PARAM_REVERB_LEVEL) { + break; + } + pValue32 = &pProperties->reverbDelay; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REVERB_DELAY) { - break; - } - pValue16 = &pProperties->diffusion; - /* FALL THROUGH */ + case REVERB_PARAM_REVERB_DELAY: + // convert samples to ms + *pValue32 = (pReverb->m_nLateDelay * 1000) / pReverb->m_nSamplingRate; - case REVERB_PARAM_DIFFUSION: - temp = (int16_t) ((1000 * (pReverb->m_sAp0.m_nApGain - AP0_GAIN_BASE)) - / AP0_GAIN_RANGE); + LOGV("get REVERB_PARAM_REVERB_DELAY, samples %d, ms %d", pReverb->m_nLateDelay, *pValue32); - if (temp < 0) - temp = 0; - if (temp > 1000) - temp = 1000; + if (param == REVERB_PARAM_REVERB_DELAY) { + break; + } + pValue16 = &pProperties->diffusion; + /* FALL THROUGH */ - *pValue16 = temp; - LOGV("get REVERB_PARAM_DIFFUSION, %d, AP0 gain %d", *pValue16, pReverb->m_sAp0.m_nApGain); + case REVERB_PARAM_DIFFUSION: + temp = (int16_t) ((1000 * (pReverb->m_sAp0.m_nApGain - AP0_GAIN_BASE)) + / AP0_GAIN_RANGE); - if (param == REVERB_PARAM_DIFFUSION) { - break; - } - pValue16 = &pProperties->density; - /* FALL THROUGH */ + if (temp < 0) + temp = 0; + if (temp > 1000) + temp = 1000; - case REVERB_PARAM_DENSITY: - // Calculate AP delay in time units - temp = ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) << 16) - / pReverb->m_nSamplingRate; + *pValue16 = temp; + LOGV("get REVERB_PARAM_DIFFUSION, %d, AP0 gain %d", *pValue16, pReverb->m_sAp0.m_nApGain); - temp = (int16_t) ((1000 * (temp - AP0_TIME_BASE)) / AP0_TIME_RANGE); + if (param == REVERB_PARAM_DIFFUSION) { + break; + } + pValue16 = &pProperties->density; + /* FALL THROUGH */ - if (temp < 0) - temp = 0; - if (temp > 1000) - temp = 1000; + case REVERB_PARAM_DENSITY: + // Calculate AP delay in time units + temp = ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) << 16) + / pReverb->m_nSamplingRate; - *pValue16 = temp; + temp = (int16_t) ((1000 * (temp - AP0_TIME_BASE)) / AP0_TIME_RANGE); - LOGV("get REVERB_PARAM_DENSITY, %d, AP0 delay smps %d", *pValue16, pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn); - break; + if (temp < 0) + temp = 0; + if (temp > 1000) + temp = 1000; - default: - break; + *pValue16 = temp; + + LOGV("get REVERB_PARAM_DENSITY, %d, AP0 delay smps %d", *pValue16, pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn); + break; + + default: + break; + } } + *pSize = size; + LOGV("Reverb_getParameter, context %p, param %d, value %d", pReverb, param, *(int *)pValue); @@ -945,382 +951,386 @@ int Reverb_setParameter(reverb_object_t *pReverb, int32_t param, size_t size, LOGV("Reverb_setParameter, context %p, param %d, value16 %d, value32 %d", pReverb, param, *(int16_t *)pValue, *(int32_t *)pValue); - if (pReverb->m_Preset && param != REVERB_PARAM_PRESET) { - return -EINVAL; - } - if (!pReverb->m_Preset && param == REVERB_PARAM_PRESET) { - return -EINVAL; - } - - switch (param) { - case REVERB_PARAM_ROOM_LEVEL: - case REVERB_PARAM_ROOM_HF_LEVEL: - case REVERB_PARAM_DECAY_HF_RATIO: - case REVERB_PARAM_REFLECTIONS_LEVEL: - case REVERB_PARAM_REVERB_LEVEL: - case REVERB_PARAM_DIFFUSION: - case REVERB_PARAM_DENSITY: - paramSize = sizeof(int16_t); - break; - - case REVERB_PARAM_BYPASS: - case REVERB_PARAM_PRESET: - case REVERB_PARAM_DECAY_TIME: - case REVERB_PARAM_REFLECTIONS_DELAY: - case REVERB_PARAM_REVERB_DELAY: - paramSize = sizeof(int32_t); - break; - - case REVERB_PARAM_PROPERTIES: - paramSize = sizeof(t_reverb_properties); - break; - - default: - return -EINVAL; - } - - if (size != paramSize) { - return -EINVAL; - } - - if (paramSize == sizeof(int16_t)) { - value16 = *(int16_t *) pValue; - } else if (paramSize == sizeof(int32_t)) { - value32 = *(int32_t *) pValue; - } else { - pProperties = (t_reverb_properties *) pValue; - } - - pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nCurrentRoom]; - - switch (param) { - case REVERB_PARAM_BYPASS: - pReverb->m_bBypass = (uint16_t)value32; - break; - case REVERB_PARAM_PRESET: - if (value32 != REVERB_PRESET_LARGE_HALL && value32 - != REVERB_PRESET_HALL && value32 != REVERB_PRESET_CHAMBER - && value32 != REVERB_PRESET_ROOM) + if (pReverb->m_Preset) { + if (param != REVERB_PARAM_PRESET || size != sizeof(int16_t)) { return -EINVAL; - pReverb->m_nNextRoom = (int16_t) value32; - break; - - case REVERB_PARAM_PROPERTIES: - value16 = pProperties->roomLevel; - /* FALL THROUGH */ - - case REVERB_PARAM_ROOM_LEVEL: - // Convert millibels to linear 16 bit signed => m_nRoomLpfFwd - if (value16 > 0) + } + value16 = *(int16_t *)pValue; + LOGV("set REVERB_PARAM_PRESET, preset %d", value16); + if (value16 < REVERB_PRESET_NONE || value16 > REVERB_PRESET_PLATE) { return -EINVAL; + } + // REVERB_PRESET_NONE is mapped to bypass + if (value16 == REVERB_PRESET_NONE) { + pReverb->m_bBypass = 1; + } else { + pReverb->m_bBypass = 0; + pReverb->m_nNextRoom = value16 - 1; + } + } else { + switch (param) { + case REVERB_PARAM_ROOM_LEVEL: + case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_REFLECTIONS_LEVEL: + case REVERB_PARAM_REVERB_LEVEL: + case REVERB_PARAM_DIFFUSION: + case REVERB_PARAM_DENSITY: + paramSize = sizeof(int16_t); + break; - temp = Effects_MillibelsToLinear16(value16); - - pReverb->m_nRoomLpfFwd - = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRoomLpfFbk)); - - LOGV("REVERB_PARAM_ROOM_LEVEL, gain %d, new m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); - if (param == REVERB_PARAM_ROOM_LEVEL) + case REVERB_PARAM_BYPASS: + case REVERB_PARAM_DECAY_TIME: + case REVERB_PARAM_REFLECTIONS_DELAY: + case REVERB_PARAM_REVERB_DELAY: + paramSize = sizeof(int32_t); break; - value16 = pProperties->roomHFLevel; - /* FALL THROUGH */ - case REVERB_PARAM_ROOM_HF_LEVEL: + case REVERB_PARAM_PROPERTIES: + paramSize = sizeof(t_reverb_properties); + break; - // Limit to 0 , -40dB range because of low pass implementation - if (value16 > 0 || value16 < -4000) + default: return -EINVAL; - // Convert attenuation @ 5000H expressed in millibels to => m_nRoomLpfFbk - // m_nRoomLpfFbk is -a1 where a1 is the solution of: - // a1^2 + 2*(C-dG^2)/(1-dG^2)*a1 + 1 = 0 where: - // - C is cos(2*pi*5000/Fs) (pReverb->m_nCosWT_5KHz) - // - dG is G0/Gf (G0 is the linear gain at DC and Gf is the wanted gain at 5000Hz) - - // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged - // while changing HF level - temp2 = (pReverb->m_nRoomLpfFwd << 15) / (32767 - - pReverb->m_nRoomLpfFbk); - if (value16 == 0) { - pReverb->m_nRoomLpfFbk = 0; - } else { - int32_t dG2, b, delta; - - // dG^2 - temp = Effects_MillibelsToLinear16(value16); - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, HF gain %d", temp); - temp = (1 << 30) / temp; - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain %d", temp); - dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain ^ 2 %d", dG2); - // b = 2*(C-dG^2)/(1-dG^2) - b = (int32_t) ((((int64_t) 1 << (15 + 1)) - * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) - / ((int64_t) 32767 - (int64_t) dG2)); - - // delta = b^2 - 4 - delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 - + 2))); - - LOGV_IF(delta > (1<<30), " delta overflow %d", delta); - - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, dG2 %d, b %d, delta %d, m_nCosWT_5KHz %d", dG2, b, delta, pReverb->m_nCosWT_5KHz); - // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 - pReverb->m_nRoomLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; } - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, olg DC gain %d new m_nRoomLpfFbk %d, old m_nRoomLpfFwd %d", - temp2, pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFwd); - pReverb->m_nRoomLpfFwd - = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRoomLpfFbk)); - LOGV("REVERB_PARAM_ROOM_HF_LEVEL, new m_nRoomLpfFwd %d", pReverb->m_nRoomLpfFwd); - - if (param == REVERB_PARAM_ROOM_HF_LEVEL) - break; - value32 = pProperties->decayTime; - /* FALL THROUGH */ - - case REVERB_PARAM_DECAY_TIME: - - // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk) - // convert ms to samples - value32 = (value32 * pReverb->m_nSamplingRate) / 1000; - - // calculate valid decay time range as a function of current reverb delay and - // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB - // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels. - // g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time - averageDelay = pReverb->m_nLateDelay - pReverb->m_nMaxExcursion; - averageDelay += ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) - + (pReverb->m_sAp1.m_zApOut - pReverb->m_sAp1.m_zApIn)) >> 1; - - temp = (-6000 * averageDelay) / value32; - LOGV("REVERB_PARAM_DECAY_TIME, delay smps %d, DT smps %d, gain mB %d",averageDelay, value32, temp); - if (temp < -4000 || temp > -100) + if (size != paramSize) { return -EINVAL; + } - // calculate low pass gain by adding reverb input attenuation (pReverb->m_nLateGain) and substrating output - // xfade and sum gain (max +9dB) - temp -= Effects_Linear16ToMillibels(pReverb->m_nLateGain) + 900; - temp = Effects_MillibelsToLinear16(temp); - - // DC gain (temp) = b0 / (1 + a1) = pReverb->m_nRvbLpfFwd / (32767 - pReverb->m_nRvbLpfFbk) - pReverb->m_nRvbLpfFwd - = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRvbLpfFbk)); + if (paramSize == sizeof(int16_t)) { + value16 = *(int16_t *) pValue; + } else if (paramSize == sizeof(int32_t)) { + value32 = *(int32_t *) pValue; + } else { + pProperties = (t_reverb_properties *) pValue; + } - LOGV("REVERB_PARAM_DECAY_TIME, gain %d, new m_nRvbLpfFwd %d, old m_nRvbLpfFbk %d, reverb gain %d", temp, pReverb->m_nRvbLpfFwd, pReverb->m_nRvbLpfFbk, Effects_Linear16ToMillibels(pReverb->m_nLateGain)); + pPreset = &pReverb->m_sPreset.m_sPreset[pReverb->m_nNextRoom]; - if (param == REVERB_PARAM_DECAY_TIME) + switch (param) { + case REVERB_PARAM_BYPASS: + pReverb->m_bBypass = (uint16_t)value32; break; - value16 = pProperties->decayHFRatio; - /* FALL THROUGH */ - case REVERB_PARAM_DECAY_HF_RATIO: + case REVERB_PARAM_PROPERTIES: + value16 = pProperties->roomLevel; + /* FALL THROUGH */ - // We limit max value to 1000 because reverb filter is lowpass only - if (value16 < 100 || value16 > 1000) - return -EINVAL; - // Convert per mille to => m_nLpfFwd, m_nLpfFbk - - // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged - // while changing HF level - temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); + case REVERB_PARAM_ROOM_LEVEL: + // Convert millibels to linear 16 bit signed => m_nRoomLpfFwd + if (value16 > 0) + return -EINVAL; - if (value16 == 1000) { - pReverb->m_nRvbLpfFbk = 0; - } else { - int32_t dG2, b, delta; + temp = Effects_MillibelsToLinear16(value16); - temp = Effects_Linear16ToMillibels(temp2); - // G_5000Hz = G_DC * (1000/REVERB_PARAM_DECAY_HF_RATIO) in millibels + pReverb->m_nRoomLpfFwd + = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRoomLpfFbk)); + + LOGV("REVERB_PARAM_ROOM_LEVEL, gain %d, new m_nRoomLpfFwd %d, m_nRoomLpfFbk %d", temp, pReverb->m_nRoomLpfFwd, pReverb->m_nRoomLpfFbk); + if (param == REVERB_PARAM_ROOM_LEVEL) + break; + value16 = pProperties->roomHFLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_ROOM_HF_LEVEL: + + // Limit to 0 , -40dB range because of low pass implementation + if (value16 > 0 || value16 < -4000) + return -EINVAL; + // Convert attenuation @ 5000H expressed in millibels to => m_nRoomLpfFbk + // m_nRoomLpfFbk is -a1 where a1 is the solution of: + // a1^2 + 2*(C-dG^2)/(1-dG^2)*a1 + 1 = 0 where: + // - C is cos(2*pi*5000/Fs) (pReverb->m_nCosWT_5KHz) + // - dG is G0/Gf (G0 is the linear gain at DC and Gf is the wanted gain at 5000Hz) + + // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged + // while changing HF level + temp2 = (pReverb->m_nRoomLpfFwd << 15) / (32767 + - pReverb->m_nRoomLpfFbk); + if (value16 == 0) { + pReverb->m_nRoomLpfFbk = 0; + } else { + int32_t dG2, b, delta; + + // dG^2 + temp = Effects_MillibelsToLinear16(value16); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, HF gain %d", temp); + temp = (1 << 30) / temp; + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain %d", temp); + dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, 1/ HF gain ^ 2 %d", dG2); + // b = 2*(C-dG^2)/(1-dG^2) + b = (int32_t) ((((int64_t) 1 << (15 + 1)) + * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) + / ((int64_t) 32767 - (int64_t) dG2)); + + // delta = b^2 - 4 + delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 + + 2))); + + LOGV_IF(delta > (1<<30), " delta overflow %d", delta); + + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, dG2 %d, b %d, delta %d, m_nCosWT_5KHz %d", dG2, b, delta, pReverb->m_nCosWT_5KHz); + // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 + pReverb->m_nRoomLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; + } + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, olg DC gain %d new m_nRoomLpfFbk %d, old m_nRoomLpfFwd %d", + temp2, pReverb->m_nRoomLpfFbk, pReverb->m_nRoomLpfFwd); + + pReverb->m_nRoomLpfFwd + = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRoomLpfFbk)); + LOGV("REVERB_PARAM_ROOM_HF_LEVEL, new m_nRoomLpfFwd %d", pReverb->m_nRoomLpfFwd); + + if (param == REVERB_PARAM_ROOM_HF_LEVEL) + break; + value32 = pProperties->decayTime; + /* FALL THROUGH */ + + case REVERB_PARAM_DECAY_TIME: + + // Convert milliseconds to => m_nRvbLpfFwd (function of m_nRvbLpfFbk) + // convert ms to samples + value32 = (value32 * pReverb->m_nSamplingRate) / 1000; + + // calculate valid decay time range as a function of current reverb delay and + // max feed back gain. Min value <=> -40dB in one pass, Max value <=> feedback gain = -1 dB + // Calculate attenuation for each round in late reverb given a total attenuation of -6000 millibels. + // g = -6000 d/DT , g gain in millibels, d reverb delay, DT decay time + averageDelay = pReverb->m_nLateDelay - pReverb->m_nMaxExcursion; + averageDelay += ((pReverb->m_sAp0.m_zApOut - pReverb->m_sAp0.m_zApIn) + + (pReverb->m_sAp1.m_zApOut - pReverb->m_sAp1.m_zApIn)) >> 1; + + temp = (-6000 * averageDelay) / value32; + LOGV("REVERB_PARAM_DECAY_TIME, delay smps %d, DT smps %d, gain mB %d",averageDelay, value32, temp); + if (temp < -4000 || temp > -100) + return -EINVAL; + + // calculate low pass gain by adding reverb input attenuation (pReverb->m_nLateGain) and substrating output + // xfade and sum gain (max +9dB) + temp -= Effects_Linear16ToMillibels(pReverb->m_nLateGain) + 900; + temp = Effects_MillibelsToLinear16(temp); - value32 = ((int32_t) 1000 << 15) / (int32_t) value16; - LOGV("REVERB_PARAM_DECAY_HF_RATIO, DC gain %d, DC gain mB %d, 1000/R %d", temp2, temp, value32); + // DC gain (temp) = b0 / (1 + a1) = pReverb->m_nRvbLpfFwd / (32767 - pReverb->m_nRvbLpfFbk) + pReverb->m_nRvbLpfFwd + = MULT_EG1_EG1(temp, (32767 - pReverb->m_nRvbLpfFbk)); - temp = (int32_t) (((int64_t) temp * (int64_t) value32) >> 15); + LOGV("REVERB_PARAM_DECAY_TIME, gain %d, new m_nRvbLpfFwd %d, old m_nRvbLpfFbk %d, reverb gain %d", temp, pReverb->m_nRvbLpfFwd, pReverb->m_nRvbLpfFbk, Effects_Linear16ToMillibels(pReverb->m_nLateGain)); - if (temp < -4000) { - LOGV("REVERB_PARAM_DECAY_HF_RATIO HF gain overflow %d mB", temp); - temp = -4000; - } + if (param == REVERB_PARAM_DECAY_TIME) + break; + value16 = pProperties->decayHFRatio; + /* FALL THROUGH */ - temp = Effects_MillibelsToLinear16(temp); - LOGV("REVERB_PARAM_DECAY_HF_RATIO, HF gain %d", temp); - // dG^2 - temp = (temp2 << 15) / temp; - dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); + case REVERB_PARAM_DECAY_HF_RATIO: - // b = 2*(C-dG^2)/(1-dG^2) - b = (int32_t) ((((int64_t) 1 << (15 + 1)) - * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) - / ((int64_t) 32767 - (int64_t) dG2)); + // We limit max value to 1000 because reverb filter is lowpass only + if (value16 < 100 || value16 > 1000) + return -EINVAL; + // Convert per mille to => m_nLpfFwd, m_nLpfFbk - // delta = b^2 - 4 - delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 - + 2))); + // Save current DC gain m_nRoomLpfFwd / (32767 - m_nRoomLpfFbk) to keep it unchanged + // while changing HF level + temp2 = (pReverb->m_nRvbLpfFwd << 15) / (32767 - pReverb->m_nRvbLpfFbk); - // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 - pReverb->m_nRvbLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; + if (value16 == 1000) { + pReverb->m_nRvbLpfFbk = 0; + } else { + int32_t dG2, b, delta; - LOGV("REVERB_PARAM_DECAY_HF_RATIO, dG2 %d, b %d, delta %d", dG2, b, delta); + temp = Effects_Linear16ToMillibels(temp2); + // G_5000Hz = G_DC * (1000/REVERB_PARAM_DECAY_HF_RATIO) in millibels - } + value32 = ((int32_t) 1000 << 15) / (int32_t) value16; + LOGV("REVERB_PARAM_DECAY_HF_RATIO, DC gain %d, DC gain mB %d, 1000/R %d", temp2, temp, value32); - LOGV("REVERB_PARAM_DECAY_HF_RATIO, gain %d, m_nRvbLpfFbk %d, m_nRvbLpfFwd %d", temp2, pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFwd); + temp = (int32_t) (((int64_t) temp * (int64_t) value32) >> 15); - pReverb->m_nRvbLpfFwd - = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRvbLpfFbk)); + if (temp < -4000) { + LOGV("REVERB_PARAM_DECAY_HF_RATIO HF gain overflow %d mB", temp); + temp = -4000; + } - if (param == REVERB_PARAM_DECAY_HF_RATIO) - break; - value16 = pProperties->reflectionsLevel; - /* FALL THROUGH */ + temp = Effects_MillibelsToLinear16(temp); + LOGV("REVERB_PARAM_DECAY_HF_RATIO, HF gain %d", temp); + // dG^2 + temp = (temp2 << 15) / temp; + dG2 = (int32_t) (((int64_t) temp * (int64_t) temp) >> 15); - case REVERB_PARAM_REFLECTIONS_LEVEL: - // We limit max value to 0 because gain is limited to 0dB - if (value16 > 0 || value16 < -6000) - return -EINVAL; + // b = 2*(C-dG^2)/(1-dG^2) + b = (int32_t) ((((int64_t) 1 << (15 + 1)) + * ((int64_t) pReverb->m_nCosWT_5KHz - (int64_t) dG2)) + / ((int64_t) 32767 - (int64_t) dG2)); - // Convert millibels to linear 16 bit signed and recompute m_sEarlyL.m_nGain[i] and m_sEarlyR.m_nGain[i]. - value16 = Effects_MillibelsToLinear16(value16); - for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { - pReverb->m_sEarlyL.m_nGain[i] - = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],value16); - pReverb->m_sEarlyR.m_nGain[i] - = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],value16); - } - pReverb->m_nEarlyGain = value16; - LOGV("REVERB_PARAM_REFLECTIONS_LEVEL, m_nEarlyGain %d", pReverb->m_nEarlyGain); + // delta = b^2 - 4 + delta = (int32_t) ((((int64_t) b * (int64_t) b) >> 15) - (1 << (15 + + 2))); - if (param == REVERB_PARAM_REFLECTIONS_LEVEL) - break; - value32 = pProperties->reflectionsDelay; - /* FALL THROUGH */ - - case REVERB_PARAM_REFLECTIONS_DELAY: - // We limit max value MAX_EARLY_TIME - // convert ms to time units - temp = (value32 * 65536) / 1000; - if (temp < 0 || temp > MAX_EARLY_TIME) - return -EINVAL; + // m_nRoomLpfFbk = -a1 = - (- b + sqrt(delta)) / 2 + pReverb->m_nRvbLpfFbk = (b - Effects_Sqrt(delta) * 181) >> 1; - maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate) - >> 16; - temp = (temp * pReverb->m_nSamplingRate) >> 16; - for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { - temp2 = temp + (((int32_t) pPreset->m_sEarlyL.m_zDelay[i] - * pReverb->m_nSamplingRate) >> 16); - if (temp2 > maxSamples) - temp2 = maxSamples; - pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp2; - temp2 = temp + (((int32_t) pPreset->m_sEarlyR.m_zDelay[i] - * pReverb->m_nSamplingRate) >> 16); - if (temp2 > maxSamples) - temp2 = maxSamples; - pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp2; - } - pReverb->m_nEarlyDelay = temp; + LOGV("REVERB_PARAM_DECAY_HF_RATIO, dG2 %d, b %d, delta %d", dG2, b, delta); - LOGV("REVERB_PARAM_REFLECTIONS_DELAY, m_nEarlyDelay smps %d max smp delay %d", pReverb->m_nEarlyDelay, maxSamples); + } - // Convert milliseconds to sample count => m_nEarlyDelay - if (param == REVERB_PARAM_REFLECTIONS_DELAY) - break; - value16 = pProperties->reverbLevel; - /* FALL THROUGH */ + LOGV("REVERB_PARAM_DECAY_HF_RATIO, gain %d, m_nRvbLpfFbk %d, m_nRvbLpfFwd %d", temp2, pReverb->m_nRvbLpfFbk, pReverb->m_nRvbLpfFwd); - case REVERB_PARAM_REVERB_LEVEL: - // We limit max value to 0 because gain is limited to 0dB - if (value16 > 0 || value16 < -6000) - return -EINVAL; - // Convert millibels to linear 16 bits (gange 0 - 8191) => m_nLateGain. - pReverb->m_nLateGain = Effects_MillibelsToLinear16(value16) >> 2; + pReverb->m_nRvbLpfFwd + = MULT_EG1_EG1(temp2, (32767 - pReverb->m_nRvbLpfFbk)); - LOGV("REVERB_PARAM_REVERB_LEVEL, m_nLateGain %d", pReverb->m_nLateGain); + if (param == REVERB_PARAM_DECAY_HF_RATIO) + break; + value16 = pProperties->reflectionsLevel; + /* FALL THROUGH */ - if (param == REVERB_PARAM_REVERB_LEVEL) - break; - value32 = pProperties->reverbDelay; - /* FALL THROUGH */ - - case REVERB_PARAM_REVERB_DELAY: - // We limit max value to MAX_DELAY_TIME - // convert ms to time units - temp = (value32 * 65536) / 1000; - if (temp < 0 || temp > MAX_DELAY_TIME) - return -EINVAL; + case REVERB_PARAM_REFLECTIONS_LEVEL: + // We limit max value to 0 because gain is limited to 0dB + if (value16 > 0 || value16 < -6000) + return -EINVAL; - maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate) - >> 16; - temp = (temp * pReverb->m_nSamplingRate) >> 16; - if ((temp + pReverb->m_nMaxExcursion) > maxSamples) { - temp = maxSamples - pReverb->m_nMaxExcursion; - } - if (temp < pReverb->m_nMaxExcursion) { - temp = pReverb->m_nMaxExcursion; - } + // Convert millibels to linear 16 bit signed and recompute m_sEarlyL.m_nGain[i] and m_sEarlyR.m_nGain[i]. + value16 = Effects_MillibelsToLinear16(value16); + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + pReverb->m_sEarlyL.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyL.m_nGain[i],value16); + pReverb->m_sEarlyR.m_nGain[i] + = MULT_EG1_EG1(pPreset->m_sEarlyR.m_nGain[i],value16); + } + pReverb->m_nEarlyGain = value16; + LOGV("REVERB_PARAM_REFLECTIONS_LEVEL, m_nEarlyGain %d", pReverb->m_nEarlyGain); + + if (param == REVERB_PARAM_REFLECTIONS_LEVEL) + break; + value32 = pProperties->reflectionsDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REFLECTIONS_DELAY: + // We limit max value MAX_EARLY_TIME + // convert ms to time units + temp = (value32 * 65536) / 1000; + if (temp < 0 || temp > MAX_EARLY_TIME) + return -EINVAL; + + maxSamples = (int32_t) (MAX_EARLY_TIME * pReverb->m_nSamplingRate) + >> 16; + temp = (temp * pReverb->m_nSamplingRate) >> 16; + for (i = 0; i < REVERB_MAX_NUM_REFLECTIONS; i++) { + temp2 = temp + (((int32_t) pPreset->m_sEarlyL.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp2 > maxSamples) + temp2 = maxSamples; + pReverb->m_sEarlyL.m_zDelay[i] = pReverb->m_nEarly0in + temp2; + temp2 = temp + (((int32_t) pPreset->m_sEarlyR.m_zDelay[i] + * pReverb->m_nSamplingRate) >> 16); + if (temp2 > maxSamples) + temp2 = maxSamples; + pReverb->m_sEarlyR.m_zDelay[i] = pReverb->m_nEarly1in + temp2; + } + pReverb->m_nEarlyDelay = temp; + + LOGV("REVERB_PARAM_REFLECTIONS_DELAY, m_nEarlyDelay smps %d max smp delay %d", pReverb->m_nEarlyDelay, maxSamples); + + // Convert milliseconds to sample count => m_nEarlyDelay + if (param == REVERB_PARAM_REFLECTIONS_DELAY) + break; + value16 = pProperties->reverbLevel; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_LEVEL: + // We limit max value to 0 because gain is limited to 0dB + if (value16 > 0 || value16 < -6000) + return -EINVAL; + // Convert millibels to linear 16 bits (gange 0 - 8191) => m_nLateGain. + pReverb->m_nLateGain = Effects_MillibelsToLinear16(value16) >> 2; + + LOGV("REVERB_PARAM_REVERB_LEVEL, m_nLateGain %d", pReverb->m_nLateGain); + + if (param == REVERB_PARAM_REVERB_LEVEL) + break; + value32 = pProperties->reverbDelay; + /* FALL THROUGH */ + + case REVERB_PARAM_REVERB_DELAY: + // We limit max value to MAX_DELAY_TIME + // convert ms to time units + temp = (value32 * 65536) / 1000; + if (temp < 0 || temp > MAX_DELAY_TIME) + return -EINVAL; + + maxSamples = (int32_t) (MAX_DELAY_TIME * pReverb->m_nSamplingRate) + >> 16; + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if ((temp + pReverb->m_nMaxExcursion) > maxSamples) { + temp = maxSamples - pReverb->m_nMaxExcursion; + } + if (temp < pReverb->m_nMaxExcursion) { + temp = pReverb->m_nMaxExcursion; + } - temp -= pReverb->m_nLateDelay; - pReverb->m_nDelay0Out += temp; - pReverb->m_nDelay1Out += temp; - pReverb->m_nLateDelay += temp; + temp -= pReverb->m_nLateDelay; + pReverb->m_nDelay0Out += temp; + pReverb->m_nDelay1Out += temp; + pReverb->m_nLateDelay += temp; - LOGV("REVERB_PARAM_REVERB_DELAY, m_nLateDelay smps %d max smp delay %d", pReverb->m_nLateDelay, maxSamples); + LOGV("REVERB_PARAM_REVERB_DELAY, m_nLateDelay smps %d max smp delay %d", pReverb->m_nLateDelay, maxSamples); - // Convert milliseconds to sample count => m_nDelay1Out + m_nMaxExcursion - if (param == REVERB_PARAM_REVERB_DELAY) - break; + // Convert milliseconds to sample count => m_nDelay1Out + m_nMaxExcursion + if (param == REVERB_PARAM_REVERB_DELAY) + break; - value16 = pProperties->diffusion; - /* FALL THROUGH */ + value16 = pProperties->diffusion; + /* FALL THROUGH */ - case REVERB_PARAM_DIFFUSION: - if (value16 < 0 || value16 > 1000) - return -EINVAL; + case REVERB_PARAM_DIFFUSION: + if (value16 < 0 || value16 > 1000) + return -EINVAL; - // Convert per mille to m_sAp0.m_nApGain, m_sAp1.m_nApGain - pReverb->m_sAp0.m_nApGain = AP0_GAIN_BASE + ((int32_t) value16 - * AP0_GAIN_RANGE) / 1000; - pReverb->m_sAp1.m_nApGain = AP1_GAIN_BASE + ((int32_t) value16 - * AP1_GAIN_RANGE) / 1000; + // Convert per mille to m_sAp0.m_nApGain, m_sAp1.m_nApGain + pReverb->m_sAp0.m_nApGain = AP0_GAIN_BASE + ((int32_t) value16 + * AP0_GAIN_RANGE) / 1000; + pReverb->m_sAp1.m_nApGain = AP1_GAIN_BASE + ((int32_t) value16 + * AP1_GAIN_RANGE) / 1000; - LOGV("REVERB_PARAM_DIFFUSION, m_sAp0.m_nApGain %d m_sAp1.m_nApGain %d", pReverb->m_sAp0.m_nApGain, pReverb->m_sAp1.m_nApGain); + LOGV("REVERB_PARAM_DIFFUSION, m_sAp0.m_nApGain %d m_sAp1.m_nApGain %d", pReverb->m_sAp0.m_nApGain, pReverb->m_sAp1.m_nApGain); - if (param == REVERB_PARAM_DIFFUSION) - break; + if (param == REVERB_PARAM_DIFFUSION) + break; - value16 = pProperties->density; - /* FALL THROUGH */ + value16 = pProperties->density; + /* FALL THROUGH */ - case REVERB_PARAM_DENSITY: - if (value16 < 0 || value16 > 1000) - return -EINVAL; + case REVERB_PARAM_DENSITY: + if (value16 < 0 || value16 > 1000) + return -EINVAL; - // Convert per mille to m_sAp0.m_zApOut, m_sAp1.m_zApOut - maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16; + // Convert per mille to m_sAp0.m_zApOut, m_sAp1.m_zApOut + maxSamples = (int32_t) (MAX_AP_TIME * pReverb->m_nSamplingRate) >> 16; - temp = AP0_TIME_BASE + ((int32_t) value16 * AP0_TIME_RANGE) / 1000; - /*lint -e{702} shift for performance */ - temp = (temp * pReverb->m_nSamplingRate) >> 16; - if (temp > maxSamples) - temp = maxSamples; - pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp); + temp = AP0_TIME_BASE + ((int32_t) value16 * AP0_TIME_RANGE) / 1000; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp0.m_zApOut = (uint16_t) (pReverb->m_sAp0.m_zApIn + temp); - LOGV("REVERB_PARAM_DENSITY, Ap0 delay smps %d", temp); + LOGV("REVERB_PARAM_DENSITY, Ap0 delay smps %d", temp); - temp = AP1_TIME_BASE + ((int32_t) value16 * AP1_TIME_RANGE) / 1000; - /*lint -e{702} shift for performance */ - temp = (temp * pReverb->m_nSamplingRate) >> 16; - if (temp > maxSamples) - temp = maxSamples; - pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp); + temp = AP1_TIME_BASE + ((int32_t) value16 * AP1_TIME_RANGE) / 1000; + /*lint -e{702} shift for performance */ + temp = (temp * pReverb->m_nSamplingRate) >> 16; + if (temp > maxSamples) + temp = maxSamples; + pReverb->m_sAp1.m_zApOut = (uint16_t) (pReverb->m_sAp1.m_zApIn + temp); - LOGV("Ap1 delay smps %d", temp); + LOGV("Ap1 delay smps %d", temp); - break; + break; - default: - break; + default: + break; + } } + return 0; } /* end Reverb_setParameter */ @@ -1905,24 +1915,26 @@ static int ReverbUpdateRoom(reverb_object_t *pReverb, bool fullUpdate) { */ static int ReverbReadInPresets(reverb_object_t *pReverb) { - int preset = 0; - int defaultPreset = 0; + int preset; - //now init any remaining presets to defaults - for (defaultPreset = preset; defaultPreset < REVERB_MAX_ROOM_TYPE; defaultPreset++) { - reverb_preset_t *pPreset = &pReverb->m_sPreset.m_sPreset[defaultPreset]; - if (defaultPreset == 0 || defaultPreset > REVERB_MAX_ROOM_TYPE - 1) { - pPreset->m_nRvbLpfFbk = 8307; - pPreset->m_nRvbLpfFwd = 14768; + // this is for test only. OpenSL ES presets are mapped to 4 presets. + // REVERB_PRESET_NONE is mapped to bypass + for (preset = 0; preset < REVERB_NUM_PRESETS; preset++) { + reverb_preset_t *pPreset = &pReverb->m_sPreset.m_sPreset[preset]; + switch (preset + 1) { + case REVERB_PRESET_PLATE: + case REVERB_PRESET_SMALLROOM: + pPreset->m_nRvbLpfFbk = 5077; + pPreset->m_nRvbLpfFwd = 11076; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 24569; + pPreset->m_nRoomLpfFwd = 20474; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; - pPreset->m_sEarlyL.m_zDelay[1] = 2163; + pPreset->m_sEarlyL.m_zDelay[1] = 1462; pPreset->m_sEarlyL.m_nGain[1] = 17537; pPreset->m_sEarlyL.m_zDelay[2] = 0; pPreset->m_sEarlyL.m_nGain[2] = 14768; @@ -1941,11 +1953,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6388; - pPreset->m_nAp0_ApGain = 15691; - pPreset->m_nAp0_ApOut = 711; - pPreset->m_nAp1_ApGain = 16317; - pPreset->m_nAp1_ApOut = 1029; + pPreset->m_nXfadeInterval = 6470; //6483; + pPreset->m_nAp0_ApGain = 14768; + pPreset->m_nAp0_ApOut = 792; + pPreset->m_nAp1_ApGain = 14777; + pPreset->m_nAp1_ApOut = 1191; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -1953,15 +1965,17 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; - } else if (defaultPreset == 1) { - pPreset->m_nRvbLpfFbk = 6461; - pPreset->m_nRvbLpfFwd = 14307; + break; + case REVERB_PRESET_MEDIUMROOM: + case REVERB_PRESET_LARGEROOM: + pPreset->m_nRvbLpfFbk = 5077; + pPreset->m_nRvbLpfFwd = 12922; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 24569; + pPreset->m_nRoomLpfFwd = 21703; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; pPreset->m_sEarlyL.m_zDelay[1] = 1462; @@ -1983,11 +1997,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6391; - pPreset->m_nAp0_ApGain = 15230; - pPreset->m_nAp0_ApOut = 708; - pPreset->m_nAp1_ApGain = 15547; - pPreset->m_nAp1_ApOut = 1023; + pPreset->m_nXfadeInterval = 6449; + pPreset->m_nAp0_ApGain = 15691; + pPreset->m_nAp0_ApOut = 774; + pPreset->m_nAp1_ApGain = 16317; + pPreset->m_nAp1_ApOut = 1155; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -1995,15 +2009,16 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; - } else if (defaultPreset == 2) { - pPreset->m_nRvbLpfFbk = 5077; - pPreset->m_nRvbLpfFwd = 12922; + break; + case REVERB_PRESET_MEDIUMHALL: + pPreset->m_nRvbLpfFbk = 6461; + pPreset->m_nRvbLpfFwd = 14307; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 21703; + pPreset->m_nRoomLpfFwd = 24569; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; pPreset->m_sEarlyL.m_zDelay[1] = 1462; @@ -2025,11 +2040,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6449; - pPreset->m_nAp0_ApGain = 15691; - pPreset->m_nAp0_ApOut = 774; - pPreset->m_nAp1_ApGain = 16317; - pPreset->m_nAp1_ApOut = 1155; + pPreset->m_nXfadeInterval = 6391; + pPreset->m_nAp0_ApGain = 15230; + pPreset->m_nAp0_ApOut = 708; + pPreset->m_nAp1_ApGain = 15547; + pPreset->m_nAp1_ApOut = 1023; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -2037,18 +2052,19 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; - } else if (defaultPreset == 3) { - pPreset->m_nRvbLpfFbk = 5077; - pPreset->m_nRvbLpfFwd = 11076; + break; + case REVERB_PRESET_LARGEHALL: + pPreset->m_nRvbLpfFbk = 8307; + pPreset->m_nRvbLpfFwd = 14768; pPreset->m_nEarlyGain = 27690; pPreset->m_nEarlyDelay = 1311; pPreset->m_nLateGain = 8191; pPreset->m_nLateDelay = 3932; pPreset->m_nRoomLpfFbk = 3692; - pPreset->m_nRoomLpfFwd = 20474; + pPreset->m_nRoomLpfFwd = 24569; pPreset->m_sEarlyL.m_zDelay[0] = 1376; pPreset->m_sEarlyL.m_nGain[0] = 22152; - pPreset->m_sEarlyL.m_zDelay[1] = 1462; + pPreset->m_sEarlyL.m_zDelay[1] = 2163; pPreset->m_sEarlyL.m_nGain[1] = 17537; pPreset->m_sEarlyL.m_zDelay[2] = 0; pPreset->m_sEarlyL.m_nGain[2] = 14768; @@ -2067,11 +2083,11 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_sEarlyR.m_zDelay[4] = 0; pPreset->m_sEarlyR.m_nGain[4] = 13384; pPreset->m_nMaxExcursion = 127; - pPreset->m_nXfadeInterval = 6470; //6483; - pPreset->m_nAp0_ApGain = 14768; - pPreset->m_nAp0_ApOut = 792; - pPreset->m_nAp1_ApGain = 14777; - pPreset->m_nAp1_ApOut = 1191; + pPreset->m_nXfadeInterval = 6388; + pPreset->m_nAp0_ApGain = 15691; + pPreset->m_nAp0_ApOut = 711; + pPreset->m_nAp1_ApGain = 16317; + pPreset->m_nAp1_ApOut = 1029; pPreset->m_rfu4 = 0; pPreset->m_rfu5 = 0; pPreset->m_rfu6 = 0; @@ -2079,6 +2095,7 @@ static int ReverbReadInPresets(reverb_object_t *pReverb) { pPreset->m_rfu8 = 0; pPreset->m_rfu9 = 0; pPreset->m_rfu10 = 0; + break; } } diff --git a/media/libeffects/EffectReverb.h b/media/libeffects/EffectReverb.h index f5aadfa..5af316d 100644 --- a/media/libeffects/EffectReverb.h +++ b/media/libeffects/EffectReverb.h @@ -17,7 +17,8 @@ #ifndef ANDROID_EFFECTREVERB_H_ #define ANDROID_EFFECTREVERB_H_ -#include <media/EffectReverbApi.h> +#include <media/EffectEnvironmentalReverbApi.h> +#include <media/EffectPresetReverbApi.h> /*------------------------------------ @@ -43,7 +44,7 @@ if the buffer size is a power of two. #define REVERB_BUFFER_SIZE_IN_SAMPLES_MAX 16384 -#define REVERB_MAX_ROOM_TYPE 4 // any room numbers larger than this are invalid +#define REVERB_NUM_PRESETS REVERB_PRESET_PLATE // REVERB_PRESET_NONE is not included #define REVERB_MAX_NUM_REFLECTIONS 5 // max num reflections per channel @@ -171,7 +172,7 @@ typedef struct typedef struct { - reverb_preset_t m_sPreset[REVERB_MAX_ROOM_TYPE]; //array of presets + reverb_preset_t m_sPreset[REVERB_NUM_PRESETS]; // array of presets(does not include REVERB_PRESET_NONE) } reverb_preset_bank_t; diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index 783249d..df0f73b 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -52,7 +52,7 @@ AudioEffect::AudioEffect(const effect_uuid_t *type, ) : mStatus(NO_INIT) { - mStatus = set(type, uuid, priority, cbf, user, output, sessionId); + mStatus = set(type, uuid, priority, cbf, user, sessionId, output); } AudioEffect::AudioEffect(const char *typeStr, @@ -84,7 +84,7 @@ AudioEffect::AudioEffect(const char *typeStr, } } - mStatus = set(pType, pUuid, priority, cbf, user, output, sessionId); + mStatus = set(pType, pUuid, priority, cbf, user, sessionId, output); } status_t AudioEffect::set(const effect_uuid_t *type, diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 4872047..5401ec0 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -511,11 +511,17 @@ status_t MediaPlayerService::dump(int fd, const Vector<String16>& args) sp<Client> c = mClients[i].promote(); if (c != 0) c->dump(fd, args); } - for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) { - result.append(" MediaRecorderClient\n"); - sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote(); - snprintf(buffer, 255, " pid(%d)\n\n", c->mPid); - result.append(buffer); + if (mMediaRecorderClients.size() == 0) { + result.append(" No media recorder client\n\n"); + } else { + for (int i = 0, n = mMediaRecorderClients.size(); i < n; ++i) { + sp<MediaRecorderClient> c = mMediaRecorderClients[i].promote(); + snprintf(buffer, 255, " MediaRecorderClient pid(%d)\n", c->mPid); + result.append(buffer); + write(fd, result.string(), result.size()); + result = "\n"; + c->dump(fd, args); + } } result.append(" Files opened and/or mapped:\n"); diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 80b1cfd..fef3e6e 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -329,5 +329,12 @@ status_t MediaRecorderClient::setListener(const sp<IMediaRecorderClient>& listen return mRecorder->setListener(listener); } +status_t MediaRecorderClient::dump(int fd, const Vector<String16>& args) const { + if (mRecorder != NULL) { + return mRecorder->dump(fd, args); + } + return OK; +} + }; // namespace android diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index b53d950..d12e558 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -28,7 +28,7 @@ class MediaPlayerService; class MediaRecorderClient : public BnMediaRecorder { public: - virtual status_t setCamera(const sp<ICamera>& camera); + virtual status_t setCamera(const sp<ICamera>& camera); virtual status_t setPreviewSurface(const sp<ISurface>& surface); virtual status_t setVideoSource(int vs); virtual status_t setAudioSource(int as); @@ -45,21 +45,22 @@ public: virtual status_t getMaxAmplitude(int* max); virtual status_t start(); virtual status_t stop(); - virtual status_t reset(); + virtual status_t reset(); virtual status_t init(); virtual status_t close(); virtual status_t release(); + virtual status_t dump(int fd, const Vector<String16>& args) const; private: - friend class MediaPlayerService; // for accessing private constructor + friend class MediaPlayerService; // for accessing private constructor - MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid); - virtual ~MediaRecorderClient(); + MediaRecorderClient(const sp<MediaPlayerService>& service, pid_t pid); + virtual ~MediaRecorderClient(); - pid_t mPid; - Mutex mLock; - MediaRecorderBase *mRecorder; - sp<MediaPlayerService> mMediaPlayerService; + pid_t mPid; + Mutex mLock; + MediaRecorderBase *mRecorder; + sp<MediaPlayerService> mMediaPlayerService; }; }; // namespace android diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 50f74f2..72061ad 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1057,4 +1057,64 @@ status_t StagefrightRecorder::getMaxAmplitude(int *max) { return OK; } +status_t StagefrightRecorder::dump(int fd, const Vector<String16>& args) const { + const size_t SIZE = 256; + char buffer[SIZE]; + String8 result; + snprintf(buffer, SIZE, " Recorder: %p", this); + snprintf(buffer, SIZE, " Output file (fd %d):\n", mOutputFd); + result.append(buffer); + snprintf(buffer, SIZE, " File format: %d\n", mOutputFormat); + result.append(buffer); + snprintf(buffer, SIZE, " Max file size (bytes): %lld\n", mMaxFileSizeBytes); + result.append(buffer); + snprintf(buffer, SIZE, " Max file duration (us): %lld\n", mMaxFileDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " File offset length (bits): %d\n", mUse64BitFileOffset? 64: 32); + result.append(buffer); + snprintf(buffer, SIZE, " Interleave duration (us): %d\n", mInterleaveDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " Progress notification: %d frames\n", mTrackEveryNumberOfFrames); + result.append(buffer); + snprintf(buffer, SIZE, " Progress notification: %lld us\n", mTrackEveryTimeDurationUs); + result.append(buffer); + snprintf(buffer, SIZE, " Audio\n"); + result.append(buffer); + snprintf(buffer, SIZE, " Source: %d\n", mAudioSource); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder: %d\n", mAudioEncoder); + result.append(buffer); + snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mAudioBitRate); + result.append(buffer); + snprintf(buffer, SIZE, " Sampling rate (hz): %d\n", mSampleRate); + result.append(buffer); + snprintf(buffer, SIZE, " Number of channels: %d\n", mAudioChannels); + result.append(buffer); + snprintf(buffer, SIZE, " Max amplitude: %d\n", mAudioSourceNode == 0? 0: mAudioSourceNode->getMaxAmplitude()); + result.append(buffer); + snprintf(buffer, SIZE, " Video\n"); + result.append(buffer); + snprintf(buffer, SIZE, " Source: %d\n", mVideoSource); + result.append(buffer); + snprintf(buffer, SIZE, " Camera Id: %d\n", mCameraId); + result.append(buffer); + snprintf(buffer, SIZE, " Camera flags: %d\n", mFlags); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder: %d\n", mVideoEncoder); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder profile: %d\n", mVideoEncoderProfile); + result.append(buffer); + snprintf(buffer, SIZE, " Encoder level: %d\n", mVideoEncoderLevel); + result.append(buffer); + snprintf(buffer, SIZE, " I frames interval (s): %d\n", mIFramesInterval); + result.append(buffer); + snprintf(buffer, SIZE, " Frame size (pixels): %dx%d\n", mVideoWidth, mVideoHeight); + result.append(buffer); + snprintf(buffer, SIZE, " Frame rate (fps): %d\n", mFrameRate); + result.append(buffer); + snprintf(buffer, SIZE, " Bit rate (bps): %d\n", mVideoBitRate); + result.append(buffer); + ::write(fd, result.string(), result.size()); + return OK; +} } // namespace android diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index 85d2557..704523f 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -54,6 +54,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t close(); virtual status_t reset(); virtual status_t getMaxAmplitude(int *max); + virtual status_t dump(int fd, const Vector<String16>& args) const; private: enum CameraFlags { diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 83f7040..efaab5b 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -948,7 +948,7 @@ status_t OMXCodec::getVideoProfileLevel( int32_t supportedProfile = static_cast<int32_t>(param.eProfile); int32_t supportedLevel = static_cast<int32_t>(param.eLevel); - CODEC_LOGV("Supported profile: %ld, level %ld", + CODEC_LOGV("Supported profile: %d, level %d", supportedProfile, supportedLevel); if (profile == supportedProfile && diff --git a/media/libstagefright/codecs/aacdec/AACDecoder.cpp b/media/libstagefright/codecs/aacdec/AACDecoder.cpp index 2bc4448..f3b281f 100644 --- a/media/libstagefright/codecs/aacdec/AACDecoder.cpp +++ b/media/libstagefright/codecs/aacdec/AACDecoder.cpp @@ -15,6 +15,7 @@ */ #include "AACDecoder.h" +#define LOG_TAG "AACDecoder" #include "../../include/ESDS.h" @@ -36,26 +37,33 @@ AACDecoder::AACDecoder(const sp<MediaSource> &source) mAnchorTimeUs(0), mNumSamplesOutput(0), mInputBuffer(NULL) { -} -AACDecoder::~AACDecoder() { - if (mStarted) { - stop(); - } + sp<MetaData> srcFormat = mSource->getFormat(); - delete mConfig; - mConfig = NULL; -} + int32_t sampleRate; + CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); -status_t AACDecoder::start(MetaData *params) { - CHECK(!mStarted); + mMeta = new MetaData; + mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - mBufferGroup = new MediaBufferGroup; - mBufferGroup->add_buffer(new MediaBuffer(2048 * 2)); + // We'll always output stereo, regardless of how many channels are + // present in the input due to decoder limitations. + mMeta->setInt32(kKeyChannelCount, 2); + mMeta->setInt32(kKeySampleRate, sampleRate); + + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + mMeta->setInt64(kKeyDuration, durationUs); + } + mMeta->setCString(kKeyDecoderComponent, "AACDecoder"); + + mInitCheck = initCheck(); +} +status_t AACDecoder::initCheck() { + memset(mConfig, 0, sizeof(tPVMP4AudioDecoderExternal)); mConfig->outputFormat = OUTPUTFORMAT_16PCM_INTERLEAVED; - mConfig->aacPlusUpsamplingFactor = 0; - mConfig->aacPlusEnabled = false; + mConfig->aacPlusEnabled = 1; // The software decoder doesn't properly support mono output on // AACplus files. Always output stereo. @@ -64,8 +72,11 @@ status_t AACDecoder::start(MetaData *params) { UInt32 memRequirements = PVMP4AudioDecoderGetMemRequirements(); mDecoderBuf = malloc(memRequirements); - CHECK_EQ(PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf), - MP4AUDEC_SUCCESS); + status_t err = PVMP4AudioDecoderInitLibrary(mConfig, mDecoderBuf); + if (err != MP4AUDEC_SUCCESS) { + LOGE("Failed to initialize MP4 audio decoder"); + return UNKNOWN_ERROR; + } uint32_t type; const void *data; @@ -83,18 +94,38 @@ status_t AACDecoder::start(MetaData *params) { mConfig->pInputBuffer = (UChar *)codec_specific_data; mConfig->inputBufferCurrentLength = codec_specific_data_size; mConfig->inputBufferMaxLength = 0; - mConfig->inputBufferUsedLength = 0; - mConfig->remainderBits = 0; - - mConfig->pOutputBuffer = NULL; - mConfig->pOutputBuffer_plus = NULL; - mConfig->repositionFlag = false; if (PVMP4AudioDecoderConfig(mConfig, mDecoderBuf) != MP4AUDEC_SUCCESS) { return ERROR_UNSUPPORTED; } + + // Check on the sampling rate to see whether it is changed. + int32_t sampleRate; + CHECK(mMeta->findInt32(kKeySampleRate, &sampleRate)); + if (mConfig->samplingRate != sampleRate) { + mMeta->setInt32(kKeySampleRate, mConfig->samplingRate); + LOGW("Sample rate was %d, but now is %d", + sampleRate, mConfig->samplingRate); + } } + return OK; +} + +AACDecoder::~AACDecoder() { + if (mStarted) { + stop(); + } + + delete mConfig; + mConfig = NULL; +} + +status_t AACDecoder::start(MetaData *params) { + CHECK(!mStarted); + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer(new MediaBuffer(4096 * 2)); mSource->start(); @@ -127,28 +158,7 @@ status_t AACDecoder::stop() { } sp<MetaData> AACDecoder::getFormat() { - sp<MetaData> srcFormat = mSource->getFormat(); - - int32_t sampleRate; - CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); - - sp<MetaData> meta = new MetaData; - meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); - - // We'll always output stereo, regardless of how many channels are - // present in the input due to decoder limitations. - meta->setInt32(kKeyChannelCount, 2); - - meta->setInt32(kKeySampleRate, sampleRate); - - int64_t durationUs; - if (srcFormat->findInt64(kKeyDuration, &durationUs)) { - meta->setInt64(kKeyDuration, durationUs); - } - - meta->setCString(kKeyDecoderComponent, "AACDecoder"); - - return meta; + return mMeta; } status_t AACDecoder::read( @@ -200,13 +210,19 @@ status_t AACDecoder::read( mConfig->remainderBits = 0; mConfig->pOutputBuffer = static_cast<Int16 *>(buffer->data()); - mConfig->pOutputBuffer_plus = NULL; + mConfig->pOutputBuffer_plus = &mConfig->pOutputBuffer[2048]; mConfig->repositionFlag = false; Int decoderErr = PVMP4AudioDecodeFrame(mConfig, mDecoderBuf); size_t numOutBytes = mConfig->frameLength * sizeof(int16_t) * mConfig->desiredChannels; + if (mConfig->aacPlusUpsamplingFactor == 2) { + if (mConfig->desiredChannels == 1) { + memcpy(&mConfig->pOutputBuffer[1024], &mConfig->pOutputBuffer[2048], numOutBytes * 2); + } + numOutBytes *= 2; + } if (decoderErr != MP4AUDEC_SUCCESS) { LOGW("AAC decoder returned error %d, substituting silence", decoderErr); diff --git a/media/libstagefright/include/AACDecoder.h b/media/libstagefright/include/AACDecoder.h index f09addd..200f93c 100644 --- a/media/libstagefright/include/AACDecoder.h +++ b/media/libstagefright/include/AACDecoder.h @@ -25,6 +25,7 @@ struct tPVMP4AudioDecoderExternal; namespace android { struct MediaBufferGroup; +struct MetaData; struct AACDecoder : public MediaSource { AACDecoder(const sp<MediaSource> &source); @@ -41,6 +42,7 @@ protected: virtual ~AACDecoder(); private: + sp<MetaData> mMeta; sp<MediaSource> mSource; bool mStarted; @@ -50,9 +52,11 @@ private: void *mDecoderBuf; int64_t mAnchorTimeUs; int64_t mNumSamplesOutput; + status_t mInitCheck; MediaBuffer *mInputBuffer; + status_t initCheck(); AACDecoder(const AACDecoder &); AACDecoder &operator=(const AACDecoder &); }; diff --git a/native/android/Android.mk b/native/android/Android.mk index 376c64f..509a379 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -6,17 +6,18 @@ include $(CLEAR_VARS) # our source files # LOCAL_SRC_FILES:= \ - activity.cpp \ input.cpp \ looper.cpp \ + native_activity.cpp \ native_window.cpp LOCAL_SHARED_LIBRARIES := \ - libandroid_runtime \ libcutils \ libutils \ libbinder \ - libui + libui \ + libsurfaceflinger_client \ + libandroid_runtime LOCAL_C_INCLUDES += \ frameworks/base/native/include \ diff --git a/native/android/activity.cpp b/native/android/activity.cpp deleted file mode 100644 index e69de29..0000000 --- a/native/android/activity.cpp +++ /dev/null diff --git a/native/android/input.cpp b/native/android/input.cpp index 015a1ce..89d53e2 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -186,9 +186,9 @@ float AMotionEvent_getHistoricalSize(AInputEvent* motion_event, size_t pointer_i } void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - ALooper_callbackFunc callback, void* data) { + ALooper_callbackFunc* callback, void* data) { queue->setPollLoop(static_cast<android::PollLoop*>(looper)); - ALooper_setCallback(looper, queue->getConsumer().getChannel()->getReceivePipeFd(), + ALooper_addFd(looper, queue->getConsumer().getChannel()->getReceivePipeFd(), POLLIN, callback, data); } diff --git a/native/android/looper.cpp b/native/android/looper.cpp index 6e78bbd..1564c47 100644 --- a/native/android/looper.cpp +++ b/native/android/looper.cpp @@ -27,22 +27,41 @@ ALooper* ALooper_forThread() { return PollLoop::getForThread().get(); } -ALooper* ALooper_prepare() { +ALooper* ALooper_prepare(int32_t opts) { + bool allowFds = (opts&ALOOPER_PREPARE_ALLOW_NON_CALLBACKS) != 0; sp<PollLoop> loop = PollLoop::getForThread(); if (loop == NULL) { - loop = new PollLoop(); + loop = new PollLoop(allowFds); PollLoop::setForThread(loop); } + if (loop->getAllowNonCallbacks() != allowFds) { + LOGW("ALooper_prepare again with different ALOOPER_PREPARE_ALLOW_NON_CALLBACKS"); + } return loop.get(); } -int32_t ALooper_pollOnce(int timeoutMillis) { +int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData) { sp<PollLoop> loop = PollLoop::getForThread(); if (loop == NULL) { LOGW("ALooper_pollOnce: No looper for this thread!"); return -1; } - return loop->pollOnce(timeoutMillis) ? 1 : 0; + return loop->pollOnce(timeoutMillis, outEvents, outData); +} + +int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData) { + sp<PollLoop> loop = PollLoop::getForThread(); + if (loop == NULL) { + LOGW("ALooper_pollOnce: No looper for this thread!"); + return -1; + } + + int32_t result; + while ((result = loop->pollOnce(timeoutMillis, outEvents, outData)) == ALOOPER_POLL_CALLBACK) { + ; + } + + return result; } void ALooper_acquire(ALooper* looper) { @@ -53,11 +72,11 @@ void ALooper_release(ALooper* looper) { static_cast<PollLoop*>(looper)->decStrong((void*)ALooper_acquire); } -void ALooper_setCallback(ALooper* looper, int fd, int events, +void ALooper_addFd(ALooper* looper, int fd, int events, ALooper_callbackFunc* callback, void* data) { static_cast<PollLoop*>(looper)->setLooperCallback(fd, events, callback, data); } -int32_t ALooper_removeCallback(ALooper* looper, int fd) { +int32_t ALooper_removeFd(ALooper* looper, int fd) { return static_cast<PollLoop*>(looper)->removeCallback(fd) ? 1 : 0; } diff --git a/native/android/native_activity.cpp b/native/android/native_activity.cpp new file mode 100644 index 0000000..509cc33 --- /dev/null +++ b/native/android/native_activity.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + +#define LOG_TAG "native_activity" +#include <utils/Log.h> + +#include <android_runtime/android_app_NativeActivity.h> + +using namespace android; + +void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format) { + android_NativeActivity_setWindowFormat(activity, format); +} + +void ANativeActivity_setWindowFlags(ANativeActivity* activity, + uint32_t addFlags, uint32_t removeFlags) { + android_NativeActivity_setWindowFlags(activity, addFlags, addFlags|removeFlags); +} diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp index 448cbfc..bada078 100644 --- a/native/android/native_window.cpp +++ b/native/android/native_window.cpp @@ -17,10 +17,27 @@ #define LOG_TAG "Surface" #include <utils/Log.h> -#include <android/native_window.h> +#include <android/native_window_jni.h> #include <surfaceflinger/Surface.h> +#include <android_runtime/android_view_Surface.h> -using android::Surface; +using namespace android; + +ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) { + sp<ANativeWindow> win = android_Surface_getNativeWindow(env, surface); + if (win != NULL) { + win->incStrong((void*)ANativeWindow_acquire); + } + return win.get(); +} + +void ANativeWindow_acquire(ANativeWindow* window) { + window->incStrong((void*)ANativeWindow_acquire); +} + +void ANativeWindow_release(ANativeWindow* window) { + window->decStrong((void*)ANativeWindow_acquire); +} static int32_t getWindowProp(ANativeWindow* window, int what) { int value; @@ -41,7 +58,40 @@ int32_t ANativeWindow_getFormat(ANativeWindow* window) { } int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, - int32_t height, int32_t format) { - native_window_set_buffers_geometry(window, width, height, format); + int32_t height) { + native_window_set_buffers_geometry(window, width, height, 0); return 0; } + +int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, + ARect* inOutDirtyBounds) { + Region dirtyRegion; + Region* dirtyParam = NULL; + if (inOutDirtyBounds != NULL) { + dirtyRegion.set(*(Rect*)inOutDirtyBounds); + dirtyParam = &dirtyRegion; + } + + Surface::SurfaceInfo info; + status_t res = static_cast<Surface*>(window)->lock(&info, dirtyParam); + if (res != OK) { + return -1; + } + + outBuffer->width = (int32_t)info.w; + outBuffer->height = (int32_t)info.h; + outBuffer->stride = (int32_t)info.s; + outBuffer->format = (int32_t)info.format; + outBuffer->bits = info.bits; + + if (inOutDirtyBounds != NULL) { + *inOutDirtyBounds = dirtyRegion.getBounds(); + } + + return 0; +} + +int32_t ANativeWindow_unlockAndPost(ANativeWindow* window) { + status_t res = static_cast<Surface*>(window)->unlockAndPost(); + return res == android::OK ? 0 : -1; +} diff --git a/native/glue/threaded_app/Android.mk b/native/glue/threaded_app/Android.mk new file mode 100644 index 0000000..cfc9b2a --- /dev/null +++ b/native/glue/threaded_app/Android.mk @@ -0,0 +1,18 @@ +BASE_PATH := $(call my-dir) +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +# our source files +# +LOCAL_SRC_FILES:= \ + threaded_app.c + +LOCAL_C_INCLUDES += \ + frameworks/base/native/include \ + frameworks/base/core/jni/android \ + dalvik/libnativehelper/include/nativehelper + +LOCAL_MODULE:= libthreaded_app + +include $(BUILD_STATIC_LIBRARY) diff --git a/native/glue/threaded_app/threaded_app.c b/native/glue/threaded_app/threaded_app.c new file mode 100644 index 0000000..2411e93 --- /dev/null +++ b/native/glue/threaded_app/threaded_app.c @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + * + */ + +#include <jni.h> + +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <sys/resource.h> + +#include <android_glue/threaded_app.h> + +#include <android/log.h> + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "threaded_app", __VA_ARGS__)) + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + return cmd; + } else { + LOGW("No data on command pipe!"); + } + return -1; +} + +int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGI("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGI("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, NULL, (void*)LOOPER_ID_EVENT); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_WINDOW_CHANGED: + LOGI("APP_CMD_WINDOW_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_START: + case APP_CMD_RESUME: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGI("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_DESTROY: + LOGI("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } + + return android_app->destroyRequested ? 0 : 1; +} + +static void android_app_destroy(struct android_app* android_app) { + LOGI("android_app_destroy!"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, POLLIN, NULL, (void*)LOOPER_ID_MAIN); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGI("could not create pipe: %s", strerror(errno)); + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGI("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingWindow = window; + android_app_write_cmd(android_app, APP_CMD_WINDOW_CHANGED); + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGI("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGI("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGI("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + LOGI("SaveInstanceState: %p\n", activity); + return NULL; +} + +static void onPause(ANativeActivity* activity) { + LOGI("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGI("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onLowMemory(ANativeActivity* activity) { + LOGI("LowMemory: %p\n", activity); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +void ANativeActivity_onCreate(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + LOGI("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity); +} diff --git a/native/include/android/input.h b/native/include/android/input.h index 75be85a..014b6a3 100644 --- a/native/include/android/input.h +++ b/native/include/android/input.h @@ -534,10 +534,11 @@ struct AInputQueue; typedef struct AInputQueue AInputQueue; /* - * Add this input queue to a looper for processing. + * Add this input queue to a looper for processing. See + * ALooper_addFd() for information on the callback and data params. */ void AInputQueue_attachLooper(AInputQueue* queue, ALooper* looper, - ALooper_callbackFunc callback, void* data); + ALooper_callbackFunc* callback, void* data); /* * Remove the input queue from the looper it is currently attached to. diff --git a/native/include/android/looper.h b/native/include/android/looper.h index 90a8983..2917216 100644 --- a/native/include/android/looper.h +++ b/native/include/android/looper.h @@ -24,25 +24,151 @@ extern "C" { #endif +/** + * ALooper + * + * A looper is the state tracking an event loop for a thread. + * Loopers do not define event structures or other such things; rather + * they are a lower-level facility to attach one or more discrete objects + * listening for an event. An "event" here is simply data available on + * a file descriptor: each attached object has an associated file descriptor, + * and waiting for "events" means (internally) polling on all of these file + * descriptors until one or more of them have data available. + * + * A thread can have only one ALooper associated with it. + */ struct ALooper; typedef struct ALooper ALooper; +/** + * For callback-based event loops, this is the prototype of the function + * that is called. It is given the file descriptor it is associated with, + * a bitmask of the poll events that were triggered (typically POLLIN), and + * the data pointer that was originally supplied. + * + * Implementations should return 1 to continue receiving callbacks, or 0 + * to have this file descriptor and callback unregistered from the looper. + */ typedef int ALooper_callbackFunc(int fd, int events, void* data); +/** + * Return the ALooper associated with the calling thread, or NULL if + * there is not one. + */ ALooper* ALooper_forThread(); -ALooper* ALooper_prepare(); +enum { + /** + * Option for ALooper_prepare: this ALooper will accept calls to + * ALooper_addFd() that do not have a callback (that is provide NULL + * for the callback). In this case the caller of ALooper_pollOnce() + * or ALooper_pollAll() MUST check the return from these functions to + * discover when data is available on such fds and process it. + */ + ALOOPER_PREPARE_ALLOW_NON_CALLBACKS = 1<<0 +}; -int32_t ALooper_pollOnce(int timeoutMillis); +/** + * Prepare an ALooper associated with the calling thread, and return it. + * If the thread already has an ALooper, it is returned. Otherwise, a new + * one is created, associated with the thread, and returned. + * + * The opts may be ALOOPER_PREPARE_ALLOW_NON_CALLBACKS or 0. + */ +ALooper* ALooper_prepare(int32_t opts); + +enum { + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): one or + * more callbacks were executed. + */ + ALOOPER_POLL_CALLBACK = -1, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): the + * timeout expired. + */ + ALOOPER_POLL_TIMEOUT = -2, + + /** + * Result from ALooper_pollOnce() and ALooper_pollAll(): an error + * occurred. + */ + ALOOPER_POLL_ERROR = -3, +}; +/** + * Wait for events to be available, with optional timeout in milliseconds. + * Invokes callbacks for all file descriptors on which an event occurred. + * + * If the timeout is zero, returns immediately without blocking. + * If the timeout is negative, waits indefinitely until an event appears. + * + * Returns ALOOPER_POLL_CALLBACK if a callback was invoked. + * + * Returns ALOOPER_POLL_TIMEOUT if there was no data before the given + * timeout expired. + * + * Returns ALOPER_POLL_ERROR if an error occurred. + * + * Returns a value >= 0 containing a file descriptor if it has data + * and it has no callback function (requiring the caller here to handle it). + * In this (and only this) case outEvents and outData will contain the poll + * events and data associated with the fd. + * + * This method does not return until it has finished invoking the appropriate callbacks + * for all file descriptors that were signalled. + */ +int32_t ALooper_pollOnce(int timeoutMillis, int* outEvents, void** outData); + +/** + * Like ALooper_pollOnce(), but performs all pending callbacks until all + * data has been consumed or a file descriptor is available with no callback. + * This function will never return ALOOPER_POLL_CALLBACK. + */ +int32_t ALooper_pollAll(int timeoutMillis, int* outEvents, void** outData); + +/** + * Acquire a reference on the given ALooper object. This prevents the object + * from being deleted until the reference is removed. This is only needed + * to safely hand an ALooper from one thread to another. + */ void ALooper_acquire(ALooper* looper); +/** + * Remove a reference that was previously acquired with ALooper_acquire(). + */ void ALooper_release(ALooper* looper); -void ALooper_setCallback(ALooper* looper, int fd, int events, +/** + * Add a new file descriptor to be polled by the looper. If the same file + * descriptor was previously added, it is replaced. + * + * "fd" is the file descriptor to be added. + * "events" are the poll events to wake up on. Typically this is POLLIN. + * "callback" is the function to call when there is an event on the file + * descriptor. + * "id" is an identifier to associated with this file descriptor, or 0. + * "data" is a private data pointer to supply to the callback. + * + * There are two main uses of this function: + * + * (1) If "callback" is non-NULL, then + * this function will be called when there is data on the file descriptor. It + * should execute any events it has pending, appropriately reading from the + * file descriptor. + * + * (2) If "callback" is NULL, the fd will be returned by ALooper_pollOnce + * when it has data available, requiring the caller to take care of processing + * it. + */ +void ALooper_addFd(ALooper* looper, int fd, int events, ALooper_callbackFunc* callback, void* data); -int32_t ALooper_removeCallback(ALooper* looper, int fd); +/** + * Remove a previously added file descriptor from the looper. + */ +int32_t ALooper_removeFd(ALooper* looper, int fd); #ifdef __cplusplus }; diff --git a/native/include/android/native_activity.h b/native/include/android/native_activity.h index a31c5af..d0ff052 100644 --- a/native/include/android/native_activity.h +++ b/native/include/android/native_activity.h @@ -192,6 +192,11 @@ typedef void ANativeActivity_createFunc(ANativeActivity* activity, */ extern ANativeActivity_createFunc ANativeActivity_onCreate; +void ANativeActivity_setWindowFormat(ANativeActivity* activity, int32_t format); + +void ANativeActivity_setWindowFlags(ANativeActivity* activity, + uint32_t addFlags, uint32_t removeFlags); + #ifdef __cplusplus }; #endif diff --git a/native/include/android/native_window.h b/native/include/android/native_window.h index 678ba3d..7599d7e 100644 --- a/native/include/android/native_window.h +++ b/native/include/android/native_window.h @@ -14,10 +14,11 @@ * limitations under the License. */ - #ifndef ANDROID_NATIVE_WINDOW_H #define ANDROID_NATIVE_WINDOW_H +#include <android/rect.h> + #ifdef __cplusplus extern "C" { #endif @@ -34,6 +35,27 @@ enum { struct ANativeWindow; typedef struct ANativeWindow ANativeWindow; +typedef struct ANativeWindow_Buffer { + int32_t width; + int32_t height; + int32_t stride; + int32_t format; + void* bits; + + uint32_t reserved[6]; +} ANativeWindow_Buffer; + +/** + * Acquire a reference on the given ANativeWindow object. This prevents the object + * from being deleted until the reference is removed. + */ +void ANativeWindow_acquire(ANativeWindow* window); + +/** + * Remove a reference that was previously acquired with ANativeWindow_acquire(). + */ +void ANativeWindow_release(ANativeWindow* window); + /* * Return the current width in pixels of the window surface. Returns a * negative value on error. @@ -60,13 +82,22 @@ int32_t ANativeWindow_getFormat(ANativeWindow* window); * window's physical size, then it buffer will be scaled to match that size * when compositing it to the screen. * - * The format may be one of the window format constants above. - * - * For all of these parameters, if 0 is supplied than the window's base + * For all of these parameters, if 0 is supplied then the window's base * value will come back in force. */ -int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, - int32_t height, int32_t format); +int32_t ANativeWindow_setBuffersGeometry(ANativeWindow* window, int32_t width, int32_t height); + +/** + * Lock the window's next drawing surface for writing. + */ +int32_t ANativeWindow_lock(ANativeWindow* window, ANativeWindow_Buffer* outBuffer, + ARect* inOutDirtyBounds); + +/** + * Unlock the window's drawing surface after previously locking it, + * posting the new buffer to the display. + */ +int32_t ANativeWindow_unlockAndPost(ANativeWindow* window); #ifdef __cplusplus }; diff --git a/native/include/android/native_window_jni.h b/native/include/android/native_window_jni.h new file mode 100644 index 0000000..b9e72ef --- /dev/null +++ b/native/include/android/native_window_jni.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + +#ifndef ANDROID_NATIVE_WINDOW_JNI_H +#define ANDROID_NATIVE_WINDOW_JNI_H + +#include <android/native_window.h> + +#include <jni.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Return the ANativeWindow associated with a Java Surface object, + * for interacting with it through native code. This acquires a reference + * on the ANativeWindow that is returned; be sure to use ANativeWindow_release() + * when done with it so that it doesn't leak. + */ +ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface); + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_NATIVE_WINDOW_H diff --git a/native/include/android/rect.h b/native/include/android/rect.h new file mode 100644 index 0000000..3e81f53 --- /dev/null +++ b/native/include/android/rect.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + + +#ifndef ANDROID_RECT_H +#define ANDROID_RECT_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ARect { + int32_t left; + int32_t top; + int32_t right; + int32_t bottom; +} ARect; + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_RECT_H diff --git a/native/include/android/window.h b/native/include/android/window.h new file mode 100644 index 0000000..2ab192b --- /dev/null +++ b/native/include/android/window.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + */ + + +#ifndef ANDROID_WINDOW_H +#define ANDROID_WINDOW_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Window flags, as per the Java API at android.view.WindowManager.LayoutParams. + */ +enum { + AWINDOW_FLAG_ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, + AWINDOW_FLAG_DIM_BEHIND = 0x00000002, + AWINDOW_FLAG_BLUR_BEHIND = 0x00000004, + AWINDOW_FLAG_NOT_FOCUSABLE = 0x00000008, + AWINDOW_FLAG_NOT_TOUCHABLE = 0x00000010, + AWINDOW_FLAG_NOT_TOUCH_MODAL = 0x00000020, + AWINDOW_FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040, + AWINDOW_FLAG_KEEP_SCREEN_ON = 0x00000080, + AWINDOW_FLAG_LAYOUT_IN_SCREEN = 0x00000100, + AWINDOW_FLAG_LAYOUT_NO_LIMITS = 0x00000200, + AWINDOW_FLAG_FULLSCREEN = 0x00000400, + AWINDOW_FLAG_FORCE_NOT_FULLSCREEN = 0x00000800, + AWINDOW_FLAG_DITHER = 0x00001000, + AWINDOW_FLAG_SECURE = 0x00002000, + AWINDOW_FLAG_SCALED = 0x00004000, + AWINDOW_FLAG_IGNORE_CHEEK_PRESSES = 0x00008000, + AWINDOW_FLAG_LAYOUT_INSET_DECOR = 0x00010000, + AWINDOW_FLAG_ALT_FOCUSABLE_IM = 0x00020000, + AWINDOW_FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000, + AWINDOW_FLAG_SHOW_WHEN_LOCKED = 0x00080000, + AWINDOW_FLAG_SHOW_WALLPAPER = 0x00100000, + AWINDOW_FLAG_TURN_SCREEN_ON = 0x00200000, + AWINDOW_FLAG_DISMISS_KEYGUARD = 0x00400000, +}; + +#ifdef __cplusplus +}; +#endif + +#endif // ANDROID_WINDOW_H diff --git a/native/include/android_glue/threaded_app.h b/native/include/android_glue/threaded_app.h new file mode 100644 index 0000000..adfdbea --- /dev/null +++ b/native/include/android_glue/threaded_app.h @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * 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. + * + */ + +#include <poll.h> +#include <pthread.h> +#include <sched.h> + +#include <android/native_activity.h> +#include <android/looper.h> + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + int running; + int destroyed; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_EVENT = 2 +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: the ANativeWindow has changed. Upon processing + * this command, android_app->window will be updated to the new window surface + * (or NULL). + */ + APP_CMD_WINDOW_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * default processing of the given command. + * + * Important: returns 0 if the app should exit. You must ALWAYS check for + * a zero return and, if found, exit your android_main() function. + */ +int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java index f8abc5a..b9e915a4 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java +++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java @@ -244,27 +244,12 @@ public class StorageNotification extends StorageEventListener { intent.setClass(mContext, com.android.systemui.usb.UsbStorageActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - final boolean adbOn = 1 == Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.ADB_ENABLED, - 0); - PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); setUsbStorageNotification( com.android.internal.R.string.usb_storage_notification_title, com.android.internal.R.string.usb_storage_notification_message, com.android.internal.R.drawable.stat_sys_data_usb, false, true, pi); - - if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { - // We assume that developers don't want to enable UMS every - // time they attach a device to a USB host. The average user, - // however, is looking to charge the phone (in which case this - // is harmless) or transfer files (in which case this coaches - // the user about how to complete that task and saves several - // steps). - mContext.startActivity(intent); - } } else { setUsbStorageNotification(0, 0, 0, false, false, null); } @@ -313,6 +298,23 @@ public class StorageNotification extends StorageEventListener { } mUsbStorageNotification.setLatestEventInfo(mContext, title, message, pi); + final boolean adbOn = 1 == Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.ADB_ENABLED, + 0); + + if (POP_UMS_ACTIVITY_ON_CONNECT && !adbOn) { + // Pop up a full-screen alert to coach the user through enabling UMS. The average + // user has attached the device to USB either to charge the phone (in which case + // this is harmless) or transfer files, and in the latter case this alert saves + // several steps (as well as subtly indicates that you shouldn't mix UMS with other + // activities on the device). + // + // If ADB is enabled, however, we suppress this dialog (under the assumption that a + // developer (a) knows how to enable UMS, and (b) is probably using USB to install + // builds or use adb commands. + mUsbStorageNotification.fullScreenIntent = pi; + } } final int notificationId = mUsbStorageNotification.icon; diff --git a/test-runner/src/android/test/InstrumentationTestRunner.java b/test-runner/src/android/test/InstrumentationTestRunner.java index 63d50c7..70d1643 100644 --- a/test-runner/src/android/test/InstrumentationTestRunner.java +++ b/test-runner/src/android/test/InstrumentationTestRunner.java @@ -496,9 +496,18 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu return null; } + /** + * Initialize the current thread as a looper. + * <p/> + * Exposed for unit testing. + */ + void prepareLooper() { + Looper.prepare(); + } + @Override public void onStart() { - Looper.prepare(); + prepareLooper(); if (mJustCount) { mResults.putString(Instrumentation.REPORT_KEY_IDENTIFIER, REPORT_VALUE_ID); @@ -521,6 +530,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu long runTime = System.currentTimeMillis() - startTime; resultPrinter.print(mTestRunner.getTestResult(), runTime); + } catch (Throwable t) { + // catch all exceptions so a more verbose error message can be outputted + writer.println(String.format("Test run aborted due to unexpected exception: %s", + t.getMessage())); + t.printStackTrace(writer); } finally { mResults.putString(Instrumentation.REPORT_KEY_STREAMRESULT, String.format("\nTest results for %s=%s", @@ -762,9 +776,11 @@ public class InstrumentationTestRunner extends Instrumentation implements TestSu TimedTest.class).includeDetailedStats(); } } catch (SecurityException e) { - throw new IllegalStateException(e); + // ignore - the test with given name cannot be accessed. Will be handled during + // test execution } catch (NoSuchMethodException e) { - throw new IllegalStateException(e); + // ignore- the test with given name does not exist. Will be handled during test + // execution } if (mIsTimedTest && mIncludeDetailedStats) { diff --git a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java index 6db72ad..d98b217 100644 --- a/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java +++ b/test-runner/tests/src/android/test/InstrumentationTestRunnerTest.java @@ -16,6 +16,7 @@ package android.test; +import android.app.Instrumentation; import android.content.Context; import android.os.Bundle; import android.test.mock.MockContext; @@ -89,6 +90,42 @@ public class InstrumentationTestRunnerTest extends TestCase { } + /** + * Test that runtime exceptions during runTest are handled gracefully + */ + public void testUnhandledException() throws Exception { + StubAndroidTestRunner stubAndroidTestRunner = new StubAndroidTestRunner() { + @Override + public void runTest() { + throw new RuntimeException(); + } + }; + StubInstrumentationTestRunner instrumentationTestRunner = new StubInstrumentationTestRunner( + new StubContext("com.google.foo.tests"), + new StubContext(mTargetContextPackageName), stubAndroidTestRunner); + instrumentationTestRunner.onCreate(new Bundle()); + instrumentationTestRunner.onStart(); + assertTrue("Instrumentation did not finish", instrumentationTestRunner.isFinished()); + // ensure a meaningful error message placed in results + String resultsData = instrumentationTestRunner.mResults.getString( + Instrumentation.REPORT_KEY_STREAMRESULT); + assertTrue("Instrumentation results is missing RuntimeException", + resultsData.contains("RuntimeException")); + } + + /** + * Test that specifying a method which does not exist is handled gracefully + */ + public void testBadMethodArgument() throws Exception { + String testClassName = PlaceHolderTest.class.getName(); + String invalidMethodName = "testNoExist"; + String classAndMethod = testClassName + "#" + invalidMethodName; + mInstrumentationTestRunner.onCreate(createBundle( + InstrumentationTestRunner.ARGUMENT_TEST_CLASS, classAndMethod)); + assertTestRunnerCalledWithExpectedParameters(testClassName, + invalidMethodName); + } + public void testDelayParameter() throws Exception { int delayMsec = 1000; Bundle args = new Bundle(); @@ -170,6 +207,7 @@ public class InstrumentationTestRunnerTest extends TestCase { private TestSuite mTestSuite; private TestSuite mDefaultTestSuite; private String mPackageNameForDefaultTests; + private Bundle mResults; public StubInstrumentationTestRunner(Context context, Context targetContext, AndroidTestRunner androidTestRunner) { @@ -200,6 +238,7 @@ public class InstrumentationTestRunnerTest extends TestCase { public void finish(int resultCode, Bundle results) { mFinished = true; + mResults = results; } public boolean isStarted() { @@ -221,6 +260,11 @@ public class InstrumentationTestRunnerTest extends TestCase { public String getPackageNameForDefaultTests() { return mPackageNameForDefaultTests; } + + @Override + void prepareLooper() { + // ignore + } } private static class StubContext extends MockContext { |