summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorJohn Spurlock <jspurlock@google.com>2014-05-20 16:25:37 -0400
committerJohn Spurlock <jspurlock@google.com>2014-05-21 09:58:41 -0400
commit3346a802087f621c6441bc512dfcc17b07143fc6 (patch)
tree8c912d2e1a6e350193ad8565cb7a5ad5957849b5 /media
parentc84947db56ce9e6e11541f055a2cf23332552fb7 (diff)
downloadframeworks_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.java78
-rw-r--r--media/java/android/media/AudioService.java71
-rw-r--r--media/java/android/media/IAudioService.aidl7
-rw-r--r--media/java/android/media/IVolumeController.aidl42
-rw-r--r--media/java/android/media/VolumeController.java118
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