diff options
author | Matt Garnes <matt@cyngn.com> | 2015-09-04 18:02:20 -0700 |
---|---|---|
committer | Steve Kondik <steve@cyngn.com> | 2015-10-28 13:30:32 -0700 |
commit | b09f7797429af5c0034d1a6c52c2a47a35b0e428 (patch) | |
tree | 90d7b719101a9df1d88da2801ccf3144f20b997b | |
parent | 66ed04e967adb4bb5545ecf06ce692c6e5b790ea (diff) | |
download | frameworks_base-b09f7797429af5c0034d1a6c52c2a47a35b0e428.zip frameworks_base-b09f7797429af5c0034d1a6c52c2a47a35b0e428.tar.gz frameworks_base-b09f7797429af5c0034d1a6c52c2a47a35b0e428.tar.bz2 |
Add broadcast and query API for AudioSource.HOTWORD.
- When the AudioSource.HOTWORD input becomes active or is released, send
a Broadcast with the package name and the new state of the audio input
to any applications that hold CAPTURE_AUDIO_OUTPUT.
- Store the package name of the application that controls the hOTWORD
input or set it to null if the input is not in use.
- Add a new method to AudioService to retrieve the package name of the
application that currently controls the HOTWORD input.
Change-Id: I2f11888f3711d23b6287a4de7b81d361734a8f3b
-rw-r--r-- | core/java/android/content/Intent.java | 48 | ||||
-rw-r--r-- | media/java/android/media/AudioRecord.java | 19 | ||||
-rw-r--r-- | media/java/android/media/IAudioService.aidl | 5 | ||||
-rw-r--r-- | services/core/java/com/android/server/audio/AudioService.java | 56 |
4 files changed, 127 insertions, 1 deletions
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 404564b..c63fe1c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2678,6 +2678,45 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.GET_RESTRICTION_ENTRIES"; /** + * <p>Broadcast Action: The state of the HOTWORD audio input has changed.:</p> + * <ul> + * <li><em>state</em> - A String value indicating the state of the input. + * {@link #EXTRA_HOTWORD_INPUT_STATE}. The value will be one of: + * {@link android.media.AudioRecord#RECORDSTATE_RECORDING} or + * {@link android.media.AudioRecord#RECORDSTATE_STOPPED}. + * </li> + * <li><em>package</em> - A String value indicating the package name of the application + * that currently holds the HOTWORD input. + * {@link #EXTRA_CURRENT_PACKAGE_NAME} + * </li> + * </ul> + * + * <p class="note">This is a protected intent that can only be sent + * by the system. It can only be received by packages that hold + * {@link android.Manifest.permission#CAPTURE_AUDIO_HOTWORD}. + * + * @hide + */ + //@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_HOTWORD_INPUT_CHANGED + = "com.cyanogenmod.intent.action.HOTWORD_INPUT_CHANGED"; + + /** + * @hide + * Activity to challenge the user for a PIN that was configured when setting up + * restrictions. Restrictions include blocking of apps and preventing certain user operations, + * controlled by {@link android.os.UserManager#setUserRestrictions(Bundle). + * Launch the activity using + * {@link android.app.Activity#startActivityForResult(Intent, int)} and check if the + * result is {@link android.app.Activity#RESULT_OK} for a successful response to the + * challenge.<p/> + * Before launching this activity, make sure that there is a PIN in effect, by calling + * {@link android.os.UserManager#hasRestrictionsChallenge()}. + */ + public static final String ACTION_RESTRICTIONS_CHALLENGE = + "android.intent.action.RESTRICTIONS_CHALLENGE"; + + /** * Sent the first time a user is starting, to allow system apps to * perform one time initialization. (This will not be seen by third * party applications because a newly initialized user does not have any @@ -3881,6 +3920,15 @@ public class Intent implements Parcelable, Cloneable { */ public static final String EXTRA_THEME_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME"; + /** + * Extra for {@link #ACTION_HOTWORD_INPUT_CHANGED} that provides the state of + * the input when the broadcast action was sent. + * @hide + */ + public static final String EXTRA_HOTWORD_INPUT_STATE = + "com.cyanogenmod.intent.extra.HOTWORD_INPUT_STATE"; + + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 27cc220..c1ca2b0 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -903,6 +903,11 @@ public class AudioRecord mRecordingState = RECORDSTATE_RECORDING; } } + + if (getRecordingState() == RECORDSTATE_RECORDING && + getAudioSource() == MediaRecorder.AudioSource.HOTWORD) { + handleHotwordInput(true); + } } /** @@ -946,6 +951,10 @@ public class AudioRecord native_stop(); mRecordingState = RECORDSTATE_STOPPED; } + + if (getAudioSource() == MediaRecorder.AudioSource.HOTWORD) { + handleHotwordInput(false); + } } private final IBinder mICallBack = new Binder(); @@ -962,6 +971,16 @@ public class AudioRecord } } + private void handleHotwordInput(boolean listening) { + final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE); + final IAudioService ias = IAudioService.Stub.asInterface(b); + try { + ias.handleHotwordInput(listening); + } catch (RemoteException e) { + Log.e(TAG, "Error talking to AudioService when handling hotword input.", e); + } + } + //--------------------------------------------------------- // Audio data supply //-------------------- diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 0b98e1b..cdcd83c 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -227,4 +227,9 @@ interface IAudioService { void addMediaPlayerAndUpdateRemoteController(String packageName); void removeMediaPlayerAndUpdateRemoteController(String packageName); + + void handleHotwordInput(boolean listening); + + String getCurrentHotwordInputPackageName(); + } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index cde2d7c..e60d9cf 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -59,6 +59,7 @@ import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioManagerInternal; import android.media.AudioPort; +import android.media.AudioRecord; import android.media.AudioRoutesInfo; import android.media.IAudioFocusDispatcher; import android.media.IAudioRoutesObserver; @@ -405,6 +406,15 @@ public class AudioService extends IAudioService.Stub { * @see System#MUTE_STREAMS_AFFECTED */ private int mMuteAffectedStreams; + /** @see #handleHotwordInput **/ + private Object mHotwordInputLock = new Object(); + + /** The package name of the application that is + * currently using the HOTWORD input. + */ + // protected by mHotwordInputLock + private String mHotwordAudioInputPackage; + /** * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated. * mVibrateSetting is just maintained during deprecation period but vibration policy is @@ -1601,6 +1611,46 @@ public class AudioService extends IAudioService.Stub { sendVolumeUpdate(streamType, oldIndex, index, flags); } + /** + * Retrieve the package name of the application that currently controls + * the {@link android.media.MediaRecorder.AudioSource#HOTWORD} input. + * @return The package name of the application that controls the input + * or null if no package currently controls it. + */ + public String getCurrentHotwordInputPackageName() { + return mHotwordAudioInputPackage; + } + + /** + * Handle the change of state of the HOTWORD input. + * + * When the {@link android.media.MediaRecorder.AudioSource#HOTWORD} input is + * in use, send a broadcast to alert the new state and store the name of the current + * package that controls the input in mHotwordAudioInputPackage. + * @param listening Whether the input is being listened to. + */ + public void handleHotwordInput(boolean listening) { + synchronized (mHotwordInputLock) { + Intent broadcastIntent = new Intent(Intent.ACTION_HOTWORD_INPUT_CHANGED); + String[] packages = mContext.getPackageManager().getPackagesForUid( + Binder.getCallingUid()); + if (packages.length > 0) { + if (listening) { + mHotwordAudioInputPackage = packages[0]; + } + broadcastIntent.putExtra(Intent.EXTRA_CURRENT_PACKAGE_NAME, packages[0]); + } + broadcastIntent.putExtra(Intent.EXTRA_HOTWORD_INPUT_STATE, + listening ? AudioRecord.RECORDSTATE_RECORDING : + AudioRecord.RECORDSTATE_STOPPED); + // Set the currently listening package to null if listening has stopped. + if (!listening) { + mHotwordAudioInputPackage = null; + } + sendBroadcastToAll(broadcastIntent, Manifest.permission.CAPTURE_AUDIO_HOTWORD); + } + } + /** @see AudioManager#forceVolumeControlStream(int) */ public void forceVolumeControlStream(int streamType, IBinder cb) { synchronized(mForceControlStreamLock) { @@ -1653,11 +1703,15 @@ public class AudioService extends IAudioService.Stub { } private void sendBroadcastToAll(Intent intent) { + sendBroadcastToAll(intent, null); + } + + private void sendBroadcastToAll(Intent intent, String receiverPermission) { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); final long ident = Binder.clearCallingIdentity(); try { - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, receiverPermission); } finally { Binder.restoreCallingIdentity(ident); } |