diff options
author | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-09 20:25:53 +0000 |
---|---|---|
committer | henrika@chromium.org <henrika@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-09 20:25:53 +0000 |
commit | 31d88a3e4311c9f075e0e24e186fa17d165feb75 (patch) | |
tree | 4fc8f7b322141d18904c575bfbb0f0dcc45a57c3 /media | |
parent | 28156413fcc8f1cee2fcd258a0370ce98f4c34b4 (diff) | |
download | chromium_src-31d88a3e4311c9f075e0e24e186fa17d165feb75.zip chromium_src-31d88a3e4311c9f075e0e24e186fa17d165feb75.tar.gz chromium_src-31d88a3e4311c9f075e0e24e186fa17d165feb75.tar.bz2 |
Improves audio support for case when process lacks MODIFY_AUDIO_SETTINGS and/or RECORD_AUDIO permission.
This CL ensures that a process can use the AudioManager for Android without MODIFY_AUDIO_SETTINGS and RECORD_AUDIO permissions without crashing. logcat warnings are added and getAudioInputDeviceNames() and setDevice() APIs are disabled unless we hold both permissions. As a result, no input streams can be created. Output streams can still be created, hence WebAudio should not be affected.
To summarize: AudioManager for Android will still work without all required permissions but with a limited functionality.
BUG=NONE
TEST=WebRTC demos (e.g. apprtc.com) with missing permissions.
Review URL: https://codereview.chromium.org/305483004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275850 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/android/audio_manager_android.cc | 5 | ||||
-rw-r--r-- | media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java | 87 |
2 files changed, 70 insertions, 22 deletions
diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc index 733ece6..48f203a 100644 --- a/media/audio/android/audio_manager_android.cc +++ b/media/audio/android/audio_manager_android.cc @@ -90,6 +90,11 @@ void AudioManagerAndroid::GetAudioInputDeviceNames( ScopedJavaLocalRef<jobjectArray> j_device_array = Java_AudioManagerAndroid_getAudioInputDeviceNames( env, j_audio_manager_.obj()); + if (j_device_array.is_null()) { + // Most probable reason for a NULL result here is that the process lacks + // MODIFY_AUDIO_SETTINGS or RECORD_AUDIO permissions. + return; + } jsize len = env->GetArrayLength(j_device_array.obj()); AudioDeviceName device; for (jsize i = 0; i < len; ++i) { diff --git a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java index e675883..85baad0 100644 --- a/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java +++ b/media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java @@ -163,6 +163,13 @@ class AudioManagerAndroid { private final Context mContext; private final long mNativeAudioManagerAndroid; + // Enabled during initialization if MODIFY_AUDIO_SETTINGS permission is + // granted. Required to shift system-wide audio settings. + private boolean mHasModifyAudioSettingsPermission = false; + + // Enabled during initialization if RECORD_AUDIO permission is granted. + private boolean mHasRecordAudioPermission = false; + // Enabled during initialization if BLUETOOTH permission is granted. private boolean mHasBluetoothPermission = false; @@ -237,6 +244,19 @@ class AudioManagerAndroid { if (mIsInitialized) return; + // Check if process has MODIFY_AUDIO_SETTINGS and RECORD_AUDIO + // permissions. Both are required for full functionality. + mHasModifyAudioSettingsPermission = hasPermission( + android.Manifest.permission.MODIFY_AUDIO_SETTINGS); + if (DEBUG && !mHasModifyAudioSettingsPermission) { + logd("MODIFY_AUDIO_SETTINGS permission is missing"); + } + mHasRecordAudioPermission = hasPermission( + android.Manifest.permission.RECORD_AUDIO); + if (DEBUG && !mHasRecordAudioPermission) { + logd("RECORD_AUDIO permission is missing"); + } + // Initialize audio device list with things we know is always available. mAudioDevices[DEVICE_EARPIECE] = hasEarpiece(); mAudioDevices[DEVICE_WIRED_HEADSET] = hasWiredHeadset(); @@ -277,14 +297,23 @@ class AudioManagerAndroid { * Saves current audio mode and sets audio mode to MODE_IN_COMMUNICATION * if input parameter is true. Restores saved audio mode if input parameter * is false. + * Required permission: android.Manifest.permission.MODIFY_AUDIO_SETTINGS. */ @CalledByNative private void setCommunicationAudioModeOn(boolean on) { if (DEBUG) logd("setCommunicationAudioModeOn(" + on + ")"); + // The MODIFY_AUDIO_SETTINGS permission is required to allow an + // application to modify global audio settings. + if (!mHasModifyAudioSettingsPermission) { + Log.w(TAG, "MODIFY_AUDIO_SETTINGS is missing => client will run " + + "with reduced functionality"); + return; + } + if (on) { if (mSavedAudioMode != AudioManager.MODE_INVALID) { - Log.wtf(TAG, "Audio mode has already been set!"); + Log.wtf(TAG, "Audio mode has already been set"); return; } @@ -318,7 +347,7 @@ class AudioManagerAndroid { } else { if (mSavedAudioMode == AudioManager.MODE_INVALID) { - Log.wtf(TAG, "Audio mode has not yet been set!"); + Log.wtf(TAG, "Audio mode has not yet been set"); return; } @@ -346,12 +375,20 @@ class AudioManagerAndroid { * @param deviceId Unique device ID (integer converted to string) * representing the selected device. This string is empty if the so-called * default device is requested. + * Required permissions: android.Manifest.permission.MODIFY_AUDIO_SETTINGS + * and android.Manifest.permission.RECORD_AUDIO. */ @CalledByNative private boolean setDevice(String deviceId) { if (DEBUG) logd("setDevice: " + deviceId); if (!mIsInitialized) return false; + if (!mHasModifyAudioSettingsPermission || !mHasRecordAudioPermission) { + Log.w(TAG, "Requires MODIFY_AUDIO_SETTINGS and RECORD_AUDIO"); + Log.w(TAG, "Selected device will not be available for recording"); + return false; + } + int intDeviceId = deviceId.isEmpty() ? DEVICE_DEFAULT : Integer.parseInt(deviceId); @@ -383,12 +420,20 @@ class AudioManagerAndroid { * @return the current list of available audio devices. * Note that this call does not trigger any update of the list of devices, * it only copies the current state in to the output array. + * Required permissions: android.Manifest.permission.MODIFY_AUDIO_SETTINGS + * and android.Manifest.permission.RECORD_AUDIO. */ @CalledByNative private AudioDeviceName[] getAudioInputDeviceNames() { if (DEBUG) logd("getAudioInputDeviceNames"); if (!mIsInitialized) return null; + if (!mHasModifyAudioSettingsPermission || !mHasRecordAudioPermission) { + Log.w(TAG, "Requires MODIFY_AUDIO_SETTINGS and RECORD_AUDIO"); + Log.w(TAG, "No audio device will be available for recording"); + return null; + } + boolean devices[] = null; synchronized (mLock) { devices = mAudioDevices.clone(); @@ -501,7 +546,7 @@ class AudioManagerAndroid { */ private void checkIfCalledOnValidThread() { if (DEBUG && !mNonThreadSafe.calledOnValidThread()) { - Log.wtf(TAG, "Method is not called on valid thread!"); + Log.wtf(TAG, "Method is not called on valid thread"); } } @@ -511,7 +556,8 @@ class AudioManagerAndroid { */ private void registerBluetoothIntentsIfNeeded() { // Check if this process has the BLUETOOTH permission or not. - mHasBluetoothPermission = hasBluetoothPermission(); + mHasBluetoothPermission = hasPermission( + android.Manifest.permission.BLUETOOTH); // Add a Bluetooth headset to the list of available devices if a BT // headset is detected and if we have the BLUETOOTH permission. @@ -520,6 +566,7 @@ class AudioManagerAndroid { // is not sticky and will only be received if a BT headset is connected // after this method has been called. if (!mHasBluetoothPermission) { + Log.w(TAG, "Requires BLUETOOTH permission"); return; } mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = hasBluetoothHeadset(); @@ -580,16 +627,12 @@ class AudioManagerAndroid { return mAudioManager.isWiredHeadsetOn(); } - /** Checks if the process has BLUETOOTH permission or not. */ - private boolean hasBluetoothPermission() { - boolean hasBluetooth = mContext.checkPermission( - android.Manifest.permission.BLUETOOTH, - Process.myPid(), - Process.myUid()) == PackageManager.PERMISSION_GRANTED; - if (DEBUG && !hasBluetooth) { - logd("BLUETOOTH permission is missing!"); - } - return hasBluetooth; + /** Checks if the process has as specified permission or not. */ + private boolean hasPermission(String permission) { + return mContext.checkPermission( + permission, + Process.myPid(), + Process.myUid()) == PackageManager.PERMISSION_GRANTED; } /** @@ -599,7 +642,7 @@ class AudioManagerAndroid { */ private boolean hasBluetoothHeadset() { if (!mHasBluetoothPermission) { - Log.wtf(TAG, "hasBluetoothHeadset() requires BLUETOOTH permission!"); + Log.w(TAG, "hasBluetoothHeadset() requires BLUETOOTH permission"); return false; } @@ -695,7 +738,7 @@ class AudioManagerAndroid { } break; default: - loge("Invalid state!"); + loge("Invalid state"); break; } @@ -770,7 +813,7 @@ class AudioManagerAndroid { // Bluetooth service is switching from on to off. break; default: - loge("Invalid state!"); + loge("Invalid state"); break; } @@ -824,7 +867,7 @@ class AudioManagerAndroid { // do nothing break; default: - loge("Invalid state!"); + loge("Invalid state"); } if (DEBUG) { reportUpdate(); @@ -875,7 +918,7 @@ class AudioManagerAndroid { } if (!mAudioManager.isBluetoothScoOn()) { // TODO(henrika): can we do anything else than logging here? - loge("Unable to stop BT SCO since it is already disabled!"); + loge("Unable to stop BT SCO since it is already disabled"); return; } @@ -913,7 +956,7 @@ class AudioManagerAndroid { setSpeakerphoneOn(false); break; default: - loge("Invalid audio device selection!"); + loge("Invalid audio device selection"); break; } reportUpdate(); @@ -955,7 +998,7 @@ class AudioManagerAndroid { devices = mAudioDevices.clone(); } if (requested == DEVICE_INVALID) { - loge("Unable to activate device since no device is selected!"); + loge("Unable to activate device since no device is selected"); return; } @@ -1045,7 +1088,7 @@ class AudioManagerAndroid { // Ensure that the observer is activated during communication mode. if (mAudioManager.getMode() != AudioManager.MODE_IN_COMMUNICATION) { - Log.wtf(TAG, "Only enable SettingsObserver in COMM mode!"); + Log.wtf(TAG, "Only enable SettingsObserver in COMM mode"); return; } |