Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 62629f23 authored by Matt Garnes's avatar Matt Garnes Committed by Gerrit Code Review
Browse files

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
parent bd1c125c
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -2534,6 +2534,30 @@ public class Intent implements Parcelable, Cloneable {
    public static final String ACTION_GET_RESTRICTION_ENTRIES =
            "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
@@ -3639,6 +3663,15 @@ public class Intent implements Parcelable, Cloneable {
    public static final String EXTRA_CURRENT_PACKAGE_NAME =
            "com.cyanogenmod.intent.extra.CURRENT_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).
+19 −0
Original line number Diff line number Diff line
@@ -650,6 +650,11 @@ public class AudioRecord
                mRecordingState = RECORDSTATE_RECORDING;
            }
        }

        if (getRecordingState() == RECORDSTATE_RECORDING &&
                getAudioSource() == MediaRecorder.AudioSource.HOTWORD) {
            handleHotwordInput(true);
        }
    }

    /**
@@ -695,6 +700,10 @@ public class AudioRecord
            native_stop();
            mRecordingState = RECORDSTATE_STOPPED;
        }

        if (getAudioSource() == MediaRecorder.AudioSource.HOTWORD) {
            handleHotwordInput(false);
        }
    }

    private final IBinder mICallBack = new Binder();
@@ -711,6 +720,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
    //--------------------
+55 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL;
import static android.media.AudioManager.RINGER_MODE_SILENT;
import static android.media.AudioManager.RINGER_MODE_VIBRATE;

import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
@@ -412,6 +413,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
@@ -1455,6 +1465,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) {
@@ -1542,10 +1592,14 @@ 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);
        final long ident = Binder.clearCallingIdentity();
        try {
            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
            mContext.sendBroadcastAsUser(intent, UserHandle.ALL, receiverPermission);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
+4 −0
Original line number Diff line number Diff line
@@ -60,6 +60,10 @@ interface IAudioService {

    void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);

    void handleHotwordInput(boolean listening);

    String getCurrentHotwordInputPackageName();

    void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);

    boolean isMasterMute();