summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt1
-rw-r--r--api/test-current.txt1
-rwxr-xr-xcore/java/android/provider/Settings.java15
-rw-r--r--media/java/android/media/MediaPlayer.java82
-rw-r--r--media/java/android/media/RingtoneManager.java34
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java40
7 files changed, 139 insertions, 35 deletions
diff --git a/api/current.txt b/api/current.txt
index c7815fa2a18a..01bdc644acfb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -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;
diff --git a/api/system-current.txt b/api/system-current.txt
index 990f09436b6b..13f3998e50ba 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -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;
diff --git a/api/test-current.txt b/api/test-current.txt
index 79351ece34ab..71f1c680b0a5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -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;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 00747885a143..5d78008d6b28 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -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.
*
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 26e466e181d9..adf85517c344 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -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;
+ }
}
/**
@@ -1102,6 +1102,26 @@ public class MediaPlayer implements SubtitleController.Listener
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.
*
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 025029e75ec1..06ac11b41c1a 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -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.
*
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 57d495f6f936..9842e28ffdba 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -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) {