Merge "Ask RingtonePlayer to open data for caching." into nyc-dev
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user