diff options
Diffstat (limited to 'core')
-rw-r--r-- | core/java/android/app/ContextImpl.java | 14 | ||||
-rw-r--r-- | core/java/android/app/VoiceInteractor.java | 79 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 35 | ||||
-rw-r--r-- | core/java/android/content/ContextWrapper.java | 5 | ||||
-rw-r--r-- | core/java/android/os/Environment.java | 4 | ||||
-rw-r--r-- | core/java/android/service/voice/VoiceInteractionSession.java | 242 | ||||
-rw-r--r-- | core/java/android/widget/Toolbar.java | 22 | ||||
-rw-r--r-- | core/java/com/android/internal/app/IVoiceInteractor.aidl | 4 | ||||
-rw-r--r-- | core/java/com/android/internal/app/IVoiceInteractorCallback.aidl | 1 | ||||
-rw-r--r-- | core/java/com/android/internal/widget/ToolbarWidgetWrapper.java | 10 | ||||
-rw-r--r-- | core/jni/android_media_AudioFormat.h | 54 | ||||
-rw-r--r-- | core/jni/android_media_AudioSystem.cpp | 1115 | ||||
-rw-r--r-- | core/res/res/anim/input_method_exit.xml | 2 | ||||
-rw-r--r-- | core/res/res/values/config.xml | 1 |
14 files changed, 1557 insertions, 31 deletions
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ff8688d..8ffa6fe 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -249,6 +249,8 @@ class ContextImpl extends Context { private File[] mExternalFilesDirs; @GuardedBy("mSync") private File[] mExternalCacheDirs; + @GuardedBy("mSync") + private File[] mExternalMediaDirs; private static final String[] EMPTY_FILE_LIST = {}; @@ -1032,6 +1034,18 @@ class ContextImpl extends Context { } @Override + public File[] getExternalMediaDirs() { + synchronized (mSync) { + if (mExternalMediaDirs == null) { + mExternalMediaDirs = Environment.buildExternalStorageAppMediaDirs(getPackageName()); + } + + // Create dirs if needed + return ensureDirsExistOrFilter(mExternalMediaDirs); + } + } + + @Override public File getFileStreamPath(String name) { return makeFilename(getFilesDir(), name); } diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index fe85ef4..f332c9d 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -33,7 +33,26 @@ import com.android.internal.os.SomeArgs; import java.util.ArrayList; /** - * Interface for an {@link Activity} to interact with the user through voice. + * Interface for an {@link Activity} to interact with the user through voice. Use + * {@link android.app.Activity#getVoiceInteractor() Activity.getVoiceInteractor} + * to retrieve the interface, if the activity is currently involved in a voice interaction. + * + * <p>The voice interactor revolves around submitting voice interaction requests to the + * back-end voice interaction service that is working with the user. These requests are + * submitted with {@link #submitRequest}, providing a new instance of a + * {@link Request} subclass describing the type of operation to perform -- currently the + * possible requests are {@link ConfirmationRequest} and {@link CommandRequest}. + * + * <p>Once a request is submitted, the voice system will process it and evetually deliver + * the result to the request object. The application can cancel a pending request at any + * time. + * + * <p>The VoiceInteractor is integrated with Activity's state saving mechanism, so that + * if an activity is being restarted with retained state, it will retain the current + * VoiceInteractor and any outstanding requests. Because of this, you should always use + * {@link Request#getActivity() Request.getActivity} to get back to the activity of a + * request, rather than holding on to the actvitity instance yourself, either explicitly + * or implicitly through a non-static inner class. */ public class VoiceInteractor { static final String TAG = "VoiceInteractor"; @@ -62,6 +81,16 @@ public class VoiceInteractor { request.clear(); } break; + case MSG_ABORT_VOICE_RESULT: + request = pullRequest((IVoiceInteractorRequest)args.arg1, true); + if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + + ((IVoiceInteractorRequest)args.arg1).asBinder() + "/" + request + + " result=" + args.arg1); + if (request != null) { + ((AbortVoiceRequest)request).onAbortResult((Bundle) args.arg2); + request.clear(); + } + break; case MSG_COMMAND_RESULT: request = pullRequest((IVoiceInteractorRequest)args.arg1, msg.arg1 != 0); if (DEBUG) Log.d(TAG, "onCommandResult: req=" @@ -96,6 +125,12 @@ public class VoiceInteractor { } @Override + public void deliverAbortVoiceResult(IVoiceInteractorRequest request, Bundle result) { + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOO( + MSG_ABORT_VOICE_RESULT, request, result)); + } + + @Override public void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, Bundle result) { mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO( @@ -112,8 +147,9 @@ public class VoiceInteractor { final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>(); static final int MSG_CONFIRMATION_RESULT = 1; - static final int MSG_COMMAND_RESULT = 2; - static final int MSG_CANCEL_RESULT = 3; + static final int MSG_ABORT_VOICE_RESULT = 2; + static final int MSG_COMMAND_RESULT = 3; + static final int MSG_CANCEL_RESULT = 4; public static abstract class Request { IVoiceInteractorRequest mRequestInterface; @@ -188,9 +224,42 @@ public class VoiceInteractor { IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, IVoiceInteractorCallback callback) throws RemoteException { - return interactor.startConfirmation(packageName, callback, mPrompt.toString(), mExtras); + return interactor.startConfirmation(packageName, callback, mPrompt, mExtras); } - } + } + + public static class AbortVoiceRequest extends Request { + final CharSequence mMessage; + final Bundle mExtras; + + /** + * Reports that the current interaction can not be complete with voice, so the + * application will need to switch to a traditional input UI. Applications should + * only use this when they need to completely bail out of the voice interaction + * and switch to a traditional UI. When the resonsponse comes back, the voice + * system has handled the request and is ready to switch; at that point the application + * can start a new non-voice activity. Be sure when starting the new activity + * to use {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK + * Intent.FLAG_ACTIVITY_NEW_TASK} to keep the new activity out of the current voice + * interaction task. + * + * @param message Optional message to tell user about not being able to complete + * the interaction with voice. + * @param extras Additional optional information. + */ + public AbortVoiceRequest(CharSequence message, Bundle extras) { + mMessage = message; + mExtras = extras; + } + + public void onAbortResult(Bundle result) { + } + + IVoiceInteractorRequest submit(IVoiceInteractor interactor, String packageName, + IVoiceInteractorCallback callback) throws RemoteException { + return interactor.startAbortVoice(packageName, callback, mMessage, mExtras); + } + } public static class CommandRequest extends Request { final String mCommand; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2ff85c6..d3a979c 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -40,6 +40,7 @@ import android.os.Looper; import android.os.StatFs; import android.os.UserHandle; import android.os.UserManager; +import android.provider.MediaStore; import android.util.AttributeSet; import android.view.DisplayAdjustments; import android.view.Display; @@ -929,6 +930,40 @@ public abstract class Context { public abstract File[] getExternalCacheDirs(); /** + * Returns absolute paths to application-specific directories on all + * external storage devices where the application can place media files. + * These files are scanned and made available to other apps through + * {@link MediaStore}. + * <p> + * This is like {@link #getExternalFilesDirs} in that these files will be + * deleted when the application is uninstalled, however there are some + * important differences: + * <ul> + * <li>External files are not always available: they will disappear if the + * user mounts the external storage on a computer or removes it. + * <li>There is no security enforced with these files. + * </ul> + * <p> + * External storage devices returned here are considered a permanent part of + * the device, including both emulated external storage and physical media + * slots, such as SD cards in a battery compartment. The returned paths do + * not include transient devices, such as USB flash drives. + * <p> + * An application may store data on any or all of the returned devices. For + * example, an app may choose to store large files on the device with the + * most available space, as measured by {@link StatFs}. + * <p> + * No permissions are required to read or write to the returned paths; they + * are always accessible to the calling app. Write access outside of these + * paths on secondary external storage devices is not available. + * <p> + * Returned paths may be {@code null} if a storage device is unavailable. + * + * @see Environment#getExternalStorageState(File) + */ + public abstract File[] getExternalMediaDirs(); + + /** * Returns an array of strings naming the private files associated with * this Context's application package. * diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index c66355b..dbf9122 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -237,6 +237,11 @@ public class ContextWrapper extends Context { } @Override + public File[] getExternalMediaDirs() { + return mBase.getExternalMediaDirs(); + } + + @Override public File getDir(String name, int mode) { return mBase.getDir(name, mode); } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index e98a26b..e84b695 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -191,6 +191,10 @@ public class Environment { return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_MEDIA, packageName); } + public File[] buildExternalStorageAppMediaDirsForVold(String packageName) { + return buildPaths(mExternalDirsForVold, DIR_ANDROID, DIR_MEDIA, packageName); + } + public File[] buildExternalStorageAppObbDirs(String packageName) { return buildPaths(mExternalDirsForApp, DIR_ANDROID, DIR_OBB, packageName); } diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 1e29f8e..2e9077a 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -47,9 +47,22 @@ import com.android.internal.app.IVoiceInteractorRequest; import com.android.internal.os.HandlerCaller; import com.android.internal.os.SomeArgs; +import java.lang.ref.WeakReference; + import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +/** + * An active voice interaction session, providing a facility for the implementation + * to interact with the user in the voice interaction layer. This interface is no shown + * by default, but you can request that it be shown with {@link #showWindow()}, which + * will result in a later call to {@link #onCreateContentView()} in which the UI can be + * built + * + * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish} + * when done. It can also initiate voice interactions with applications by calling + * {@link #startVoiceActivity}</p>. + */ public abstract class VoiceInteractionSession implements KeyEvent.Callback { static final String TAG = "VoiceInteractionSession"; static final boolean DEBUG = true; @@ -80,11 +93,14 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final Insets mTmpInsets = new Insets(); final int[] mTmpLocation = new int[2]; + final WeakReference<VoiceInteractionSession> mWeakRef + = new WeakReference<VoiceInteractionSession>(this); + final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() { @Override public IVoiceInteractorRequest startConfirmation(String callingPackage, - IVoiceInteractorCallback callback, String prompt, Bundle extras) { - Request request = findRequest(callback, true); + IVoiceInteractorCallback callback, CharSequence prompt, Bundle extras) { + Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_CONFIRMATION, new Caller(callingPackage, Binder.getCallingUid()), request, prompt, extras)); @@ -92,9 +108,19 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } @Override + public IVoiceInteractorRequest startAbortVoice(String callingPackage, + IVoiceInteractorCallback callback, CharSequence message, Bundle extras) { + Request request = newRequest(callback); + mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_ABORT_VOICE, + new Caller(callingPackage, Binder.getCallingUid()), request, + message, extras)); + return request.mInterface; + } + + @Override public IVoiceInteractorRequest startCommand(String callingPackage, IVoiceInteractorCallback callback, String command, Bundle extras) { - Request request = findRequest(callback, true); + Request request = newRequest(callback); mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_START_COMMAND, new Caller(callingPackage, Binder.getCallingUid()), request, command, extras)); @@ -143,29 +169,60 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() { @Override public void cancel() throws RemoteException { - mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); + VoiceInteractionSession session = mSession.get(); + if (session != null) { + session.mHandlerCaller.sendMessage( + session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this)); + } } }; final IVoiceInteractorCallback mCallback; - final HandlerCaller mHandlerCaller; - Request(IVoiceInteractorCallback callback, HandlerCaller handlerCaller) { + final WeakReference<VoiceInteractionSession> mSession; + + Request(IVoiceInteractorCallback callback, VoiceInteractionSession session) { mCallback = callback; - mHandlerCaller = handlerCaller; + mSession = session.mWeakRef; + } + + void finishRequest() { + VoiceInteractionSession session = mSession.get(); + if (session == null) { + throw new IllegalStateException("VoiceInteractionSession has been destroyed"); + } + Request req = session.removeRequest(mInterface.asBinder()); + if (req == null) { + throw new IllegalStateException("Request not active: " + this); + } else if (req != this) { + throw new IllegalStateException("Current active request " + req + + " not same as calling request " + this); + } } public void sendConfirmResult(boolean confirmed, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface + " confirmed=" + confirmed + " result=" + result); + finishRequest(); mCallback.deliverConfirmationResult(mInterface, confirmed, result); } catch (RemoteException e) { } } + public void sendAbortVoiceResult(Bundle result) { + try { + if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface + + " result=" + result); + finishRequest(); + mCallback.deliverAbortVoiceResult(mInterface, result); + } catch (RemoteException e) { + } + } + public void sendCommandResult(boolean complete, Bundle result) { try { if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface + " result=" + result); + finishRequest(); mCallback.deliverCommandResult(mInterface, complete, result); } catch (RemoteException e) { } @@ -174,6 +231,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { public void sendCancelResult() { try { if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface); + finishRequest(); mCallback.deliverCancel(mInterface); } catch (RemoteException e) { } @@ -191,9 +249,10 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } static final int MSG_START_CONFIRMATION = 1; - static final int MSG_START_COMMAND = 2; - static final int MSG_SUPPORTS_COMMANDS = 3; - static final int MSG_CANCEL = 4; + static final int MSG_START_ABORT_VOICE = 2; + static final int MSG_START_COMMAND = 3; + static final int MSG_SUPPORTS_COMMANDS = 4; + static final int MSG_CANCEL = 5; static final int MSG_TASK_STARTED = 100; static final int MSG_TASK_FINISHED = 101; @@ -209,9 +268,16 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { args = (SomeArgs)msg.obj; if (DEBUG) Log.d(TAG, "onConfirm: req=" + ((Request) args.arg2).mInterface + " prompt=" + args.arg3 + " extras=" + args.arg4); - onConfirm((Caller)args.arg1, (Request)args.arg2, (String)args.arg3, + onConfirm((Caller)args.arg1, (Request)args.arg2, (CharSequence)args.arg3, (Bundle)args.arg4); break; + case MSG_START_ABORT_VOICE: + args = (SomeArgs)msg.obj; + if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + ((Request) args.arg2).mInterface + + " message=" + args.arg3 + " extras=" + args.arg4); + onAbortVoice((Caller) args.arg1, (Request) args.arg2, (CharSequence) args.arg3, + (Bundle) args.arg4); + break; case MSG_START_COMMAND: args = (SomeArgs)msg.obj; if (DEBUG) Log.d(TAG, "onCommand: req=" + ((Request) args.arg2).mInterface @@ -329,18 +395,20 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mCallbacks, true); } - Request findRequest(IVoiceInteractorCallback callback, boolean newRequest) { + Request newRequest(IVoiceInteractorCallback callback) { + synchronized (this) { + Request req = new Request(callback, this); + mActiveRequests.put(req.mInterface.asBinder(), req); + return req; + } + } + + Request removeRequest(IBinder reqInterface) { synchronized (this) { - Request req = mActiveRequests.get(callback.asBinder()); + Request req = mActiveRequests.get(reqInterface); if (req != null) { - if (newRequest) { - throw new IllegalArgumentException("Given request callback " + callback - + " is already active"); - } - return req; + mActiveRequests.remove(req); } - req = new Request(callback, mHandlerCaller); - mActiveRequests.put(callback.asBinder(), req); return req; } } @@ -425,6 +493,27 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mTheme = theme; } + /** + * Ask that a new activity be started for voice interaction. This will create a + * new dedicated task in the activity manager for this voice interaction session; + * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK} + * will be set for you to make it a new task. + * + * <p>The newly started activity will be displayed to the user in a special way, as + * a layer under the voice interaction UI.</p> + * + * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor} + * through which it can perform voice interactions through your session. These requests + * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands}, + * {@link #onConfirm}, {@link #onCommand}, and {@link #onCancel}. + * + * <p>You will receive a call to {@link #onTaskStarted} when the task starts up + * and {@link #onTaskFinished} when the last activity has finished. + * + * @param intent The Intent to start this voice interaction. The given Intent will + * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since + * this is part of a voice interaction. + */ public void startVoiceActivity(Intent intent) { if (mToken == null) { throw new IllegalStateException("Can't call before onCreate()"); @@ -439,14 +528,23 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } + /** + * Convenience for inflating views. + */ public LayoutInflater getLayoutInflater() { return mInflater; } + /** + * Retrieve the window being used to show the session's UI. + */ public Dialog getWindow() { return mWindow; } + /** + * Finish the session. + */ public void finish() { if (mToken == null) { throw new IllegalStateException("Can't call before onCreate()"); @@ -458,6 +556,12 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } } + /** + * Initiatize a new session. + * + * @param args The arguments that were supplied to + * {@link VoiceInteractionService#startSession VoiceInteractionService.startSession}. + */ public void onCreate(Bundle args) { mTheme = mTheme != 0 ? mTheme : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession; @@ -472,9 +576,15 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { mWindow.setToken(mToken); } + /** + * Last callback to the session as it is being finished. + */ public void onDestroy() { } + /** + * Hook in which to create the session's UI. + */ public View onCreateContentView() { return null; } @@ -507,6 +617,11 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { finish(); } + /** + * Sessions automatically watch for requests that all system UI be closed (such as when + * the user presses HOME), which will appear here. The default implementation always + * calls {@link #finish}. + */ public void onCloseSystemDialogs() { finish(); } @@ -530,15 +645,98 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { outInsets.touchableRegion.setEmpty(); } + /** + * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)} + * has actually started. + * + * @param intent The original {@link Intent} supplied to + * {@link #startVoiceActivity(android.content.Intent)}. + * @param taskId Unique ID of the now running task. + */ public void onTaskStarted(Intent intent, int taskId) { } + /** + * Called when the last activity of a task initiated by + * {@link #startVoiceActivity(android.content.Intent)} has finished. The default + * implementation calls {@link #finish()} on the assumption that this represents + * the completion of a voice action. You can override the implementation if you would + * like a different behavior. + * + * @param intent The original {@link Intent} supplied to + * {@link #startVoiceActivity(android.content.Intent)}. + * @param taskId Unique ID of the finished task. + */ public void onTaskFinished(Intent intent, int taskId) { finish(); } - public abstract boolean[] onGetSupportedCommands(Caller caller, String[] commands); - public abstract void onConfirm(Caller caller, Request request, String prompt, Bundle extras); + /** + * Request to query for what extended commands the session supports. + * + * @param caller Who is making the request. + * @param commands An array of commands that are being queried. + * @return Return an array of booleans indicating which of each entry in the + * command array is supported. A true entry in the array indicates the command + * is supported; false indicates it is not. The default implementation returns + * an array of all false entries. + */ + public boolean[] onGetSupportedCommands(Caller caller, String[] commands) { + return new boolean[commands.length]; + } + + /** + * Request to confirm with the user before proceeding with an unrecoverable operation, + * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest + * VoiceInteractor.ConfirmationRequest}. + * + * @param caller Who is making the request. + * @param request The active request. + * @param prompt The prompt informing the user of what will happen, as per + * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. + * @param extras Any additional information, as per + * {@link android.app.VoiceInteractor.ConfirmationRequest VoiceInteractor.ConfirmationRequest}. + */ + public abstract void onConfirm(Caller caller, Request request, CharSequence prompt, + Bundle extras); + + /** + * Request to abort the voice interaction session because the voice activity can not + * complete its interaction using voice. Corresponds to + * {@link android.app.VoiceInteractor.AbortVoiceRequest + * VoiceInteractor.AbortVoiceRequest}. The default implementation just sends an empty + * confirmation back to allow the activity to exit. + * + * @param caller Who is making the request. + * @param request The active request. + * @param message The message informing the user of the problem, as per + * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. + * @param extras Any additional information, as per + * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}. + */ + public void onAbortVoice(Caller caller, Request request, CharSequence message, Bundle extras) { + request.sendAbortVoiceResult(null); + } + + /** + * Process an arbitrary extended command from the caller, + * corresponding to a {@link android.app.VoiceInteractor.CommandRequest + * VoiceInteractor.CommandRequest}. + * + * @param caller Who is making the request. + * @param request The active request. + * @param command The command that is being executed, as per + * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. + * @param extras Any additional information, as per + * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}. + */ public abstract void onCommand(Caller caller, Request request, String command, Bundle extras); + + /** + * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request} + * that was previously delivered to {@link #onConfirm} or {@link #onCommand}. + * + * @param request The request that is being canceled. + */ public abstract void onCancel(Request request); } diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index 5033bee..f35ca27 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -557,6 +557,28 @@ public class Toolbar extends ViewGroup { } /** + * Sets the text color, size, style, hint color, and highlight color + * from the specified TextAppearance resource. + */ + public void setTitleTextAppearance(Context context, int resId) { + mTitleTextAppearance = resId; + if (mTitleTextView != null) { + mTitleTextView.setTextAppearance(context, resId); + } + } + + /** + * Sets the text color, size, style, hint color, and highlight color + * from the specified TextAppearance resource. + */ + public void setSubtitleTextAppearance(Context context, int resId) { + mSubtitleTextAppearance = resId; + if (mSubtitleTextView != null) { + mSubtitleTextView.setTextAppearance(context, resId); + } + } + + /** * Set the icon to use for the toolbar's navigation button. * * <p>The navigation button appears at the start of the toolbar if present. Setting an icon diff --git a/core/java/com/android/internal/app/IVoiceInteractor.aidl b/core/java/com/android/internal/app/IVoiceInteractor.aidl index 737906a..2900595 100644 --- a/core/java/com/android/internal/app/IVoiceInteractor.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractor.aidl @@ -26,7 +26,9 @@ import com.android.internal.app.IVoiceInteractorRequest; */ interface IVoiceInteractor { IVoiceInteractorRequest startConfirmation(String callingPackage, - IVoiceInteractorCallback callback, String prompt, in Bundle extras); + IVoiceInteractorCallback callback, CharSequence prompt, in Bundle extras); + IVoiceInteractorRequest startAbortVoice(String callingPackage, + IVoiceInteractorCallback callback, CharSequence message, in Bundle extras); IVoiceInteractorRequest startCommand(String callingPackage, IVoiceInteractorCallback callback, String command, in Bundle extras); boolean[] supportsCommands(String callingPackage, in String[] commands); diff --git a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl index c6f93e1..8dbf9d4 100644 --- a/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractorCallback.aidl @@ -26,6 +26,7 @@ import com.android.internal.app.IVoiceInteractorRequest; oneway interface IVoiceInteractorCallback { void deliverConfirmationResult(IVoiceInteractorRequest request, boolean confirmed, in Bundle result); + void deliverAbortVoiceResult(IVoiceInteractorRequest request, in Bundle result); void deliverCommandResult(IVoiceInteractorRequest request, boolean complete, in Bundle result); void deliverCancel(IVoiceInteractorRequest request); } diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java index f90aaea..b8dc307 100644 --- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java +++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java @@ -132,6 +132,16 @@ public class ToolbarWidgetWrapper implements DecorToolbar { mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd); } + final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); + if (titleTextStyle != 0) { + mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle); + } + + final int subtitleTextStyle = a.getResourceId(R.styleable.ActionBar_subtitleTextStyle, 0); + if (subtitleTextStyle != 0) { + mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle); + } + a.recycle(); mToolbar.setNavigationOnClickListener(new View.OnClickListener() { diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index fedb1b2..a2b1ed9 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -23,6 +23,11 @@ #define ENCODING_PCM_16BIT 2 #define ENCODING_PCM_8BIT 3 #define ENCODING_PCM_FLOAT 4 +#define ENCODING_INVALID 0 +#define ENCODING_DEFAULT 1 + +#define CHANNEL_INVALID 0 +#define CHANNEL_OUT_DEFAULT 1 static inline audio_format_t audioFormatToNative(int audioFormat) { @@ -33,9 +38,58 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_PCM_8_BIT; case ENCODING_PCM_FLOAT: return AUDIO_FORMAT_PCM_FLOAT; + case ENCODING_DEFAULT: + return AUDIO_FORMAT_DEFAULT; default: return AUDIO_FORMAT_INVALID; } } +static inline int audioFormatFromNative(audio_format_t nativeFormat) +{ + switch (nativeFormat) { + case AUDIO_FORMAT_PCM_16_BIT: + return ENCODING_PCM_16BIT; + case AUDIO_FORMAT_PCM_8_BIT: + return ENCODING_PCM_8BIT; + case AUDIO_FORMAT_PCM_FLOAT: + return ENCODING_PCM_FLOAT; + case AUDIO_FORMAT_DEFAULT: + return ENCODING_DEFAULT; + default: + return ENCODING_INVALID; + } +} + +static inline audio_channel_mask_t outChannelMaskToNative(int channelMask) +{ + switch (channelMask) { + case CHANNEL_OUT_DEFAULT: + case CHANNEL_INVALID: + return AUDIO_CHANNEL_NONE; + default: + return (audio_channel_mask_t)(channelMask>>2); + } +} + +static inline int outChannelMaskFromNative(audio_channel_mask_t nativeMask) +{ + switch (nativeMask) { + case AUDIO_CHANNEL_NONE: + return CHANNEL_OUT_DEFAULT; + default: + return (int)nativeMask<<2; + } +} + +static inline audio_channel_mask_t inChannelMaskToNative(int channelMask) +{ + return (audio_channel_mask_t)channelMask; +} + +static inline int inChannelMaskFromNative(audio_channel_mask_t nativeMask) +{ + return (int)nativeMask; +} + #endif // ANDROID_MEDIA_AUDIOFORMAT_H diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index a9a62f8..0f7e140 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -15,7 +15,9 @@ ** limitations under the License. */ -#define LOG_TAG "AudioSystem" +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioSystem-JNI" #include <utils/Log.h> #include <jni.h> @@ -26,6 +28,8 @@ #include <system/audio.h> #include <system/audio_policy.h> +#include "android_media_AudioFormat.h" +#include "android_media_AudioErrors.h" // ---------------------------------------------------------------------------- @@ -33,12 +37,160 @@ using namespace android; static const char* const kClassPathName = "android/media/AudioSystem"; +static jclass gArrayListClass; +static struct { + jmethodID add; +} gArrayListMethods; + +static jclass gAudioHandleClass; +static jmethodID gAudioHandleCstor; +static struct { + jfieldID mId; +} gAudioHandleFields; + +static jclass gAudioPortClass; +static jmethodID gAudioPortCstor; +static struct { + jfieldID mHandle; + jfieldID mRole; + jfieldID mGains; + jfieldID mActiveConfig; + // other fields unused by JNI +} gAudioPortFields; + +static jclass gAudioPortConfigClass; +static jmethodID gAudioPortConfigCstor; +static struct { + jfieldID mPort; + jfieldID mSamplingRate; + jfieldID mChannelMask; + jfieldID mFormat; + jfieldID mGain; + jfieldID mConfigMask; +} gAudioPortConfigFields; + +static jclass gAudioDevicePortClass; +static jmethodID gAudioDevicePortCstor; + +static jclass gAudioDevicePortConfigClass; +static jmethodID gAudioDevicePortConfigCstor; + +static jclass gAudioMixPortClass; +static jmethodID gAudioMixPortCstor; + +static jclass gAudioMixPortConfigClass; +static jmethodID gAudioMixPortConfigCstor; + +static jclass gAudioGainClass; +static jmethodID gAudioGainCstor; + +static jclass gAudioGainConfigClass; +static jmethodID gAudioGainConfigCstor; +static struct { + jfieldID mIndex; + jfieldID mMode; + jfieldID mChannelMask; + jfieldID mValues; + jfieldID mRampDurationMs; + // other fields unused by JNI +} gAudioGainConfigFields; + +static jclass gAudioPatchClass; +static jmethodID gAudioPatchCstor; +static struct { + jfieldID mHandle; + // other fields unused by JNI +} gAudioPatchFields; + +static const char* const kEventHandlerClassPathName = + "android/media/AudioPortEventHandler"; +static jmethodID gPostEventFromNative; + enum AudioError { kAudioStatusOk = 0, kAudioStatusError = 1, kAudioStatusMediaServerDied = 100 }; +enum { + AUDIOPORT_EVENT_PORT_LIST_UPDATED = 1, + AUDIOPORT_EVENT_PATCH_LIST_UPDATED = 2, + AUDIOPORT_EVENT_SERVICE_DIED = 3, +}; + +#define MAX_PORT_GENERATION_SYNC_ATTEMPTS 5 + +// ---------------------------------------------------------------------------- +// ref-counted object for callbacks +class JNIAudioPortCallback: public AudioSystem::AudioPortCallback +{ +public: + JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz); + ~JNIAudioPortCallback(); + + virtual void onAudioPortListUpdate(); + virtual void onAudioPatchListUpdate(); + virtual void onServiceDied(); + +private: + void sendEvent(int event); + + jclass mClass; // Reference to AudioPortEventHandlerDelegate class + jobject mObject; // Weak ref to AudioPortEventHandlerDelegate Java object to call on +}; + +JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject weak_thiz) +{ + + // Hold onto the SoundTriggerModule class for use in calling the static method + // that posts events to the application thread. + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + ALOGE("Can't find class %s", kEventHandlerClassPathName); + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the SoundTriggerModule object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); +} + +JNIAudioPortCallback::~JNIAudioPortCallback() +{ + // remove global references + JNIEnv *env = AndroidRuntime::getJNIEnv(); + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNIAudioPortCallback::sendEvent(int event) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->CallStaticVoidMethod(mClass, gPostEventFromNative, mObject, + event, 0, 0, NULL); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying an event."); + env->ExceptionClear(); + } +} + +void JNIAudioPortCallback::onAudioPortListUpdate() +{ + sendEvent(AUDIOPORT_EVENT_PORT_LIST_UPDATED); +} + +void JNIAudioPortCallback::onAudioPatchListUpdate() +{ + sendEvent(AUDIOPORT_EVENT_PATCH_LIST_UPDATED); +} + +void JNIAudioPortCallback::onServiceDied() +{ + sendEvent(AUDIOPORT_EVENT_SERVICE_DIED); +} + static int check_AudioSystem_Command(status_t status) { switch (status) { @@ -281,6 +433,854 @@ android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz) return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); } + +static bool useInChannelMask(audio_port_type_t type, audio_port_role_t role) +{ + return ((type == AUDIO_PORT_TYPE_DEVICE) && (role == AUDIO_PORT_ROLE_SOURCE)) || + ((type == AUDIO_PORT_TYPE_MIX) && (role == AUDIO_PORT_ROLE_SINK)); +} + +static void convertAudioGainConfigToNative(JNIEnv *env, + struct audio_gain_config *nAudioGainConfig, + const jobject jAudioGainConfig, + bool useInMask) +{ + nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex); + nAudioGainConfig->mode = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode); + ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index); + jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask); + audio_channel_mask_t nMask; + if (useInMask) { + nMask = inChannelMaskToNative(jMask); + ALOGV("convertAudioGainConfigToNative IN mask java %x native %x", jMask, nMask); + } else { + nMask = outChannelMaskToNative(jMask); + ALOGV("convertAudioGainConfigToNative OUT mask java %x native %x", jMask, nMask); + } + nAudioGainConfig->channel_mask = nMask; + nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig, + gAudioGainConfigFields.mRampDurationMs); + jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig, + gAudioGainConfigFields.mValues); + int *nValues = env->GetIntArrayElements(jValues, NULL); + size_t size = env->GetArrayLength(jValues); + memcpy(nAudioGainConfig->values, nValues, size * sizeof(int)); + env->DeleteLocalRef(jValues); +} + + +static jint convertAudioPortConfigToNative(JNIEnv *env, + struct audio_port_config *nAudioPortConfig, + const jobject jAudioPortConfig) +{ + jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); + jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle); + nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId); + nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort, + gAudioPortFields.mRole); + if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { + nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE; + } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { + nAudioPortConfig->type = AUDIO_PORT_TYPE_MIX; + } else { + env->DeleteLocalRef(jAudioPort); + env->DeleteLocalRef(jHandle); + return (jint)AUDIO_JAVA_ERROR; + } + ALOGV("convertAudioPortConfigToNative handle %d role %d type %d", + nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type); + + nAudioPortConfig->sample_rate = env->GetIntField(jAudioPortConfig, + gAudioPortConfigFields.mSamplingRate); + + bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role); + audio_channel_mask_t nMask; + jint jMask = env->GetIntField(jAudioPortConfig, + gAudioPortConfigFields.mChannelMask); + if (useInMask) { + nMask = inChannelMaskToNative(jMask); + ALOGV("convertAudioPortConfigToNative IN mask java %x native %x", jMask, nMask); + } else { + nMask = outChannelMaskToNative(jMask); + ALOGV("convertAudioPortConfigToNative OUT mask java %x native %x", jMask, nMask); + } + nAudioPortConfig->channel_mask = nMask; + + jint jFormat = env->GetIntField(jAudioPortConfig, gAudioPortConfigFields.mFormat); + audio_format_t nFormat = audioFormatToNative(jFormat); + ALOGV("convertAudioPortConfigToNative format %d native %d", jFormat, nFormat); + nAudioPortConfig->format = nFormat; + jobject jGain = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mGain); + if (jGain != NULL) { + convertAudioGainConfigToNative(env, &nAudioPortConfig->gain, jGain, useInMask); + env->DeleteLocalRef(jGain); + } else { + ALOGV("convertAudioPortConfigToNative no gain"); + nAudioPortConfig->gain.index = -1; + } + nAudioPortConfig->config_mask = env->GetIntField(jAudioPortConfig, + gAudioPortConfigFields.mConfigMask); + + env->DeleteLocalRef(jAudioPort); + env->DeleteLocalRef(jHandle); + return (jint)AUDIO_JAVA_SUCCESS; +} + +static jint convertAudioPortConfigFromNative(JNIEnv *env, + jobject jAudioPort, + jobject *jAudioPortConfig, + const struct audio_port_config *nAudioPortConfig) +{ + jint jStatus = AUDIO_JAVA_SUCCESS; + jobject jAudioGainConfig = NULL; + jobject jAudioGain = NULL; + jintArray jGainValues; + bool audioportCreated = false; + + ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort); + + if (jAudioPort == NULL) { + jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPortConfig->id); + + ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id, + nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix"); + + if (jHandle == NULL) { + return (jint)AUDIO_JAVA_ERROR; + } + // create dummy port and port config objects with just the correct handle + // and configuration data. The actual AudioPortConfig objects will be + // constructed by java code with correct class type (device, mix etc...) + // and reference to AudioPort instance in this client + jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor, + jHandle, + 0, + NULL, + NULL, + NULL, + NULL); + env->DeleteLocalRef(jHandle); + if (jAudioPort == NULL) { + return (jint)AUDIO_JAVA_ERROR; + } + ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d", + nAudioPortConfig->id); + + audioportCreated = true; + } + + bool useInMask = useInChannelMask(nAudioPortConfig->type, nAudioPortConfig->role); + + audio_channel_mask_t nMask; + jint jMask; + + int gainIndex = nAudioPortConfig->gain.index; + if (gainIndex >= 0) { + ALOGV("convertAudioPortConfigFromNative gain found with index %d mode %x", + gainIndex, nAudioPortConfig->gain.mode); + if (audioportCreated) { + ALOGV("convertAudioPortConfigFromNative creating gain"); + jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor, + gainIndex, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0); + if (jAudioGain == NULL) { + ALOGV("convertAudioPortConfigFromNative creating gain FAILED"); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } else { + ALOGV("convertAudioPortConfigFromNative reading gain from port"); + jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort, + gAudioPortFields.mGains); + if (jGains == NULL) { + ALOGV("convertAudioPortConfigFromNative could not get gains from port"); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + jAudioGain = env->GetObjectArrayElement(jGains, gainIndex); + env->DeleteLocalRef(jGains); + if (jAudioGain == NULL) { + ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } + //TODO: replace popcount by audio utils function mask to count + int numValues = popcount(nAudioPortConfig->gain.channel_mask); + jGainValues = env->NewIntArray(numValues); + if (jGainValues == NULL) { + ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + env->SetIntArrayRegion(jGainValues, 0, numValues, + nAudioPortConfig->gain.values); + + nMask = nAudioPortConfig->gain.channel_mask; + if (useInMask) { + jMask = inChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask); + } else { + jMask = outChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); + } + + jAudioGainConfig = env->NewObject(gAudioGainConfigClass, + gAudioGainConfigCstor, + gainIndex, + jAudioGain, + nAudioPortConfig->gain.mode, + jMask, + jGainValues, + nAudioPortConfig->gain.ramp_duration_ms); + env->DeleteLocalRef(jGainValues); + if (jAudioGainConfig == NULL) { + ALOGV("convertAudioPortConfigFromNative could not create gain config"); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } + jclass clazz; + jmethodID methodID; + if (audioportCreated) { + clazz = gAudioPortConfigClass; + methodID = gAudioPortConfigCstor; + ALOGV("convertAudioPortConfigFromNative building a generic port config"); + } else { + if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { + clazz = gAudioDevicePortConfigClass; + methodID = gAudioDevicePortConfigCstor; + ALOGV("convertAudioPortConfigFromNative building a device config"); + } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { + clazz = gAudioMixPortConfigClass; + methodID = gAudioMixPortConfigCstor; + ALOGV("convertAudioPortConfigFromNative building a mix config"); + } else { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + } + nMask = nAudioPortConfig->channel_mask; + if (useInMask) { + jMask = inChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask); + } else { + jMask = outChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); + } + + *jAudioPortConfig = env->NewObject(clazz, methodID, + jAudioPort, + nAudioPortConfig->sample_rate, + jMask, + audioFormatFromNative(nAudioPortConfig->format), + jAudioGainConfig); + if (*jAudioPortConfig == NULL) { + ALOGV("convertAudioPortConfigFromNative could not create new port config"); + jStatus = (jint)AUDIO_JAVA_ERROR; + } else { + ALOGV("convertAudioPortConfigFromNative OK"); + } + +exit: + if (audioportCreated) { + env->DeleteLocalRef(jAudioPort); + if (jAudioGain != NULL) { + env->DeleteLocalRef(jAudioGain); + } + } + if (jAudioGainConfig != NULL) { + env->DeleteLocalRef(jAudioGainConfig); + } + return jStatus; +} + +static jint convertAudioPortFromNative(JNIEnv *env, + jobject *jAudioPort, const struct audio_port *nAudioPort) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jintArray jSamplingRates = NULL; + jintArray jChannelMasks = NULL; + jintArray jFormats = NULL; + jobjectArray jGains = NULL; + jobject jHandle = NULL; + bool useInMask; + + ALOGV("convertAudioPortFromNative id %d role %d type %d", + nAudioPort->id, nAudioPort->role, nAudioPort->type); + + jSamplingRates = env->NewIntArray(nAudioPort->num_sample_rates); + if (jSamplingRates == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + if (nAudioPort->num_sample_rates) { + env->SetIntArrayRegion(jSamplingRates, 0, nAudioPort->num_sample_rates, + (jint *)nAudioPort->sample_rates); + } + + jChannelMasks = env->NewIntArray(nAudioPort->num_channel_masks); + if (jChannelMasks == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + useInMask = useInChannelMask(nAudioPort->type, nAudioPort->role); + + jint jMask; + for (size_t j = 0; j < nAudioPort->num_channel_masks; j++) { + if (useInMask) { + jMask = inChannelMaskFromNative(nAudioPort->channel_masks[j]); + } else { + jMask = outChannelMaskFromNative(nAudioPort->channel_masks[j]); + } + env->SetIntArrayRegion(jChannelMasks, j, 1, &jMask); + } + + jFormats = env->NewIntArray(nAudioPort->num_formats); + if (jFormats == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + for (size_t j = 0; j < nAudioPort->num_formats; j++) { + jint jFormat = audioFormatFromNative(nAudioPort->formats[j]); + env->SetIntArrayRegion(jFormats, j, 1, &jFormat); + } + + jGains = env->NewObjectArray(nAudioPort->num_gains, + gAudioGainClass, NULL); + if (jGains == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + for (size_t j = 0; j < nAudioPort->num_gains; j++) { + audio_channel_mask_t nMask = nAudioPort->gains[j].channel_mask; + if (useInMask) { + jMask = inChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative IN mask java %x native %x", jMask, nMask); + } else { + jMask = outChannelMaskFromNative(nMask); + ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); + } + + jobject jGain = env->NewObject(gAudioGainClass, gAudioGainCstor, + j, + nAudioPort->gains[j].mode, + jMask, + nAudioPort->gains[j].min_value, + nAudioPort->gains[j].max_value, + nAudioPort->gains[j].default_value, + nAudioPort->gains[j].step_value, + nAudioPort->gains[j].min_ramp_ms, + nAudioPort->gains[j].max_ramp_ms); + if (jGain == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + env->SetObjectArrayElement(jGains, j, jGain); + env->DeleteLocalRef(jGain); + } + + jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPort->id); + if (jHandle == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) { + ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); + jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address); + *jAudioPort = env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, + jHandle, jSamplingRates, jChannelMasks, jFormats, jGains, + nAudioPort->ext.device.type, jAddress); + env->DeleteLocalRef(jAddress); + } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) { + ALOGV("convertAudioPortFromNative is a mix"); + *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, + jHandle, nAudioPort->role, jSamplingRates, jChannelMasks, + jFormats, jGains); + } else { + ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type); + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + if (*jAudioPort == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + + jobject jAudioPortConfig; + jStatus = convertAudioPortConfigFromNative(env, + *jAudioPort, + &jAudioPortConfig, + &nAudioPort->active_config); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + + env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig); + +exit: + if (jSamplingRates != NULL) { + env->DeleteLocalRef(jSamplingRates); + } + if (jChannelMasks != NULL) { + env->DeleteLocalRef(jChannelMasks); + } + if (jFormats != NULL) { + env->DeleteLocalRef(jFormats); + } + if (jGains != NULL) { + env->DeleteLocalRef(jGains); + } + if (jHandle != NULL) { + env->DeleteLocalRef(jHandle); + } + + return jStatus; +} + + +static jint +android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, + jobject jPorts, jintArray jGeneration) +{ + ALOGV("listAudioPorts"); + + if (jPorts == NULL) { + ALOGE("listAudioPorts NULL AudioPort ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jPorts, gArrayListClass)) { + ALOGE("listAudioPorts not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + status_t status; + unsigned int generation1; + unsigned int generation; + unsigned int numPorts; + jint *nGeneration; + struct audio_port *nPorts = NULL; + int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; + + // get the port count and all the ports until they both return the same generation + do { + if (attempts-- < 0) { + status = TIMED_OUT; + break; + } + + numPorts = 0; + status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, + AUDIO_PORT_TYPE_NONE, + &numPorts, + NULL, + &generation1); + if (status != NO_ERROR || numPorts == 0) { + ALOGE_IF(status != NO_ERROR, "AudioSystem::listAudioPorts error %d", status); + break; + } + nPorts = (struct audio_port *)realloc(nPorts, numPorts * sizeof(struct audio_port)); + + status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, + AUDIO_PORT_TYPE_NONE, + &numPorts, + nPorts, + &generation); + ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d", + numPorts, generation, generation1); + } while (generation1 != generation && status == NO_ERROR); + + jint jStatus = nativeToJavaStatus(status); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + + nGeneration = env->GetIntArrayElements(jGeneration, NULL); + if (nGeneration == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + nGeneration[0] = generation1; + env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); + + for (size_t i = 0; i < numPorts; i++) { + jobject jAudioPort; + jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort); + } + +exit: + free(nPorts); + return jStatus; +} + +static int +android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, + jobjectArray jPatches, jobjectArray jSources, jobjectArray jSinks) +{ + status_t status; + jint jStatus; + + ALOGV("createAudioPatch"); + if (jPatches == NULL || jSources == NULL || jSinks == NULL) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + if (env->GetArrayLength(jPatches) != 1) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + jint numSources = env->GetArrayLength(jSources); + if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + jint numSinks = env->GetArrayLength(jSinks); + if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + audio_patch_handle_t handle = (audio_patch_handle_t)0; + jobject jPatch = env->GetObjectArrayElement(jPatches, 0); + jobject jPatchHandle = NULL; + if (jPatch != NULL) { + if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); + handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + } + + struct audio_patch nPatch; + + nPatch.id = handle; + nPatch.num_sources = 0; + nPatch.num_sinks = 0; + jobject jSource = NULL; + jobject jSink = NULL; + + for (jint i = 0; i < numSources; i++) { + jSource = env->GetObjectArrayElement(jSources, i); + if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) { + jStatus = (jint)AUDIO_JAVA_BAD_VALUE; + goto exit; + } + jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource); + env->DeleteLocalRef(jSource); + jSource = NULL; + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + nPatch.num_sources++; + } + + for (jint i = 0; i < numSinks; i++) { + jSink = env->GetObjectArrayElement(jSinks, i); + if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) { + jStatus = (jint)AUDIO_JAVA_BAD_VALUE; + goto exit; + } + jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink); + env->DeleteLocalRef(jSink); + jSink = NULL; + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + nPatch.num_sinks++; + } + + ALOGV("AudioSystem::createAudioPatch"); + status = AudioSystem::createAudioPatch(&nPatch, &handle); + ALOGV("AudioSystem::createAudioPatch() returned %d hande %d", status, handle); + + jStatus = nativeToJavaStatus(status); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + + if (jPatchHandle == NULL) { + jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + handle); + if (jPatchHandle == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks); + if (jPatch == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + env->SetObjectArrayElement(jPatches, 0, jPatch); + } else { + env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle); + } + +exit: + if (jPatchHandle != NULL) { + env->DeleteLocalRef(jPatchHandle); + } + if (jPatch != NULL) { + env->DeleteLocalRef(jPatch); + } + if (jSource != NULL) { + env->DeleteLocalRef(jSource); + } + if (jSink != NULL) { + env->DeleteLocalRef(jSink); + } + return jStatus; +} + +static int +android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz, + jobject jPatch) +{ + ALOGV("releaseAudioPatch"); + if (jPatch == NULL) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + audio_patch_handle_t handle = (audio_patch_handle_t)0; + jobject jPatchHandle = NULL; + if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); + handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + env->DeleteLocalRef(jPatchHandle); + + ALOGV("AudioSystem::releaseAudioPatch"); + status_t status = AudioSystem::releaseAudioPatch(handle); + ALOGV("AudioSystem::releaseAudioPatch() returned %d", status); + jint jStatus = nativeToJavaStatus(status); + return status; +} + +static jint +android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, + jobject jPatches, jintArray jGeneration) +{ + ALOGV("listAudioPatches"); + if (jPatches == NULL) { + ALOGE("listAudioPatches NULL AudioPatch ArrayList"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jPatches, gArrayListClass)) { + ALOGE("listAudioPatches not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + status_t status; + unsigned int generation1; + unsigned int generation; + unsigned int numPatches; + jint *nGeneration; + struct audio_patch *nPatches = NULL; + jobjectArray jSources = NULL; + jobject jSource = NULL; + jobjectArray jSinks = NULL; + jobject jSink = NULL; + jobject jPatch = NULL; + int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; + + // get the patch count and all the patches until they both return the same generation + do { + if (attempts-- < 0) { + status = TIMED_OUT; + break; + } + + numPatches = 0; + status = AudioSystem::listAudioPatches(&numPatches, + NULL, + &generation1); + if (status != NO_ERROR || numPatches == 0) { + ALOGE_IF(status != NO_ERROR, "listAudioPatches AudioSystem::listAudioPatches error %d", + status); + break; + } + nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch)); + + status = AudioSystem::listAudioPatches(&numPatches, + nPatches, + &generation); + ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d", + numPatches, generation, generation1); + + } while (generation1 != generation && status == NO_ERROR); + + jint jStatus = nativeToJavaStatus(status); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + + nGeneration = env->GetIntArrayElements(jGeneration, NULL); + if (nGeneration == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + nGeneration[0] = generation1; + env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); + + for (size_t i = 0; i < numPatches; i++) { + jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nPatches[i].id); + if (patchHandle == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + ALOGV("listAudioPatches patch %d num_sources %d num_sinks %d", + i, nPatches[i].num_sources, nPatches[i].num_sinks); + + env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id); + + // load sources + jSources = env->NewObjectArray(nPatches[i].num_sources, + gAudioPortConfigClass, NULL); + if (jSources == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + + for (size_t j = 0; j < nPatches[i].num_sources; j++) { + jStatus = convertAudioPortConfigFromNative(env, + NULL, + &jSource, + &nPatches[i].sources[j]); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->SetObjectArrayElement(jSources, j, jSource); + env->DeleteLocalRef(jSource); + jSource = NULL; + ALOGV("listAudioPatches patch %d source %d is a %s handle %d", + i, j, + nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", + nPatches[i].sources[j].id); + } + // load sinks + jSinks = env->NewObjectArray(nPatches[i].num_sinks, + gAudioPortConfigClass, NULL); + if (jSinks == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + + for (size_t j = 0; j < nPatches[i].num_sinks; j++) { + jStatus = convertAudioPortConfigFromNative(env, + NULL, + &jSink, + &nPatches[i].sinks[j]); + + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->SetObjectArrayElement(jSinks, j, jSink); + env->DeleteLocalRef(jSink); + jSink = NULL; + ALOGV("listAudioPatches patch %d sink %d is a %s handle %d", + i, j, + nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", + nPatches[i].sinks[j].id); + } + + jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, + patchHandle, jSources, jSinks); + env->DeleteLocalRef(jSources); + jSources = NULL; + env->DeleteLocalRef(jSinks); + jSinks = NULL; + if (jPatch == NULL) { + jStatus = AUDIO_JAVA_ERROR; + goto exit; + } + env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch); + env->DeleteLocalRef(jPatch); + jPatch = NULL; + } + +exit: + if (jSources != NULL) { + env->DeleteLocalRef(jSources); + } + if (jSource != NULL) { + env->DeleteLocalRef(jSource); + } + if (jSinks != NULL) { + env->DeleteLocalRef(jSinks); + } + if (jSink != NULL) { + env->DeleteLocalRef(jSink); + } + if (jPatch != NULL) { + env->DeleteLocalRef(jPatch); + } + free(nPatches); + return jStatus; +} + +static jint +android_media_AudioSystem_setAudioPortConfig(JNIEnv *env, jobject clazz, + jobject jAudioPortConfig) +{ + ALOGV("setAudioPortConfig"); + if (jAudioPortConfig == NULL) { + return AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jAudioPortConfig, gAudioPortConfigClass)) { + return AUDIO_JAVA_BAD_VALUE; + } + struct audio_port_config nAudioPortConfig; + jint jStatus = convertAudioPortConfigToNative(env, &nAudioPortConfig, jAudioPortConfig); + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; + } + status_t status = AudioSystem::setAudioPortConfig(&nAudioPortConfig); + ALOGV("AudioSystem::setAudioPortConfig() returned %d", status); + jStatus = nativeToJavaStatus(status); + return jStatus; +} + +static void +android_media_AudioSystem_eventHandlerSetup(JNIEnv *env, jobject thiz, jobject weak_this) +{ + ALOGV("eventHandlerSetup"); + + sp<JNIAudioPortCallback> callback = new JNIAudioPortCallback(env, thiz, weak_this); + + AudioSystem::setAudioPortCallback(callback); +} + +static void +android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz) +{ + ALOGV("eventHandlerFinalize"); + + sp<JNIAudioPortCallback> callback; + + AudioSystem::setAudioPortCallback(callback); +} + // ---------------------------------------------------------------------------- static JNINativeMethod gMethods[] = { @@ -309,12 +1309,123 @@ static JNINativeMethod gMethods[] = { {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency}, {"setLowRamDevice", "(Z)I", (void *)android_media_AudioSystem_setLowRamDevice}, {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger}, + {"listAudioPorts", "(Ljava/util/ArrayList;[I)I", + (void *)android_media_AudioSystem_listAudioPorts}, + {"createAudioPatch", "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)I", + (void *)android_media_AudioSystem_createAudioPatch}, + {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I", + (void *)android_media_AudioSystem_releaseAudioPatch}, + {"listAudioPatches", "(Ljava/util/ArrayList;[I)I", + (void *)android_media_AudioSystem_listAudioPatches}, + {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", + (void *)android_media_AudioSystem_setAudioPortConfig}, +}; + + +static JNINativeMethod gEventHandlerMethods[] = { + {"native_setup", + "(Ljava/lang/Object;)V", + (void *)android_media_AudioSystem_eventHandlerSetup}, + {"native_finalize", + "()V", + (void *)android_media_AudioSystem_eventHandlerFinalize}, }; int register_android_media_AudioSystem(JNIEnv *env) { + + jclass arrayListClass = env->FindClass("java/util/ArrayList"); + gArrayListClass = (jclass) env->NewGlobalRef(arrayListClass); + gArrayListMethods.add = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z"); + + jclass audioHandleClass = env->FindClass("android/media/AudioHandle"); + gAudioHandleClass = (jclass) env->NewGlobalRef(audioHandleClass); + gAudioHandleCstor = env->GetMethodID(audioHandleClass, "<init>", "(I)V"); + gAudioHandleFields.mId = env->GetFieldID(audioHandleClass, "mId", "I"); + + jclass audioPortClass = env->FindClass("android/media/AudioPort"); + gAudioPortClass = (jclass) env->NewGlobalRef(audioPortClass); + gAudioPortCstor = env->GetMethodID(audioPortClass, "<init>", + "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V"); + gAudioPortFields.mHandle = env->GetFieldID(audioPortClass, "mHandle", + "Landroid/media/AudioHandle;"); + gAudioPortFields.mRole = env->GetFieldID(audioPortClass, "mRole", "I"); + gAudioPortFields.mGains = env->GetFieldID(audioPortClass, "mGains", + "[Landroid/media/AudioGain;"); + gAudioPortFields.mActiveConfig = env->GetFieldID(audioPortClass, "mActiveConfig", + "Landroid/media/AudioPortConfig;"); + + jclass audioPortConfigClass = env->FindClass("android/media/AudioPortConfig"); + gAudioPortConfigClass = (jclass) env->NewGlobalRef(audioPortConfigClass); + gAudioPortConfigCstor = env->GetMethodID(audioPortConfigClass, "<init>", + "(Landroid/media/AudioPort;IIILandroid/media/AudioGainConfig;)V"); + gAudioPortConfigFields.mPort = env->GetFieldID(audioPortConfigClass, "mPort", + "Landroid/media/AudioPort;"); + gAudioPortConfigFields.mSamplingRate = env->GetFieldID(audioPortConfigClass, + "mSamplingRate", "I"); + gAudioPortConfigFields.mChannelMask = env->GetFieldID(audioPortConfigClass, + "mChannelMask", "I"); + gAudioPortConfigFields.mFormat = env->GetFieldID(audioPortConfigClass, "mFormat", "I"); + gAudioPortConfigFields.mGain = env->GetFieldID(audioPortConfigClass, "mGain", + "Landroid/media/AudioGainConfig;"); + gAudioPortConfigFields.mConfigMask = env->GetFieldID(audioPortConfigClass, "mConfigMask", "I"); + + jclass audioDevicePortConfigClass = env->FindClass("android/media/AudioDevicePortConfig"); + gAudioDevicePortConfigClass = (jclass) env->NewGlobalRef(audioDevicePortConfigClass); + gAudioDevicePortConfigCstor = env->GetMethodID(audioDevicePortConfigClass, "<init>", + "(Landroid/media/AudioDevicePort;IIILandroid/media/AudioGainConfig;)V"); + + jclass audioMixPortConfigClass = env->FindClass("android/media/AudioMixPortConfig"); + gAudioMixPortConfigClass = (jclass) env->NewGlobalRef(audioMixPortConfigClass); + gAudioMixPortConfigCstor = env->GetMethodID(audioMixPortConfigClass, "<init>", + "(Landroid/media/AudioMixPort;IIILandroid/media/AudioGainConfig;)V"); + + jclass audioDevicePortClass = env->FindClass("android/media/AudioDevicePort"); + gAudioDevicePortClass = (jclass) env->NewGlobalRef(audioDevicePortClass); + gAudioDevicePortCstor = env->GetMethodID(audioDevicePortClass, "<init>", + "(Landroid/media/AudioHandle;[I[I[I[Landroid/media/AudioGain;ILjava/lang/String;)V"); + + jclass audioMixPortClass = env->FindClass("android/media/AudioMixPort"); + gAudioMixPortClass = (jclass) env->NewGlobalRef(audioMixPortClass); + gAudioMixPortCstor = env->GetMethodID(audioMixPortClass, "<init>", + "(Landroid/media/AudioHandle;I[I[I[I[Landroid/media/AudioGain;)V"); + + jclass audioGainClass = env->FindClass("android/media/AudioGain"); + gAudioGainClass = (jclass) env->NewGlobalRef(audioGainClass); + gAudioGainCstor = env->GetMethodID(audioGainClass, "<init>", "(IIIIIIIII)V"); + + jclass audioGainConfigClass = env->FindClass("android/media/AudioGainConfig"); + gAudioGainConfigClass = (jclass) env->NewGlobalRef(audioGainConfigClass); + gAudioGainConfigCstor = env->GetMethodID(audioGainConfigClass, "<init>", + "(ILandroid/media/AudioGain;II[II)V"); + gAudioGainConfigFields.mIndex = env->GetFieldID(gAudioGainConfigClass, "mIndex", "I"); + gAudioGainConfigFields.mMode = env->GetFieldID(audioGainConfigClass, "mMode", "I"); + gAudioGainConfigFields.mChannelMask = env->GetFieldID(audioGainConfigClass, "mChannelMask", + "I"); + gAudioGainConfigFields.mValues = env->GetFieldID(audioGainConfigClass, "mValues", "[I"); + gAudioGainConfigFields.mRampDurationMs = env->GetFieldID(audioGainConfigClass, + "mRampDurationMs", "I"); + + jclass audioPatchClass = env->FindClass("android/media/AudioPatch"); + gAudioPatchClass = (jclass) env->NewGlobalRef(audioPatchClass); + gAudioPatchCstor = env->GetMethodID(audioPatchClass, "<init>", +"(Landroid/media/AudioHandle;[Landroid/media/AudioPortConfig;[Landroid/media/AudioPortConfig;)V"); + gAudioPatchFields.mHandle = env->GetFieldID(audioPatchClass, "mHandle", + "Landroid/media/AudioHandle;"); + + jclass eventHandlerClass = env->FindClass(kEventHandlerClassPathName); + gPostEventFromNative = env->GetStaticMethodID(eventHandlerClass, "postEventFromNative", + "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + + AudioSystem::setErrorCallback(android_media_AudioSystem_error_callback); - return AndroidRuntime::registerNativeMethods(env, + int status = AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); + + if (status == 0) { + status = AndroidRuntime::registerNativeMethods(env, + kEventHandlerClassPathName, gEventHandlerMethods, NELEM(gEventHandlerMethods)); + } + return status; } diff --git a/core/res/res/anim/input_method_exit.xml b/core/res/res/anim/input_method_exit.xml index 4c4f6a4..117774a 100644 --- a/core/res/res/anim/input_method_exit.xml +++ b/core/res/res/anim/input_method_exit.xml @@ -17,7 +17,7 @@ --> <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false"> - <translate android:fromYDelta="0" android:toYDelta="-20%" + <translate android:fromYDelta="0" android:toYDelta="10%" android:interpolator="@interpolator/accelerate_quint" android:duration="@android:integer/config_shortAnimTime"/> <alpha android:fromAlpha="1.0" android:toAlpha="0.0" diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e9d8ccc..f6732d3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1538,6 +1538,7 @@ --> <string-array translatable="false" name="config_globalActionsList"> <item>power</item> + <item>bugreport</item> <item>users</item> </string-array> |