Support for selection of silent ringtone from the ringtone picker.
This doesn't actually enable that, but adds the necessary code to make it work when enabled, and cleans up some ringtone related code.
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index abdcd93..db25cfa 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -28,6 +28,7 @@
 import android.provider.Settings;
 import android.provider.Settings.System;
 import android.util.AttributeSet;
+import android.view.KeyEvent;
 import android.view.View;
 import android.widget.SeekBar;
 import android.widget.SeekBar.OnSeekBarChangeListener;
@@ -36,7 +37,7 @@
  * @hide
  */
 public class VolumePreference extends SeekBarPreference implements 
-        PreferenceManager.OnActivityStopListener {
+        PreferenceManager.OnActivityStopListener, View.OnKeyListener {
 
     private static final String TAG = "VolumePreference";
     
@@ -66,6 +67,30 @@
         mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType);
         
         getPreferenceManager().registerOnActivityStopListener(this);
+
+        // grab focus and key events so that pressing the volume buttons in the
+        // dialog doesn't also show the normal volume adjust toast.
+        view.setOnKeyListener(this);
+        view.setFocusableInTouchMode(true);
+        view.requestFocus();
+    }
+
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+        boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_VOLUME_DOWN:
+                if (isdown) {
+                    mSeekBarVolumizer.changeVolumeBy(-1);
+                }
+                return true;
+            case KeyEvent.KEYCODE_VOLUME_UP:
+                if (isdown) {
+                    mSeekBarVolumizer.changeVolumeBy(1);
+                }
+                return true;
+            default:
+                return false;
+        }
     }
 
     @Override
@@ -158,7 +183,9 @@
             }
 
             mRingtone = RingtoneManager.getRingtone(mContext, defaultUri);
-            mRingtone.setStreamType(mStreamType);
+            if (mRingtone != null) {
+                mRingtone.setStreamType(mStreamType);
+            }
         }
         
         public void stop() {
@@ -215,5 +242,12 @@
             return mSeekBar;
         }
         
+        public void changeVolumeBy(int amount) {
+            mSeekBar.incrementProgressBy(amount);
+            if (mRingtone != null && !mRingtone.isPlaying()) {
+                sample();
+            }
+            postSetVolume(mSeekBar.getProgress());
+        }
     }
 }
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index a573983..e21824e 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -23,7 +23,9 @@
 import android.media.AudioManager;
 import android.media.AudioService;
 import android.media.AudioSystem;
+import android.media.RingtoneManager;
 import android.media.ToneGenerator;
+import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
 import android.os.Vibrator;
@@ -44,7 +46,7 @@
 public class VolumePanel extends Handler
 {
     private static final String TAG = "VolumePanel";
-    private static boolean LOGD = false || Config.LOGD;
+    private static boolean LOGD = false;
 
     /**
      * The delay before playing a sound. This small period exists so the user
@@ -86,6 +88,7 @@
     protected Context mContext;
     private AudioManager mAudioManager;
     protected AudioService mAudioService;
+    private boolean mRingIsSilent;
 
     private final Toast mToast;
     private final View mView;
@@ -138,7 +141,7 @@
             onShowVolumeChanged(streamType, flags);
         }
 
-        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
+        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
             removeMessages(MSG_PLAY_SOUND);
             sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
         }
@@ -157,6 +160,7 @@
         int index = mAudioService.getStreamVolume(streamType);
         int message = UNKNOWN_VOLUME_TEXT;
         int additionalMessage = 0;
+        mRingIsSilent = false;
 
         if (LOGD) {
             Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
@@ -169,8 +173,15 @@
         switch (streamType) {
 
             case AudioManager.STREAM_RING: {
+                setRingerIcon();
                 message = RINGTONE_VOLUME_TEXT;
-                setRingerIcon(index);
+                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
+                        mContext, RingtoneManager.TYPE_RINGTONE);
+                if (ringuri == null) {
+                    additionalMessage =
+                        com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;
+                    mRingIsSilent = true;
+                }
                 break;
             }
 
@@ -208,6 +219,13 @@
             case AudioManager.STREAM_NOTIFICATION: {
                 message = NOTIFICATION_VOLUME_TEXT;
                 setSmallIcon(index);
+                Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
+                        mContext, RingtoneManager.TYPE_NOTIFICATION);
+                if (ringuri == null) {
+                    additionalMessage =
+                        com.android.internal.R.string.volume_music_hint_silent_ringtone_selected;
+                    mRingIsSilent = true;
+                }
                 break;
             }
 
@@ -254,7 +272,6 @@
                 mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
             sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
         }
-
     }
 
     protected void onPlaySound(int streamType, int flags) {
@@ -337,17 +354,15 @@
     /**
      * Makes the ringer icon visible with an icon that is chosen
      * based on the current ringer mode.
-     *
-     * @param index
      */
-    private void setRingerIcon(int index) {
+    private void setRingerIcon() {
         mSmallStreamIcon.setVisibility(View.GONE);
         mLargeStreamIcon.setVisibility(View.VISIBLE);
 
         int ringerMode = mAudioService.getRingerMode();
         int icon;
 
-        if (LOGD) Log.d(TAG, "setRingerIcon(index: " + index+ "), ringerMode: " + ringerMode);
+        if (LOGD) Log.d(TAG, "setRingerIcon(), ringerMode: " + ringerMode);
 
         if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
             icon = com.android.internal.R.drawable.ic_volume_off;
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0e84839..549b668 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1709,6 +1709,8 @@
     <string name="volume_music">Media volume</string>
     <!-- Hint shown in the volume toast to inform the user that the media audio is playing through Bluetooth. -->
     <string name="volume_music_hint_playing_through_bluetooth">Playing through Bluetooth</string>
+    <!-- Hint shown in the volume toast to inform the user that the current ringtone is the silent ringtone. -->
+    <string name="volume_music_hint_silent_ringtone_selected">Silent ringtone selected</string>
     <!-- Title of the dialog where the user is adjusting the phone call volume -->
     <string name="volume_call">In-call volume</string>
     <!-- Title of the dialog where the user is adjusting the phone call volume when connected on bluetooth-->
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 44026e4..8481410 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -605,21 +605,6 @@
             Log.e(TAG, "Failed to open ringtone " + ringtoneUri);
         }
 
-        // Ringtone doesn't exist, use the fallback ringtone.
-        try {
-            AssetFileDescriptor afd = context.getResources().openRawResourceFd(
-                    com.android.internal.R.raw.fallbackring);
-            if (afd != null) {
-                Ringtone r = new Ringtone(context);
-                r.open(afd);
-                afd.close();
-                return r;
-            }
-        } catch (Exception ex) {
-        }
-        
-        // we should never get here
-        Log.e(TAG, "unable to find a usable ringtone");
         return null;
     }
     
@@ -638,8 +623,8 @@
     public static Uri getActualDefaultRingtoneUri(Context context, int type) {
         String setting = getSettingForType(type);
         if (setting == null) return null;
-        final String uriString = Settings.System.getString(context.getContentResolver(), setting); 
-        return uriString != null ? Uri.parse(uriString) : getValidRingtoneUri(context);
+        final String uriString = Settings.System.getString(context.getContentResolver(), setting);
+        return uriString != null ? Uri.parse(uriString) : null;
     }
     
     /**
@@ -655,7 +640,8 @@
     public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
         String setting = getSettingForType(type);
         if (setting == null) return;
-        Settings.System.putString(context.getContentResolver(), setting, ringtoneUri.toString());
+        Settings.System.putString(context.getContentResolver(), setting,
+                ringtoneUri != null ? ringtoneUri.toString() : null);
     }
     
     private static String getSettingForType(int type) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 1451682..9877342 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -24,9 +24,11 @@
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.res.AssetFileDescriptor;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteQueryBuilder;
+import android.media.Ringtone;
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.ParcelFileDescriptor;
@@ -397,12 +399,8 @@
             
             // Get the current value for the default sound
             Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
-            if (soundUri == null) {
-                // Fallback on any valid ringtone Uri
-                soundUri = RingtoneManager.getValidRingtoneUri(context);
-            }
 
-            if (soundUri != null) { 
+            if (soundUri != null) {
                 // Only proxy the openFile call to drm or media providers
                 String authority = soundUri.getAuthority();
                 boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
@@ -426,4 +424,64 @@
 
         return super.openFile(uri, mode);
     }
+
+    @Override
+    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
+
+        /*
+         * When a client attempts to openFile the default ringtone or
+         * notification setting Uri, we will proxy the call to the current
+         * default ringtone's Uri (if it is in the DRM or media provider).
+         */
+        int ringtoneType = RingtoneManager.getDefaultType(uri);
+        // Above call returns -1 if the Uri doesn't match a default type
+        if (ringtoneType != -1) {
+            Context context = getContext();
+
+            // Get the current value for the default sound
+            Uri soundUri = RingtoneManager.getActualDefaultRingtoneUri(context, ringtoneType);
+
+            if (soundUri != null) {
+                // Only proxy the openFile call to drm or media providers
+                String authority = soundUri.getAuthority();
+                boolean isDrmAuthority = authority.equals(DrmStore.AUTHORITY);
+                if (isDrmAuthority || authority.equals(MediaStore.AUTHORITY)) {
+
+                    if (isDrmAuthority) {
+                        try {
+                            // Check DRM access permission here, since once we
+                            // do the below call the DRM will be checking our
+                            // permission, not our caller's permission
+                            DrmStore.enforceAccessDrmPermission(context);
+                        } catch (SecurityException e) {
+                            throw new FileNotFoundException(e.getMessage());
+                        }
+                    }
+
+                    ParcelFileDescriptor pfd = null;
+                    try {
+                        pfd = context.getContentResolver().openFileDescriptor(soundUri, mode);
+                        return new AssetFileDescriptor(pfd, 0, -1);
+                    } catch (FileNotFoundException ex) {
+                        // fall through and open the fallback ringtone below
+                    }
+                }
+
+                try {
+                    return super.openAssetFile(soundUri, mode);
+                } catch (FileNotFoundException ex) {
+                    // Since a non-null Uri was specified, but couldn't be opened,
+                    // fall back to the built-in ringtone.
+                    return context.getResources().openRawResourceFd(
+                            com.android.internal.R.raw.fallbackring);
+                }
+            }
+            // no need to fall through and have openFile() try again, since we
+            // already know that will fail.
+            throw new FileNotFoundException(); // or return null ?
+        }
+
+        // Note that this will end up calling openFile() above.
+        return super.openAssetFile(uri, mode);
+    }
 }