summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Garnes <matt@cyngn.com>2015-09-04 18:02:20 -0700
committerSteve Kondik <steve@cyngn.com>2015-10-28 13:30:32 -0700
commitb09f7797429af5c0034d1a6c52c2a47a35b0e428 (patch)
tree90d7b719101a9df1d88da2801ccf3144f20b997b
parent66ed04e967adb4bb5545ecf06ce692c6e5b790ea (diff)
downloadframeworks_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.java48
-rw-r--r--media/java/android/media/AudioRecord.java19
-rw-r--r--media/java/android/media/IAudioService.aidl5
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java56
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);
}