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

am: b94f2df289

* commit 'b94f2df289f4289f1eca7206f3d1bcdd737e69a6':
  Ask RingtonePlayer to open data for caching.
This commit is contained in:
Jeff Sharkey
2016-03-06 00:32:25 +00:00
committed by android-build-merger
3 changed files with 73 additions and 9 deletions

View File

@@ -18,6 +18,7 @@ package android.media;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.net.Uri; import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle; import android.os.UserHandle;
/** /**
@@ -36,4 +37,6 @@ interface IRingtonePlayer {
/** Return the title of the media. */ /** Return the title of the media. */
String getTitle(in Uri uri); 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.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.Process; import android.os.Process;
import android.os.RemoteException;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.provider.Settings; import android.provider.Settings;
import android.provider.Settings.System; import android.provider.Settings.System;
@@ -223,9 +225,9 @@ public class RingtoneManager {
*/ */
public static final int URI_COLUMN_INDEX = 2; public static final int URI_COLUMN_INDEX = 2;
private Activity mActivity; private final Activity mActivity;
private Context mContext; private final Context mContext;
private Cursor mCursor; private Cursor mCursor;
private int mType = TYPE_RINGTONE; private int mType = TYPE_RINGTONE;
@@ -246,7 +248,8 @@ public class RingtoneManager {
* @param activity The activity used to get a managed cursor. * @param activity The activity used to get a managed cursor.
*/ */
public RingtoneManager(Activity activity) { public RingtoneManager(Activity activity) {
mContext = mActivity = activity; mActivity = activity;
mContext = activity;
setType(mType); setType(mType);
} }
@@ -258,6 +261,7 @@ public class RingtoneManager {
* @param context The context to used to get a cursor. * @param context The context to used to get a cursor.
*/ */
public RingtoneManager(Context context) { public RingtoneManager(Context context) {
mActivity = null;
mContext = context; mContext = context;
setType(mType); setType(mType);
} }
@@ -271,7 +275,6 @@ public class RingtoneManager {
* @see #EXTRA_RINGTONE_TYPE * @see #EXTRA_RINGTONE_TYPE
*/ */
public void setType(int type) { public void setType(int type) {
if (mCursor != null) { if (mCursor != null) {
throw new IllegalStateException( throw new IllegalStateException(
"Setting filter columns should be done before querying for ringtones."); "Setting filter columns should be done before querying for ringtones.");
@@ -656,18 +659,19 @@ public class RingtoneManager {
* @see #getActualDefaultRingtoneUri(Context, int) * @see #getActualDefaultRingtoneUri(Context, int)
*/ */
public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) { public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
final ContentResolver resolver = context.getContentResolver();
String setting = getSettingForType(type); String setting = getSettingForType(type);
if (setting == null) return; if (setting == null) return;
Settings.System.putString(context.getContentResolver(), setting, Settings.System.putString(resolver, setting,
ringtoneUri != null ? ringtoneUri.toString() : null); ringtoneUri != null ? ringtoneUri.toString() : null);
// Stream selected ringtone into cache so it's available for playback // Stream selected ringtone into cache so it's available for playback
// when CE storage is still locked // when CE storage is still locked
if (ringtoneUri != null) { if (ringtoneUri != null) {
final ContentResolver cr = context.getContentResolver();
final Uri cacheUri = getCacheForType(type); final Uri cacheUri = getCacheForType(type);
try (InputStream in = cr.openInputStream(ringtoneUri); try (InputStream in = openRingtone(context, ringtoneUri);
OutputStream out = cr.openOutputStream(cacheUri)) { OutputStream out = resolver.openOutputStream(cacheUri)) {
Streams.copy(in, out); Streams.copy(in, out);
} catch (IOException e) { } catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + 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) { private static String getSettingForType(int type) {
if ((type & TYPE_RINGTONE) != 0) { if ((type & TYPE_RINGTONE) != 0) {
return Settings.System.RINGTONE; return Settings.System.RINGTONE;

View File

@@ -16,8 +16,10 @@
package com.android.systemui.media; package com.android.systemui.media;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.media.AudioAttributes; import android.media.AudioAttributes;
import android.media.IAudioService; import android.media.IAudioService;
import android.media.IRingtonePlayer; import android.media.IRingtonePlayer;
@@ -25,15 +27,20 @@ import android.media.Ringtone;
import android.net.Uri; import android.net.Uri;
import android.os.Binder; import android.os.Binder;
import android.os.IBinder; import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process; import android.os.Process;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.provider.MediaStore;
import android.provider.MediaStore.Audio.AudioColumns;
import android.util.Log; import android.util.Log;
import com.android.internal.util.Preconditions;
import com.android.systemui.SystemUI; import com.android.systemui.SystemUI;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.HashMap; import java.util.HashMap;
@@ -180,6 +187,34 @@ public class RingtonePlayer extends SystemUI {
return Ringtone.getTitle(getContextForUser(user), uri, return Ringtone.getTitle(getContextForUser(user), uri,
false /*followSettingsUri*/, false /*allowRemote*/); 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) { private Context getContextForUser(UserHandle user) {