am 6f1bd261: am 9c0a1003: Merge "Fix issue 3371080" into honeycomb

* commit '6f1bd261b7fd86ac7817ca061dfb55b95150b836':
  Fix issue 3371080
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 40b9334..5147cfa 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -64,10 +64,10 @@
 }
 
 static jboolean
-android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream)
+android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream, jint inPastMs)
 {
     bool state = false;
-    AudioSystem::isStreamActive(stream, &state);
+    AudioSystem::isStreamActive(stream, &state, inPastMs);
     return state;
 }
 
@@ -199,7 +199,7 @@
     {"getParameters",        "(Ljava/lang/String;)Ljava/lang/String;", (void *)android_media_AudioSystem_getParameters},
     {"muteMicrophone",      "(Z)I",     (void *)android_media_AudioSystem_muteMicrophone},
     {"isMicrophoneMuted",   "()Z",      (void *)android_media_AudioSystem_isMicrophoneMuted},
-    {"isStreamActive",      "(I)Z",     (void *)android_media_AudioSystem_isStreamActive},
+    {"isStreamActive",      "(II)Z",     (void *)android_media_AudioSystem_isStreamActive},
     {"setDeviceConnectionState", "(IILjava/lang/String;)I", (void *)android_media_AudioSystem_setDeviceConnectionState},
     {"getDeviceConnectionState", "(ILjava/lang/String;)I",  (void *)android_media_AudioSystem_getDeviceConnectionState},
     {"setPhoneState",       "(I)I",     (void *)android_media_AudioSystem_setPhoneState},
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 1e29d82..03f8944 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -204,8 +204,9 @@
     // set audio mode in audio hardware (see AudioSystem::audio_mode)
     static status_t setMode(int mode);
 
-    // returns true in *state if tracks are active on the specified stream
-    static status_t isStreamActive(int stream, bool *state);
+    // returns true in *state if tracks are active on the specified stream or has been active
+    // in the past inPastMs milliseconds
+    static status_t isStreamActive(int stream, bool *state, uint32_t inPastMs = 0);
 
     // set/get audio hardware parameters. The function accepts a list of parameters
     // key value pairs in the form: key1=value1;key2=value2;...
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index 70e505e..589f7cd 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -102,9 +102,6 @@
     virtual     status_t    setMicMute(bool state) = 0;
     virtual     bool        getMicMute() const = 0;
 
-    // is any track active on this stream?
-    virtual     bool        isStreamActive(int stream) const = 0;
-
     virtual     status_t    setParameters(int ioHandle, const String8& keyValuePairs) = 0;
     virtual     String8     getParameters(int ioHandle, const String8& keys) = 0;
 
diff --git a/include/media/IAudioPolicyService.h b/include/media/IAudioPolicyService.h
index 49eee59..5afceaa 100644
--- a/include/media/IAudioPolicyService.h
+++ b/include/media/IAudioPolicyService.h
@@ -81,6 +81,7 @@
                                     int session,
                                     int id) = 0;
     virtual status_t unregisterEffect(int id) = 0;
+    virtual bool     isStreamActive(int stream, uint32_t inPastMs = 0) const = 0;
 };
 
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 9058a7b..051a0fc 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -394,10 +394,10 @@
                  */
                 adjustSuggestedStreamVolume(
                         keyCode == KeyEvent.KEYCODE_VOLUME_UP
-                                ? AudioManager.ADJUST_RAISE
-                                : AudioManager.ADJUST_LOWER,
+                                ? ADJUST_RAISE
+                                : ADJUST_LOWER,
                         stream,
-                        AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
+                        FLAG_SHOW_UI | FLAG_VIBRATE);
                 break;
             case KeyEvent.KEYCODE_VOLUME_MUTE:
                 // TODO: Actually handle MUTE.
@@ -416,7 +416,11 @@
                  * Play a sound. This is done on key up since we don't want the
                  * sound to play when a user holds down volume down to mute.
                  */
-                adjustSuggestedStreamVolume(ADJUST_SAME, stream, FLAG_PLAY_SOUND);
+                adjustSuggestedStreamVolume(
+                        ADJUST_SAME,
+                        stream,
+                        FLAG_PLAY_SOUND);
+
                 mVolumeKeyUpTime = SystemClock.uptimeMillis();
                 break;
             case KeyEvent.KEYCODE_VOLUME_MUTE:
@@ -555,6 +559,21 @@
     }
 
     /**
+     * Get last audible volume before stream was muted.
+     *
+     * @hide
+     */
+    public int getLastAudibleStreamVolume(int streamType) {
+        IAudioService service = getService();
+        try {
+            return service.getLastAudibleStreamVolume(streamType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in getLastAudibleStreamVolume", e);
+            return 0;
+        }
+    }
+
+    /**
      * Sets the ringer mode.
      * <p>
      * Silent mode will mute the volume and will not vibrate. Vibrate mode will
@@ -649,6 +668,21 @@
     }
 
     /**
+     * get stream mute state.
+     *
+     * @hide
+     */
+    public boolean isStreamMute(int streamType) {
+        IAudioService service = getService();
+        try {
+            return service.isStreamMute(streamType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in isStreamMute", e);
+            return false;
+        }
+    }
+
+    /**
      * Returns whether a particular type should vibrate according to user
      * settings and the current ringer mode.
      * <p>
@@ -1124,7 +1158,7 @@
      * @return true if any music tracks are active.
      */
     public boolean isMusicActive() {
-        return AudioSystem.isStreamActive(STREAM_MUSIC);
+        return AudioSystem.isStreamActive(STREAM_MUSIC, 0);
     }
 
     /*
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index cbc7529..e18220a 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -284,6 +284,15 @@
     private SoundPoolListenerThread mSoundPoolListenerThread;
     // message looper for SoundPool listener
     private Looper mSoundPoolLooper = null;
+    // default volume applied to sound played with playSoundEffect()
+    private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20;
+    // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume
+    private int SOUND_EFFECT_VOLUME_DB;
+    // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
+    // stopped
+    private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
+    // previous volume adjustment direction received by checkForRingerModeChange()
+    private int mPrevVolDirection = AudioManager.ADJUST_SAME;
 
     ///////////////////////////////////////////////////////////////////////////
     // Construction
@@ -301,6 +310,10 @@
             "ro.config.vc_call_vol_steps",
            MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
 
+        SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt(
+                "ro.config.sound_fx_volume",
+                SOUND_EFFECT_DEFAULT_VOLUME_DB);
+
         mVolumePanel = new VolumePanel(context, this);
         mSettingsObserver = new SettingsObserver();
         mForcedUseForComm = AudioSystem.FORCE_NONE;
@@ -401,14 +414,19 @@
         mRingerModeAffectedStreams = Settings.System.getInt(cr,
                 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
                 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
-                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
+                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)|
+                 (1 << AudioSystem.STREAM_MUSIC)));
 
         mMuteAffectedStreams = System.getInt(cr,
                 System.MUTE_STREAMS_AFFECTED,
                 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
 
-        mNotificationsUseRingVolume = System.getInt(cr,
-                Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
+        if (mVoiceCapable) {
+            mNotificationsUseRingVolume = System.getInt(cr,
+                    Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
+        } else {
+            mNotificationsUseRingVolume = 1;
+        }
 
         if (mNotificationsUseRingVolume == 1) {
             STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
@@ -465,8 +483,10 @@
 
         // If either the client forces allowing ringer modes for this adjustment,
         // or the stream type is one that is affected by ringer modes
-        if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
-                || streamType == AudioSystem.STREAM_RING) {
+        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+             (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL &&
+               streamType != AudioSystem.STREAM_BLUETOOTH_SCO) ||
+                (mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
             // Check if the ringer mode changes with this volume adjustment. If
             // it does, it will handle adjusting the volume, so we won't below
             adjustVolume = checkForRingerModeChange(oldIndex, direction);
@@ -491,10 +511,8 @@
             }
             index = streamState.mIndex;
         }
-        // UI
-        mVolumePanel.postVolumeChanged(streamType, flags);
-        // Broadcast Intent
-        sendVolumeUpdate(streamType, oldIndex, index);
+
+        sendVolumeUpdate(streamType, oldIndex, index, flags);
     }
 
     /** @see AudioManager#setStreamVolume(int, int, int) */
@@ -509,21 +527,23 @@
 
         index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
 
-        // UI, etc.
-        mVolumePanel.postVolumeChanged(streamType, flags);
-        // Broadcast Intent
-        sendVolumeUpdate(streamType, oldIndex, index);
+        sendVolumeUpdate(streamType, oldIndex, index, flags);
     }
 
-    private void sendVolumeUpdate(int streamType, int oldIndex, int index) {
+    // UI update and Broadcast Intent
+    private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
+        if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
+            streamType = AudioSystem.STREAM_NOTIFICATION;
+        }
+
+        mVolumePanel.postVolumeChanged(streamType, flags);
+
         oldIndex = (oldIndex + 5) / 10;
         index = (index + 5) / 10;
-
         Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
         intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
-
         mContext.sendBroadcast(intent);
     }
 
@@ -575,6 +595,11 @@
         }
     }
 
+    /** get stream mute state. */
+    public boolean isStreamMute(int streamType) {
+        return (mStreamStates[streamType].muteCount() != 0);
+    }
+
     /** @see AudioManager#getStreamVolume(int) */
     public int getStreamVolume(int streamType) {
         ensureValidStreamType(streamType);
@@ -587,6 +612,13 @@
         return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
     }
 
+
+    /** Get last audible volume before stream was muted. */
+    public int getLastAudibleStreamVolume(int streamType) {
+        ensureValidStreamType(streamType);
+        return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10;
+    }
+
     /** @see AudioManager#getRingerMode() */
     public int getRingerMode() {
         return mRingerMode;
@@ -1383,8 +1415,9 @@
 
         if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
             // audible mode, at the bottom of the scale
-            if (direction == AudioManager.ADJUST_LOWER
-                    && (oldIndex + 5) / 10 == 1) {
+            if ((direction == AudioManager.ADJUST_LOWER &&
+                 mPrevVolDirection != AudioManager.ADJUST_LOWER) &&
+                ((oldIndex + 5) / 10 == 0)) {
                 // "silent mode", but which one?
                 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
                     ? AudioManager.RINGER_MODE_VIBRATE
@@ -1411,6 +1444,8 @@
             adjustVolumeIndex = false;
         }
 
+        mPrevVolDirection = direction;
+
         return adjustVolumeIndex;
     }
 
@@ -1439,36 +1474,61 @@
     }
 
     private int getActiveStreamType(int suggestedStreamType) {
-        boolean isOffhook = false;
-        try {
-            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
-            if (phone != null) isOffhook = phone.isOffhook();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Couldn't connect to phone service", e);
-        }
 
-        if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
-            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
-            return AudioSystem.STREAM_BLUETOOTH_SCO;
-        } else if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) {
-            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
-            return AudioSystem.STREAM_VOICE_CALL;
-        } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC)) {
-            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
-            return AudioSystem.STREAM_MUSIC;
-        } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-            if (mVoiceCapable) {
+        if (mVoiceCapable) {
+            boolean isOffhook = false;
+            try {
+                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
+                if (phone != null) isOffhook = phone.isOffhook();
+            } catch (RemoteException e) {
+                Log.w(TAG, "Couldn't connect to phone service", e);
+            }
+
+            if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) {
+                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+                        == AudioSystem.FORCE_BT_SCO) {
+                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
+                    return AudioSystem.STREAM_BLUETOOTH_SCO;
+                } else {
+                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
+                    return AudioSystem.STREAM_VOICE_CALL;
+                }
+            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
+                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
+                return AudioSystem.STREAM_MUSIC;
+            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
                 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
                 //        + " b/c USE_DEFAULT_STREAM_TYPE...");
                 return AudioSystem.STREAM_RING;
             } else {
+                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
+                return suggestedStreamType;
+            }
+        } else {
+            if (getMode() == AudioManager.MODE_IN_COMMUNICATION) {
+                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
+                        == AudioSystem.FORCE_BT_SCO) {
+                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
+                    return AudioSystem.STREAM_BLUETOOTH_SCO;
+                } else {
+                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
+                    return AudioSystem.STREAM_VOICE_CALL;
+                }
+            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
+                            NOTIFICATION_VOLUME_DELAY_MS) ||
+                       AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
+                            NOTIFICATION_VOLUME_DELAY_MS)) {
+                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
+                return AudioSystem.STREAM_NOTIFICATION;
+            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
+                       (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
                 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
                 //        + " b/c USE_DEFAULT_STREAM_TYPE...");
                 return AudioSystem.STREAM_MUSIC;
+            } else {
+                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
+                return suggestedStreamType;
             }
-        } else {
-            // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
-            return suggestedStreamType;
         }
     }
 
@@ -1801,13 +1861,9 @@
                     return;
                 }
                 float volFloat;
-                // use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller
+                // use default if volume is not specified by caller
                 if (volume < 0) {
-                    // Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp)
-                    float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
-                    int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10;
-                    float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
-                    volFloat = (float)Math.pow(10, (musicVoldB - 3)/20);
+                    volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
                 } else {
                     volFloat = (float) volume / 1000.0f;
                 }
@@ -1884,7 +1940,7 @@
                     // Force creation of new IAudioflinger interface
                     if (!mMediaServerOk) {
                         Log.e(TAG, "Media server died.");
-                        AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC);
+                        AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0);
                         sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
                                 null, 500);
                     }
@@ -1981,21 +2037,23 @@
                 int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
                         Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
                         1);
-                if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
-                    mNotificationsUseRingVolume = notificationsUseRingVolume;
-                    if (mNotificationsUseRingVolume == 1) {
-                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
-                        mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
-                                System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]);
-                    } else {
-                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
-                        mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
-                                System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]);
-                        // Persist notification volume volume as it was not persisted while aliased to ring volume
-                        //  and persist with no delay as there might be registered observers of the persisted
-                        //  notification volume.
-                        sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
-                                SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
+                if (mVoiceCapable) {
+                    if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
+                        mNotificationsUseRingVolume = notificationsUseRingVolume;
+                        if (mNotificationsUseRingVolume == 1) {
+                            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
+                            mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
+                                    System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]);
+                        } else {
+                            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
+                            mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
+                                    System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]);
+                            // Persist notification volume volume as it was not persisted while aliased to ring volume
+                            //  and persist with no delay as there might be registered observers of the persisted
+                            //  notification volume.
+                            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
+                                    SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
+                        }
                     }
                 }
             }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 1fd03dc..e20bb25 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -158,7 +158,7 @@
      *
      * return true if any track playing on this stream is active.
      */
-    public static native boolean isStreamActive(int stream);
+    public static native boolean isStreamActive(int stream, int inPastMs);
 
     /*
      * Sets a group generic audio configuration parameters. The use of these parameters
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 384b8da..e3bd7b4 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -35,11 +35,15 @@
     void setStreamSolo(int streamType, boolean state, IBinder cb);
    	
     void setStreamMute(int streamType, boolean state, IBinder cb);
-    
+
+    boolean isStreamMute(int streamType);
+
     int getStreamVolume(int streamType);
     
     int getStreamMaxVolume(int streamType);
     
+    int getLastAudibleStreamVolume(int streamType);
+
     void setRingerMode(int ringerMode);
     
     int getRingerMode();
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 1a3fcd6..9d9b3c0 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -169,15 +169,6 @@
     return af->setMode(mode);
 }
 
-
-status_t AudioSystem::isStreamActive(int stream, bool* state) {
-    const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
-    if (af == 0) return PERMISSION_DENIED;
-    *state = af->isStreamActive(stream);
-    return NO_ERROR;
-}
-
-
 status_t AudioSystem::setParameters(audio_io_handle_t ioHandle, const String8& keyValuePairs) {
     const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
     if (af == 0) return PERMISSION_DENIED;
@@ -702,6 +693,14 @@
     return aps->unregisterEffect(id);
 }
 
+status_t AudioSystem::isStreamActive(int stream, bool* state, uint32_t inPastMs) {
+    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
+    if (aps == 0) return PERMISSION_DENIED;
+    *state = aps->isStreamActive(stream, inPastMs);
+    return NO_ERROR;
+}
+
+
 // ---------------------------------------------------------------------------
 
 void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who) {
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 3a89e25..eec47c0 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -47,7 +47,6 @@
     SET_MODE,
     SET_MIC_MUTE,
     GET_MIC_MUTE,
-    IS_STREAM_ACTIVE,
     SET_PARAMETERS,
     GET_PARAMETERS,
     REGISTER_CLIENT,
@@ -316,15 +315,6 @@
         return reply.readInt32();
     }
 
-    virtual bool isStreamActive(int stream) const
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
-        data.writeInt32(stream);
-        remote()->transact(IS_STREAM_ACTIVE, data, &reply);
-        return reply.readInt32();
-    }
-
     virtual status_t setParameters(int ioHandle, const String8& keyValuePairs)
     {
         Parcel data, reply;
@@ -826,12 +816,6 @@
             reply->writeInt32( getMicMute() );
             return NO_ERROR;
         } break;
-        case IS_STREAM_ACTIVE: {
-            CHECK_INTERFACE(IAudioFlinger, data, reply);
-            int stream = data.readInt32();
-            reply->writeInt32( isStreamActive(stream) );
-            return NO_ERROR;
-        } break;
         case SET_PARAMETERS: {
             CHECK_INTERFACE(IAudioFlinger, data, reply);
             int ioHandle = data.readInt32();
diff --git a/media/libmedia/IAudioPolicyService.cpp b/media/libmedia/IAudioPolicyService.cpp
index 950c213..457f7ed 100644
--- a/media/libmedia/IAudioPolicyService.cpp
+++ b/media/libmedia/IAudioPolicyService.cpp
@@ -48,7 +48,8 @@
     GET_STRATEGY_FOR_STREAM,
     GET_OUTPUT_FOR_EFFECT,
     REGISTER_EFFECT,
-    UNREGISTER_EFFECT
+    UNREGISTER_EFFECT,
+    IS_STREAM_ACTIVE
 };
 
 class BpAudioPolicyService : public BpInterface<IAudioPolicyService>
@@ -297,6 +298,15 @@
         return static_cast <status_t> (reply.readInt32());
     }
 
+    virtual bool isStreamActive(int stream, uint32_t inPastMs) const
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IAudioPolicyService::getInterfaceDescriptor());
+        data.writeInt32(stream);
+        data.writeInt32(inPastMs);
+        remote()->transact(IS_STREAM_ACTIVE, data, &reply);
+        return reply.readInt32();
+    }
 };
 
 IMPLEMENT_META_INTERFACE(AudioPolicyService, "android.media.IAudioPolicyService");
@@ -517,6 +527,14 @@
             return NO_ERROR;
         } break;
 
+        case IS_STREAM_ACTIVE: {
+            CHECK_INTERFACE(IAudioPolicyService, data, reply);
+            int stream = data.readInt32();
+            uint32_t inPastMs = (uint32_t)data.readInt32();
+            reply->writeInt32( isStreamActive(stream, inPastMs) );
+            return NO_ERROR;
+        } break;
+
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 49b71e2..f336f06 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -61,7 +61,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 63;
+    private static final int DATABASE_VERSION = 64;
 
     private Context mContext;
 
@@ -797,6 +797,28 @@
             upgradeVersion = 63;
         }
 
+        if (upgradeVersion == 63) {
+            // This upgrade adds the STREAM_MUSIC type to the list of
+             // types affected by ringer modes (silent, vibrate, etc.)
+             db.beginTransaction();
+             try {
+                 db.execSQL("DELETE FROM system WHERE name='"
+                         + Settings.System.MODE_RINGER_STREAMS_AFFECTED + "'");
+                 int newValue = (1 << AudioManager.STREAM_RING)
+                         | (1 << AudioManager.STREAM_NOTIFICATION)
+                         | (1 << AudioManager.STREAM_SYSTEM)
+                         | (1 << AudioManager.STREAM_SYSTEM_ENFORCED)
+                         | (1 << AudioManager.STREAM_MUSIC);
+                 db.execSQL("INSERT INTO system ('name', 'value') values ('"
+                         + Settings.System.MODE_RINGER_STREAMS_AFFECTED + "', '"
+                         + String.valueOf(newValue) + "')");
+                 db.setTransactionSuccessful();
+             } finally {
+                 db.endTransaction();
+             }
+             upgradeVersion = 64;
+         }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -1057,10 +1079,11 @@
     
             loadVibrateSetting(db, false);
     
-            // By default, only the ring/notification and system streams are affected
+            // By default, only the ring/notification, system and music streams are affected
             loadSetting(stmt, Settings.System.MODE_RINGER_STREAMS_AFFECTED,
                     (1 << AudioManager.STREAM_RING) | (1 << AudioManager.STREAM_NOTIFICATION) |
-                    (1 << AudioManager.STREAM_SYSTEM) | (1 << AudioManager.STREAM_SYSTEM_ENFORCED));
+                    (1 << AudioManager.STREAM_SYSTEM) | (1 << AudioManager.STREAM_SYSTEM_ENFORCED) |
+                    (1 << AudioManager.STREAM_MUSIC));
     
             loadSetting(stmt, Settings.System.MUTE_STREAMS_AFFECTED,
                     ((1 << AudioManager.STREAM_MUSIC) |
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 11ad4e4..4ec16c1 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -624,17 +624,6 @@
     return mStreamTypes[stream].mute;
 }
 
-bool AudioFlinger::isStreamActive(int stream) const
-{
-    Mutex::Autolock _l(mLock);
-    for (uint32_t i = 0; i < mPlaybackThreads.size(); i++) {
-        if (mPlaybackThreads.valueAt(i)->isStreamActive(stream)) {
-            return true;
-        }
-    }
-    return false;
-}
-
 status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs)
 {
     status_t result;
@@ -1291,20 +1280,6 @@
     return mStreamTypes[stream].mute;
 }
 
-bool AudioFlinger::PlaybackThread::isStreamActive(int stream) const
-{
-    Mutex::Autolock _l(mLock);
-    size_t count = mActiveTracks.size();
-    for (size_t i = 0 ; i < count ; ++i) {
-        sp<Track> t = mActiveTracks[i].promote();
-        if (t == 0) continue;
-        Track* const track = t.get();
-        if (t->type() == stream)
-            return true;
-    }
-    return false;
-}
-
 // addTrack_l() must be called with ThreadBase::mLock held
 status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
 {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index f0ef867..81f2eb4 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -107,8 +107,6 @@
     virtual     status_t    setMicMute(bool state);
     virtual     bool        getMicMute() const;
 
-    virtual     bool        isStreamActive(int stream) const;
-
     virtual     status_t    setParameters(int ioHandle, const String8& keyValuePairs);
     virtual     String8     getParameters(int ioHandle, const String8& keys);
 
@@ -579,8 +577,6 @@
         virtual     float       streamVolume(int stream) const;
         virtual     bool        streamMute(int stream) const;
 
-                    bool        isStreamActive(int stream) const;
-
                     sp<Track>   createTrack_l(
                                     const sp<AudioFlinger::Client>& client,
                                     int streamType,
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index e84d136..04d63e6 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -313,8 +313,7 @@
 
     // Flag that ringtone volume must be limited to music volume until we exit MODE_RINGTONE
     if (state == AudioSystem::MODE_RINGTONE &&
-        (hwOutputDesc->mRefCount[AudioSystem::MUSIC] ||
-        (systemTime() - mMusicStopTime) < seconds(SONIFICATION_HEADSET_MUSIC_DELAY))) {
+        isStreamActive(AudioSystem::MUSIC, SONIFICATION_HEADSET_MUSIC_DELAY)) {
         mLimitRingtoneVolume = true;
     } else {
         mLimitRingtoneVolume = false;
@@ -479,6 +478,7 @@
         outputDesc->mLatency = 0;
         outputDesc->mFlags = (AudioSystem::output_flags)(flags | AudioSystem::OUTPUT_FLAG_DIRECT);
         outputDesc->mRefCount[stream] = 0;
+        outputDesc->mStopTime[stream] = 0;
         output = mpClientInterface->openOutput(&outputDesc->mDevice,
                                         &outputDesc->mSamplingRate,
                                         &outputDesc->mFormat,
@@ -607,10 +607,8 @@
     if (outputDesc->mRefCount[stream] > 0) {
         // decrement usage count of this stream on the output
         outputDesc->changeRefCount(stream, -1);
-        // store time at which the last music track was stopped - see computeVolume()
-        if (stream == AudioSystem::MUSIC) {
-            mMusicStopTime = systemTime();
-        }
+        // store time at which the stream was stopped - see isStreamActive()
+        outputDesc->mStopTime[stream] = systemTime();
 
         setOutputDevice(output, getNewDevice(output));
 
@@ -920,6 +918,19 @@
     return NO_ERROR;
 }
 
+bool AudioPolicyManagerBase::isStreamActive(int stream, uint32_t inPastMs) const
+{
+    nsecs_t sysTime = systemTime();
+    for (size_t i = 0; i < mOutputs.size(); i++) {
+        if (mOutputs.valueAt(i)->mRefCount[stream] != 0 ||
+            ns2ms(sysTime - mOutputs.valueAt(i)->mStopTime[stream]) < inPastMs) {
+            return true;
+        }
+    }
+    return false;
+}
+
+
 status_t AudioPolicyManagerBase::dump(int fd)
 {
     const size_t SIZE = 256;
@@ -1010,7 +1021,7 @@
     Thread(false),
 #endif //AUDIO_POLICY_TEST
     mPhoneState(AudioSystem::MODE_NORMAL), mRingerMode(0),
-    mMusicStopTime(0), mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
+    mLimitRingtoneVolume(false), mLastVoiceVolume(-1.0f),
     mTotalEffectsCpuLoad(0), mTotalEffectsMemory(0),
     mA2dpSuspended(false)
 {
@@ -2094,6 +2105,7 @@
         mRefCount[i] = 0;
         mCurVolume[i] = -1.0;
         mMuteCount[i] = 0;
+        mStopTime[i] = 0;
     }
 }
 
@@ -2144,7 +2156,6 @@
     return refCount;
 }
 
-
 status_t AudioPolicyManagerBase::AudioOutputDescriptor::dump(int fd)
 {
     const size_t SIZE = 256;
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index 46a01ad..b04672d 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -394,6 +394,15 @@
     return mpPolicyManager->unregisterEffect(id);
 }
 
+bool AudioPolicyService::isStreamActive(int stream, uint32_t inPastMs) const
+{
+    if (mpPolicyManager == NULL) {
+        return 0;
+    }
+    Mutex::Autolock _l(mLock);
+    return mpPolicyManager->isStreamActive(stream, inPastMs);
+}
+
 void AudioPolicyService::binderDied(const wp<IBinder>& who) {
     LOGW("binderDied() %p, tid %d, calling tid %d", who.unsafe_get(), gettid(),
             IPCThreadState::self()->getCallingPid());
diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h
index 558f455..54af1f1 100644
--- a/services/audioflinger/AudioPolicyService.h
+++ b/services/audioflinger/AudioPolicyService.h
@@ -88,6 +88,7 @@
                                     int session,
                                     int id);
     virtual status_t unregisterEffect(int id);
+    virtual bool isStreamActive(int stream, uint32_t inPastMs = 0) const;
 
     virtual     status_t    onTransact(
                                 uint32_t code,
@@ -230,8 +231,8 @@
     status_t dumpPermissionDenial(int fd);
 
 
-    Mutex   mLock;      // prevents concurrent access to AudioPolicy manager functions changing device
-                        // connection stated our routing
+    mutable Mutex mLock;    // prevents concurrent access to AudioPolicy manager functions changing
+                            // device connection state  or routing
     AudioPolicyInterface* mpPolicyManager;          // the platform specific policy manager
     sp <AudioCommandThread> mAudioCommandThread;    // audio commands thread
     sp <AudioCommandThread> mTonePlaybackThread;     // tone playback thread