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

This commit is contained in:
Jeff Sharkey
2016-03-06 00:29:00 +00:00
committed by Android (Google) Code Review
3 changed files with 73 additions and 9 deletions

View File

@@ -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);
}

View File

@@ -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,9 +225,9 @@ 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;
private int mType = TYPE_RINGTONE;
@@ -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;

View File

@@ -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) {