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

Commit 12e27783 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by android-build-merger
Browse files

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

am: b94f2df2

* commit 'b94f2df2':
  Ask RingtonePlayer to open data for caching.
parents 24219954 b94f2df2
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) {