Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b94f2df2 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Android (Google) Code Review
Browse files

Merge "Ask RingtonePlayer to open data for caching." into nyc-dev

parents d6de3abe 783ee0ca
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.media;

import android.media.AudioAttributes;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;

/**
@@ -36,4 +37,6 @@ interface IRingtonePlayer {

    /** Return the title of the media. */
    String getTitle(in Uri uri);

    ParcelFileDescriptor openRingtone(in Uri uri);
}
+35 −9
Original line number Diff line number Diff line
@@ -30,7 +30,9 @@ import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -223,8 +225,8 @@ public class RingtoneManager {
     */
    public static final int URI_COLUMN_INDEX = 2;

    private Activity mActivity;
    private Context mContext;
    private final Activity mActivity;
    private final Context mContext;

    private Cursor mCursor;

@@ -246,7 +248,8 @@ public class RingtoneManager {
     * @param activity The activity used to get a managed cursor.
     */
    public RingtoneManager(Activity activity) {
        mContext = mActivity = activity;
        mActivity = activity;
        mContext = activity;
        setType(mType);
    }

@@ -258,6 +261,7 @@ public class RingtoneManager {
     * @param context The context to used to get a cursor.
     */
    public RingtoneManager(Context context) {
        mActivity = null;
        mContext = context;
        setType(mType);
    }
@@ -271,7 +275,6 @@ public class RingtoneManager {
     * @see #EXTRA_RINGTONE_TYPE           
     */
    public void setType(int type) {

        if (mCursor != null) {
            throw new IllegalStateException(
                    "Setting filter columns should be done before querying for ringtones.");
@@ -656,18 +659,19 @@ public class RingtoneManager {
     * @see #getActualDefaultRingtoneUri(Context, int)
     */
    public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
        final ContentResolver resolver = context.getContentResolver();

        String setting = getSettingForType(type);
        if (setting == null) return;
        Settings.System.putString(context.getContentResolver(), setting,
        Settings.System.putString(resolver, setting,
                ringtoneUri != null ? ringtoneUri.toString() : null);

        // Stream selected ringtone into cache so it's available for playback
        // when CE storage is still locked
        if (ringtoneUri != null) {
            final ContentResolver cr = context.getContentResolver();
            final Uri cacheUri = getCacheForType(type);
            try (InputStream in = cr.openInputStream(ringtoneUri);
                    OutputStream out = cr.openOutputStream(cacheUri)) {
            try (InputStream in = openRingtone(context, ringtoneUri);
                    OutputStream out = resolver.openOutputStream(cacheUri)) {
                Streams.copy(in, out);
            } catch (IOException e) {
                Log.w(TAG, "Failed to cache ringtone: " + e);
@@ -675,6 +679,28 @@ public class RingtoneManager {
        }
    }

    /**
     * Try opening the given ringtone locally first, but failover to
     * {@link IRingtonePlayer} if we can't access it directly. Typically happens
     * when process doesn't hold
     * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE}.
     */
    private static InputStream openRingtone(Context context, Uri uri) throws IOException {
        final ContentResolver resolver = context.getContentResolver();
        try {
            return resolver.openInputStream(uri);
        } catch (SecurityException | IOException e) {
            Log.w(TAG, "Failed to open directly; attempting failover: " + e);
            final IRingtonePlayer player = context.getSystemService(AudioManager.class)
                    .getRingtonePlayer();
            try {
                return new ParcelFileDescriptor.AutoCloseInputStream(player.openRingtone(uri));
            } catch (Exception e2) {
                throw new IOException(e2);
            }
        }
    }

    private static String getSettingForType(int type) {
        if ((type & TYPE_RINGTONE) != 0) {
            return Settings.System.RINGTONE;
+35 −0
Original line number Diff line number Diff line
@@ -16,8 +16,10 @@

package com.android.systemui.media;

import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.media.AudioAttributes;
import android.media.IAudioService;
import android.media.IRingtonePlayer;
@@ -25,15 +27,20 @@ import android.media.Ringtone;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.AudioColumns;
import android.util.Log;

import com.android.internal.util.Preconditions;
import com.android.systemui.SystemUI;

import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;

@@ -180,6 +187,34 @@ public class RingtonePlayer extends SystemUI {
            return Ringtone.getTitle(getContextForUser(user), uri,
                    false /*followSettingsUri*/, false /*allowRemote*/);
        }

        @Override
        public ParcelFileDescriptor openRingtone(Uri uri) {
            final UserHandle user = Binder.getCallingUserHandle();
            final ContentResolver resolver = getContextForUser(user).getContentResolver();

            // Only open the requested Uri if it's a well-known ringtone or
            // other sound from the platform media store, otherwise this opens
            // up arbitrary access to any file on external storage.
            if (uri.toString().startsWith(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI.toString())) {
                try (Cursor c = resolver.query(uri, new String[] {
                        MediaStore.Audio.AudioColumns.IS_RINGTONE,
                        MediaStore.Audio.AudioColumns.IS_ALARM,
                        MediaStore.Audio.AudioColumns.IS_NOTIFICATION
                }, null, null, null)) {
                    if (c.moveToFirst()) {
                        if (c.getInt(0) != 0 || c.getInt(1) != 0 || c.getInt(2) != 0) {
                            try {
                                return resolver.openFileDescriptor(uri, "r");
                            } catch (IOException e) {
                                throw new SecurityException(e);
                            }
                        }
                    }
                }
            }
            throw new SecurityException("Uri is not ringtone, alarm, or notification: " + uri);
        }
    };

    private Context getContextForUser(UserHandle user) {