Merge "Offer to cache ringtones in system DE storage." into nyc-dev

This commit is contained in:
Jeff Sharkey
2016-02-23 20:57:45 +00:00
committed by Android (Google) Code Review
7 changed files with 139 additions and 35 deletions

View File

@@ -21192,6 +21192,7 @@ package android.media {
method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(android.media.MediaDataSource) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;

View File

@@ -22700,6 +22700,7 @@ package android.media {
method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(android.media.MediaDataSource) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;

View File

@@ -21201,6 +21201,7 @@ package android.media {
method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
method public void setDataSource(android.content.res.AssetFileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(java.io.FileDescriptor, long, long) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException;
method public void setDataSource(android.media.MediaDataSource) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;

View File

@@ -2780,6 +2780,11 @@ public final class Settings {
*/
public static final Uri DEFAULT_RINGTONE_URI = getUriFor(RINGTONE);
/** {@hide} */
public static final String RINGTONE_CACHE = "ringtone_cache";
/** {@hide} */
public static final Uri RINGTONE_CACHE_URI = getUriFor(RINGTONE_CACHE);
/**
* Persistent store for the system-wide default notification sound.
*
@@ -2798,6 +2803,11 @@ public final class Settings {
*/
public static final Uri DEFAULT_NOTIFICATION_URI = getUriFor(NOTIFICATION_SOUND);
/** {@hide} */
public static final String NOTIFICATION_SOUND_CACHE = "notification_sound_cache";
/** {@hide} */
public static final Uri NOTIFICATION_SOUND_CACHE_URI = getUriFor(NOTIFICATION_SOUND_CACHE);
/**
* Persistent store for the system-wide default alarm alert.
*
@@ -2816,6 +2826,11 @@ public final class Settings {
*/
public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
/** {@hide} */
public static final String ALARM_ALERT_CACHE = "alarm_alert_cache";
/** {@hide} */
public static final Uri ALARM_ALERT_CACHE_URI = getUriFor(ALARM_ALERT_CACHE);
/**
* Persistent store for the system default media button event receiver.
*

View File

@@ -57,6 +57,7 @@ import android.media.SubtitleTrack.RenderingWidget;
import android.media.SyncParams;
import com.android.internal.app.IAppOpsService;
import com.android.internal.util.Preconditions;
import libcore.io.IoBridge;
import libcore.io.Libcore;
@@ -964,8 +965,8 @@ public class MediaPlayer implements SubtitleController.Listener
* @param uri the Content URI of the data you want to play
* @throws IllegalStateException if it is called in an invalid state
*/
public void setDataSource(Context context, Uri uri)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
public void setDataSource(@NonNull Context context, @NonNull Uri uri)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
setDataSource(context, uri, null);
}
@@ -981,47 +982,46 @@ public class MediaPlayer implements SubtitleController.Listener
* to disallow or allow cross domain redirection.
* @throws IllegalStateException if it is called in an invalid state
*/
public void setDataSource(Context context, Uri uri, Map<String, String> headers)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
public void setDataSource(@NonNull Context context, @NonNull Uri uri,
@Nullable Map<String, String> headers) throws IOException, IllegalArgumentException,
SecurityException, IllegalStateException {
final ContentResolver resolver = context.getContentResolver();
final String scheme = uri.getScheme();
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
setDataSource(uri.getPath());
return;
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
&& Settings.AUTHORITY.equals(uri.getAuthority())) {
// Redirect ringtones to go directly to underlying provider
uri = RingtoneManager.getActualDefaultRingtoneUri(context,
RingtoneManager.getDefaultType(uri));
if (uri == null) {
throw new FileNotFoundException("Failed to resolve default ringtone");
}
}
AssetFileDescriptor fd = null;
try {
ContentResolver resolver = context.getContentResolver();
fd = resolver.openAssetFileDescriptor(uri, "r");
if (fd == null) {
// Try cached ringtone first since the actual provider may not be
// encryption aware, or it may be stored on CE media storage
final int type = RingtoneManager.getDefaultType(uri);
final Uri cacheUri = RingtoneManager.getCacheForType(type);
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
if (attemptDataSource(resolver, cacheUri)) {
return;
} else if (attemptDataSource(resolver, actualUri)) {
return;
}
// Note: using getDeclaredLength so that our behavior is the same
// as previous versions when the content provider is returning
// a full file.
if (fd.getDeclaredLength() < 0) {
setDataSource(fd.getFileDescriptor());
} else {
setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
setDataSource(uri.toString(), headers);
}
return;
} catch (SecurityException | IOException ex) {
Log.w(TAG, "Couldn't open file on client side; trying server side: " + ex);
} finally {
if (fd != null) {
fd.close();
} else {
// Try requested Uri locally first, or fallback to media server
if (attemptDataSource(resolver, uri)) {
return;
} else {
setDataSource(uri.toString(), headers);
}
}
}
setDataSource(uri.toString(), headers);
private boolean attemptDataSource(ContentResolver resolver, Uri uri) {
try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
setDataSource(afd);
return true;
} catch (NullPointerException | SecurityException | IOException ex) {
Log.w(TAG, "Couldn't open " + uri + ": " + ex);
return false;
}
}
/**
@@ -1101,6 +1101,26 @@ public class MediaPlayer implements SubtitleController.Listener
IBinder httpServiceBinder, String path, String[] keys, String[] values)
throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
/**
* Sets the data source (AssetFileDescriptor) to use. It is the caller's
* responsibility to close the file descriptor. It is safe to do so as soon
* as this call returns.
*
* @param afd the AssetFileDescriptor for the file you want to play
*/
public void setDataSource(@NonNull AssetFileDescriptor afd)
throws IOException, IllegalArgumentException, IllegalStateException {
Preconditions.checkNotNull(afd);
// Note: using getDeclaredLength so that our behavior is the same
// as previous versions when the content provider is returning
// a full file.
if (afd.getDeclaredLength() < 0) {
setDataSource(afd.getFileDescriptor());
} else {
setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
}
}
/**
* Sets the data source (FileDescriptor) to use. It is the caller's responsibility
* to close the file descriptor. It is safe to do so as soon as this call returns.

View File

@@ -18,9 +18,12 @@ package android.media;
import com.android.internal.database.SortCursor;
import libcore.io.Streams;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -33,6 +36,9 @@ import android.provider.Settings;
import android.provider.Settings.System;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
@@ -654,8 +660,19 @@ public class RingtoneManager {
if (setting == null) return;
Settings.System.putString(context.getContentResolver(), setting,
ringtoneUri != null ? ringtoneUri.toString() : null);
// Stream selected ringtone into cache so it's available for playback
// when CE storage is still locked
final ContentResolver cr = context.getContentResolver();
final Uri cacheUri = getCacheForType(type);
try (InputStream in = cr.openInputStream(ringtoneUri);
OutputStream out = cr.openOutputStream(cacheUri)) {
Streams.copy(in, out);
} catch (IOException e) {
Log.w(TAG, "Failed to cache ringtone: " + e);
}
}
private static String getSettingForType(int type) {
if ((type & TYPE_RINGTONE) != 0) {
return Settings.System.RINGTONE;
@@ -667,7 +684,20 @@ public class RingtoneManager {
return null;
}
}
/** {@hide} */
public static Uri getCacheForType(int type) {
if ((type & TYPE_RINGTONE) != 0) {
return Settings.System.RINGTONE_CACHE_URI;
} else if ((type & TYPE_NOTIFICATION) != 0) {
return Settings.System.NOTIFICATION_SOUND_CACHE_URI;
} else if ((type & TYPE_ALARM) != 0) {
return Settings.System.ALARM_ALERT_CACHE_URI;
} else {
return null;
}
}
/**
* Returns whether the given {@link Uri} is one of the default ringtones.
*

View File

@@ -49,6 +49,7 @@ import android.os.Message;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.SELinux;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
@@ -457,8 +458,28 @@ public class SettingsProvider extends ContentProvider {
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
throw new FileNotFoundException("Direct file access no longer supported; "
+ "ringtone playback is available through android.media.Ringtone");
final String cacheName;
if (Settings.System.RINGTONE_CACHE_URI.equals(uri)) {
cacheName = Settings.System.RINGTONE_CACHE;
} else if (Settings.System.NOTIFICATION_SOUND_CACHE_URI.equals(uri)) {
cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
} else if (Settings.System.ALARM_ALERT_CACHE_URI.equals(uri)) {
cacheName = Settings.System.ALARM_ALERT_CACHE;
} else {
throw new FileNotFoundException("Direct file access no longer supported; "
+ "ringtone playback is available through android.media.Ringtone");
}
final File cacheFile = new File(
getRingtoneCacheDir(UserHandle.getCallingUserId()), cacheName);
return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
}
private File getRingtoneCacheDir(int userId) {
final File cacheDir = new File(Environment.getDataSystemDeDirectory(userId), "ringtones");
cacheDir.mkdir();
SELinux.restorecon(cacheDir);
return cacheDir;
}
@Override
@@ -901,6 +922,21 @@ public class SettingsProvider extends ContentProvider {
return false;
}
// Invalidate any relevant cache files
String cacheName = null;
if (Settings.System.RINGTONE.equals(name)) {
cacheName = Settings.System.RINGTONE_CACHE;
} else if (Settings.System.NOTIFICATION_SOUND.equals(name)) {
cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
} else if (Settings.System.ALARM_ALERT.equals(name)) {
cacheName = Settings.System.ALARM_ALERT_CACHE;
}
if (cacheName != null) {
final File cacheFile = new File(
getRingtoneCacheDir(UserHandle.getCallingUserId()), cacheName);
cacheFile.delete();
}
// Mutate the value.
synchronized (mLock) {
switch (operation) {