diff options
author | John Spurlock <jspurlock@google.com> | 2014-05-20 16:25:37 -0400 |
---|---|---|
committer | John Spurlock <jspurlock@google.com> | 2014-05-21 09:58:41 -0400 |
commit | 3346a802087f621c6441bc512dfcc17b07143fc6 (patch) | |
tree | 8c912d2e1a6e350193ad8565cb7a5ad5957849b5 /media | |
parent | c84947db56ce9e6e11541f055a2cf23332552fb7 (diff) | |
download | frameworks_base-3346a802087f621c6441bc512dfcc17b07143fc6.zip frameworks_base-3346a802087f621c6441bc512dfcc17b07143fc6.tar.gz frameworks_base-3346a802087f621c6441bc512dfcc17b07143fc6.tar.bz2 |
VolumeZen: SystemUI now hosts the volume dialog.
- Allow SystemUI to set the volume controller interface using
a new binder call to audio service.
- Remove VolumePanel's dependency on AudioService.
- Host the base VolumePanel in the SystemUI process.
Change-Id: I095d5a1a579d42b68d0f81abb4087bd0c754b876
Diffstat (limited to 'media')
-rw-r--r-- | media/java/android/media/AudioManager.java | 78 | ||||
-rw-r--r-- | media/java/android/media/AudioService.java | 71 | ||||
-rw-r--r-- | media/java/android/media/IAudioService.aidl | 7 | ||||
-rw-r--r-- | media/java/android/media/IVolumeController.aidl | 42 | ||||
-rw-r--r-- | media/java/android/media/VolumeController.java | 118 |
5 files changed, 297 insertions, 19 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 575667d..9803161 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -37,7 +37,6 @@ import android.os.ServiceManager; import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; -import android.view.VolumePanel; import java.util.HashMap; @@ -498,7 +497,7 @@ public class AudioManager { int keyCode = event.getKeyCode(); if (keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYCODE_VOLUME_UP && keyCode != KeyEvent.KEYCODE_VOLUME_MUTE - && mVolumeKeyUpTime + VolumePanel.PLAY_SOUND_DELAY + && mVolumeKeyUpTime + AudioService.PLAY_SOUND_DELAY > SystemClock.uptimeMillis()) { /* * The user has hit another key during the delay (e.g., 300ms) @@ -2892,4 +2891,79 @@ public class AudioManager { return AudioSystem.getOutputLatency(streamType); } + /** + * Registers a global volume controller interface. Currently limited to SystemUI. + * + * @hide + */ + public void setVolumeController(IVolumeController controller) { + try { + getService().setVolumeController(controller); + } catch (RemoteException e) { + Log.w(TAG, "Error setting volume controller", e); + } + } + + /** + * Only useful for volume controllers. + * @hide + */ + public int getRemoteStreamVolume() { + try { + return getService().getRemoteStreamVolume(); + } catch (RemoteException e) { + Log.w(TAG, "Error getting remote stream volume", e); + return 0; + } + } + + /** + * Only useful for volume controllers. + * @hide + */ + public int getRemoteStreamMaxVolume() { + try { + return getService().getRemoteStreamMaxVolume(); + } catch (RemoteException e) { + Log.w(TAG, "Error getting remote stream max volume", e); + return 0; + } + } + + /** + * Only useful for volume controllers. + * @hide + */ + public void setRemoteStreamVolume(int index) { + try { + getService().setRemoteStreamVolume(index); + } catch (RemoteException e) { + Log.w(TAG, "Error setting remote stream volume", e); + } + } + + /** + * Only useful for volume controllers. + * @hide + */ + public boolean isStreamAffectedByRingerMode(int streamType) { + try { + return getService().isStreamAffectedByRingerMode(streamType); + } catch (RemoteException e) { + Log.w(TAG, "Error calling isStreamAffectedByRingerMode", e); + return false; + } + } + + /** + * Only useful for volume controllers. + * @hide + */ + public void disableSafeMediaVolume() { + try { + getService().disableSafeMediaVolume(); + } catch (RemoteException e) { + Log.w(TAG, "Error disabling safe media volume", e); + } + } } diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 6e623a5..c736fc7 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -67,7 +67,6 @@ import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; import android.view.Surface; -import android.view.VolumePanel; import android.view.WindowManager; import com.android.internal.telephony.ITelephony; @@ -116,13 +115,21 @@ public class AudioService extends IAudioService.Stub { /** How long to delay before persisting a change in volume/ringer mode. */ private static final int PERSIST_DELAY = 500; + /** + * The delay before playing a sound. This small period exists so the user + * can press another key (non-volume keys, too) to have it NOT be audible. + * <p> + * PhoneWindow will implement this part. + */ + public static final int PLAY_SOUND_DELAY = 300; + private final Context mContext; private final ContentResolver mContentResolver; private final AppOpsManager mAppOps; private final boolean mVoiceCapable; - /** The UI */ - private VolumePanel mVolumePanel; + /** The controller for the volume UI. */ + private final VolumeController mVolumeController = new VolumeController(); // sendMsg() flags /** If the msg is already queued, replace it with this one. */ @@ -477,13 +484,12 @@ public class AudioService extends IAudioService.Stub { sSoundEffectVolumeDb = context.getResources().getInteger( com.android.internal.R.integer.config_soundEffectVolumeDb); - mVolumePanel = new VolumePanel(context, this); mForcedUseForComm = AudioSystem.FORCE_NONE; createAudioSystemThread(); mMediaFocusControl = new MediaFocusControl(mAudioHandler.getLooper(), - mContext, /*VolumeController*/ mVolumePanel, this); + mContext, mVolumeController, this); AudioSystem.setErrorCallback(mAudioSystemCallback); @@ -953,7 +959,7 @@ public class AudioService extends IAudioService.Stub { if ((direction == AudioManager.ADJUST_RAISE) && !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) { Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex); - mVolumePanel.postDisplaySafeVolumeWarning(flags); + mVolumeController.postDisplaySafeVolumeWarning(flags); } else if (streamState.adjustIndex(direction * step, device)) { // Post message to set system volume (it in turn will post a message // to persist). Do not change volume if stream is muted. @@ -1081,7 +1087,7 @@ public class AudioService extends IAudioService.Stub { } if (!checkSafeMediaVolume(streamTypeAlias, index, device)) { - mVolumePanel.postDisplaySafeVolumeWarning(flags); + mVolumeController.postDisplaySafeVolumeWarning(flags); mPendingVolumeCommand = new StreamVolumeCommand( streamType, index, flags, device); } else { @@ -1202,7 +1208,7 @@ public class AudioService extends IAudioService.Stub { streamType = AudioSystem.STREAM_NOTIFICATION; } - mVolumePanel.postVolumeChanged(streamType, flags); + mVolumeController.postVolumeChanged(streamType, flags); if ((flags & AudioManager.FLAG_FIXED_VOLUME) == 0) { oldIndex = (oldIndex + 5) / 10; @@ -1217,7 +1223,7 @@ public class AudioService extends IAudioService.Stub { // UI update and Broadcast Intent private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) { - mVolumePanel.postMasterVolumeChanged(flags); + mVolumeController.postMasterVolumeChanged(flags); Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION); intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume); @@ -1227,7 +1233,7 @@ public class AudioService extends IAudioService.Stub { // UI update and Broadcast Intent private void sendMasterMuteUpdate(boolean muted, int flags) { - mVolumePanel.postMasterMuteChanged(flags); + mVolumeController.postMasterMuteChanged(flags); broadcastMasterMuteStatus(muted); } @@ -2589,6 +2595,7 @@ public class AudioService extends IAudioService.Stub { return adjustVolumeIndex; } + @Override public boolean isStreamAffectedByRingerMode(int streamType) { return (mRingerModeAffectedStreams & (1 << streamType)) != 0; } @@ -4309,15 +4316,19 @@ public class AudioService extends IAudioService.Stub { mMediaFocusControl.registerRemoteVolumeObserverForRcc(rccId, rvo); } + @Override public int getRemoteStreamVolume() { return mMediaFocusControl.getRemoteStreamVolume(); } + @Override public int getRemoteStreamMaxVolume() { return mMediaFocusControl.getRemoteStreamMaxVolume(); } + @Override public void setRemoteStreamVolume(int index) { + enforceSelfOrSystemUI("set the remote stream volume"); mMediaFocusControl.setRemoteStreamVolume(index); } @@ -4450,7 +4461,7 @@ public class AudioService extends IAudioService.Stub { } } } - mVolumePanel.setLayoutDirection(config.getLayoutDirection()); + mVolumeController.setLayoutDirection(config.getLayoutDirection()); } catch (Exception e) { Log.e(TAG, "Error handling configuration change: ", e); } @@ -4625,7 +4636,9 @@ public class AudioService extends IAudioService.Stub { } } + @Override public void disableSafeMediaVolume() { + enforceSelfOrSystemUI("disable the safe media volume"); synchronized (mSafeMediaVolumeState) { setSafeMediaVolumeEnabled(false); if (mPendingVolumeCommand != null) { @@ -4681,6 +4694,7 @@ public class AudioService extends IAudioService.Stub { pw.println("\nAudio routes:"); pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType)); pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName); + pw.print(" mVolumeController="); pw.println(mVolumeController); } // Inform AudioFlinger of our device's low RAM attribute @@ -4691,4 +4705,39 @@ public class AudioService extends IAudioService.Stub { Log.w(TAG, "AudioFlinger informed of device's low RAM attribute; status " + status); } } + + private void enforceSelfOrSystemUI(String action) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE, + "Only SystemUI can " + action); + } + + @Override + public void setVolumeController(final IVolumeController controller) { + enforceSelfOrSystemUI("set the volume controller"); + + // return early if things are not actually changing + if (mVolumeController.isSameBinder(controller)) { + return; + } + + // dismiss the old volume controller + mVolumeController.postDismiss(); + if (controller != null) { + // we are about to register a new controller, listen for its death + try { + controller.asBinder().linkToDeath(new DeathRecipient() { + @Override + public void binderDied() { + if (mVolumeController.isSameBinder(controller)) { + Log.w(TAG, "Current remote volume controller died, unregistering"); + setVolumeController(null); + } + } + }, 0); + } catch (RemoteException e) { + // noop + } + } + mVolumeController.setController(controller); + } } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 2f08325..30de4f9 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -26,6 +26,7 @@ import android.media.IRemoteControlClient; import android.media.IRemoteControlDisplay; import android.media.IRemoteVolumeObserver; import android.media.IRingtonePlayer; +import android.media.IVolumeController; import android.media.Rating; import android.net.Uri; import android.view.KeyEvent; @@ -236,4 +237,10 @@ interface IAudioService { AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); boolean isCameraSoundForced(); + + void setVolumeController(in IVolumeController controller); + + boolean isStreamAffectedByRingerMode(int streamType); + + void disableSafeMediaVolume(); } diff --git a/media/java/android/media/IVolumeController.aidl b/media/java/android/media/IVolumeController.aidl new file mode 100644 index 0000000..35d7708 --- /dev/null +++ b/media/java/android/media/IVolumeController.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + + +/** + * AIDL for the AudioService to report interesting events to a remote volume control dialog. + * @hide + */ +oneway interface IVolumeController { + void hasNewRemotePlaybackInfo(); + + void remoteVolumeChanged(int streamType, int flags); + + void remoteSliderVisibility(boolean visible); + + void displaySafeVolumeWarning(int flags); + + void volumeChanged(int streamType, int flags); + + void masterVolumeChanged(int flags); + + void masterMuteChanged(int flags); + + void setLayoutDirection(int layoutDirection); + + void dismiss(); +} diff --git a/media/java/android/media/VolumeController.java b/media/java/android/media/VolumeController.java index 2d12bf2..6b70cc3 100644 --- a/media/java/android/media/VolumeController.java +++ b/media/java/android/media/VolumeController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 The Android Open Source Project + * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,120 @@ package android.media; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import java.util.Objects; + /** + * Wraps the remote volume controller interface as a convenience to audio service. * @hide */ -public interface VolumeController { +public class VolumeController { + private static final String TAG = "VolumeController"; + + private IVolumeController mController; + + public void setController(IVolumeController controller) { + mController = controller; + } + + public boolean isSameBinder(IVolumeController controller) { + return Objects.equals(asBinder(), binder(controller)); + } + + public IBinder asBinder() { + return binder(mController); + } + + private static IBinder binder(IVolumeController controller) { + return controller == null ? null : controller.asBinder(); + } + + @Override + public String toString() { + return "VolumeController(" + asBinder() + ")"; + } + + public void postHasNewRemotePlaybackInfo() { + if (mController == null) return; + try { + mController.hasNewRemotePlaybackInfo(); + } catch (RemoteException e) { + Log.w(TAG, "Error calling hasNewRemotePlaybackInfo", e); + } + } + + public void postRemoteVolumeChanged(int streamType, int flags) { + if (mController == null) return; + try { + mController.remoteVolumeChanged(streamType, flags); + } catch (RemoteException e) { + Log.w(TAG, "Error calling remoteVolumeChanged", e); + } + } + + public void postRemoteSliderVisibility(boolean visible) { + if (mController == null) return; + try { + mController.remoteSliderVisibility(visible); + } catch (RemoteException e) { + Log.w(TAG, "Error calling remoteSliderVisibility", e); + } + } + + public void postDisplaySafeVolumeWarning(int flags) { + if (mController == null) return; + try { + mController.displaySafeVolumeWarning(flags); + } catch (RemoteException e) { + Log.w(TAG, "Error calling displaySafeVolumeWarning", e); + } + } + + public void postVolumeChanged(int streamType, int flags) { + if (mController == null) return; + try { + mController.volumeChanged(streamType, flags); + } catch (RemoteException e) { + Log.w(TAG, "Error calling volumeChanged", e); + } + } + + public void postMasterVolumeChanged(int flags) { + if (mController == null) return; + try { + mController.masterVolumeChanged(flags); + } catch (RemoteException e) { + Log.w(TAG, "Error calling masterVolumeChanged", e); + } + } - public void postHasNewRemotePlaybackInfo(); + public void postMasterMuteChanged(int flags) { + if (mController == null) return; + try { + mController.masterMuteChanged(flags); + } catch (RemoteException e) { + Log.w(TAG, "Error calling masterMuteChanged", e); + } + } - public void postRemoteVolumeChanged(int streamType, int flags); + public void setLayoutDirection(int layoutDirection) { + if (mController == null) return; + try { + mController.setLayoutDirection(layoutDirection); + } catch (RemoteException e) { + Log.w(TAG, "Error calling setLayoutDirection", e); + } + } - public void postRemoteSliderVisibility(boolean visible); -} + public void postDismiss() { + if (mController == null) return; + try { + mController.dismiss(); + } catch (RemoteException e) { + Log.w(TAG, "Error calling dismiss", e); + } + } +}
\ No newline at end of file |