[media] Separate ringtones for managed profiles
Separate the default system ringtone settings for managed profiles,
which previously used the same default ringtones as the personal profile
they belong to
Bug: 30658854
Change-Id: I22c69c7b8d31c7c424f5e00a3d9febac98b93d74
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5bc50ca..2c02cfa 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3817,6 +3817,26 @@
}
/**
+ * These entries should be cloned from this profile's parent only if the dependency's
+ * value is true ("1")
+ *
+ * Note: the dependencies must be Secure settings
+ *
+ * @hide
+ */
+ public static final Map<String, String> CLONE_FROM_PARENT_ON_VALUE = new ArrayMap<>();
+ static {
+ CLONE_FROM_PARENT_ON_VALUE.put(RINGTONE, Secure.SYNC_PARENT_SOUNDS);
+ CLONE_FROM_PARENT_ON_VALUE.put(NOTIFICATION_SOUND, Secure.SYNC_PARENT_SOUNDS);
+ CLONE_FROM_PARENT_ON_VALUE.put(ALARM_ALERT, Secure.SYNC_PARENT_SOUNDS);
+ }
+
+ /** @hide */
+ public static void getCloneFromParentOnValueSettings(Map<String, String> outMap) {
+ outMap.putAll(CLONE_FROM_PARENT_ON_VALUE);
+ }
+
+ /**
* When to use Wi-Fi calling
*
* @see android.telephony.TelephonyManager.WifiCallingChoices
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 30395b8..dd26799 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -992,7 +992,7 @@
// 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 cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
if (attemptDataSource(resolver, cacheUri)) {
return;
diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java
index c2bcd93..8935b72 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -16,6 +16,7 @@
package android.media;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -206,11 +207,11 @@
public static String getTitle(
Context context, Uri uri, boolean followSettingsUri, boolean allowRemote) {
ContentResolver res = context.getContentResolver();
-
+
String title = null;
if (uri != null) {
- String authority = uri.getAuthority();
+ String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
if (Settings.AUTHORITY.equals(authority)) {
if (followSettingsUri) {
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 86ebae1..664765a 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -16,18 +16,24 @@
package android.media;
+import android.Manifest;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Activity;
+import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.provider.MediaStore;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -43,6 +49,9 @@
import java.util.ArrayList;
import java.util.List;
+import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.pm.PackageManager.NameNotFoundException;
+
/**
* RingtoneManager provides access to ringtones, notification, and other types
* of sounds. It manages querying the different media providers and combines the
@@ -82,6 +91,10 @@
* All types of sounds.
*/
public static final int TYPE_ALL = TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM;
+
+ private static final int[] RINGTONE_TYPES = {
+ TYPE_RINGTONE, TYPE_NOTIFICATION, TYPE_ALARM
+ };
// </attr>
@@ -629,6 +642,48 @@
}
/**
+ * Disables Settings.System.SYNC_PARENT_SOUNDS, copying the parent's ringtones to the current
+ * profile
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public static void disableSyncFromParent(Context userContext) {
+ // Must disable sync first so that ringtone copy below doesn't get redirected to parent
+ Settings.Secure.putIntForUser(userContext.getContentResolver(),
+ Settings.Secure.SYNC_PARENT_SOUNDS, 0 /* false */, userContext.getUserId());
+
+ // Copy ringtones from parent profile
+ UserManager um = UserManager.get(userContext);
+ UserInfo parentInfo = um.getProfileParent(userContext.getUserId());
+ if (parentInfo != null) {
+ try {
+ Context targetContext = userContext.createPackageContextAsUser(
+ userContext.getPackageName(), 0 /* flags */, UserHandle.of(parentInfo.id));
+ for (int ringtoneType : RINGTONE_TYPES) {
+ Uri ringtoneUri = getActualDefaultRingtoneUri(targetContext, ringtoneType);
+ // Add user id of parent so that custom ringtones can be read and played
+ RingtoneManager.setActualDefaultRingtoneUri(userContext, ringtoneType,
+ maybeAddUserId(ringtoneUri, parentInfo.id));
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Unable to create parent context", e);
+ }
+ }
+ }
+
+ /**
+ * Enables Settings.System.SYNC_PARENT_SOUNDS for the content's user
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public static void enableSyncFromParent(Context userContext) {
+ Settings.Secure.putIntForUser(userContext.getContentResolver(),
+ Settings.Secure.SYNC_PARENT_SOUNDS, 1 /* true */, userContext.getUserId());
+ }
+
+ /**
* Gets the current default sound's {@link Uri}. This will give the actual
* sound {@link Uri}, instead of using this, most clients can use
* {@link System#DEFAULT_RINGTONE_URI}.
@@ -645,7 +700,16 @@
if (setting == null) return null;
final String uriString = Settings.System.getStringForUser(context.getContentResolver(),
setting, context.getUserId());
- return uriString != null ? Uri.parse(uriString) : null;
+ Uri ringtoneUri = uriString != null ? Uri.parse(uriString) : null;
+
+ // If this doesn't verify, the user id must be kept in the uri to ensure it resolves in the
+ // correct user storage
+ if (ringtoneUri != null
+ && ContentProvider.getUserIdFromUri(ringtoneUri) == context.getUserId()) {
+ ringtoneUri = ContentProvider.getUriWithoutUserId(ringtoneUri);
+ }
+
+ return ringtoneUri;
}
/**
@@ -663,13 +727,14 @@
String setting = getSettingForType(type);
if (setting == null) return;
+ ringtoneUri = ContentProvider.maybeAddUserId(ringtoneUri, context.getUserId());
Settings.System.putStringForUser(resolver, setting,
ringtoneUri != null ? ringtoneUri.toString() : null, context.getUserId());
// Stream selected ringtone into cache so it's available for playback
// when CE storage is still locked
if (ringtoneUri != null) {
- final Uri cacheUri = getCacheForType(type);
+ final Uri cacheUri = getCacheForType(type, context.getUserId());
try (InputStream in = openRingtone(context, ringtoneUri);
OutputStream out = resolver.openOutputStream(cacheUri)) {
Streams.copy(in, out);
@@ -715,15 +780,20 @@
/** {@hide} */
public static Uri getCacheForType(int type) {
+ return getCacheForType(type, UserHandle.getCallingUserId());
+ }
+
+ /** {@hide} */
+ public static Uri getCacheForType(int type, int userId) {
if ((type & TYPE_RINGTONE) != 0) {
- return Settings.System.RINGTONE_CACHE_URI;
+ return ContentProvider.maybeAddUserId(Settings.System.RINGTONE_CACHE_URI, userId);
} else if ((type & TYPE_NOTIFICATION) != 0) {
- return Settings.System.NOTIFICATION_SOUND_CACHE_URI;
+ return ContentProvider.maybeAddUserId(Settings.System.NOTIFICATION_SOUND_CACHE_URI,
+ userId);
} else if ((type & TYPE_ALARM) != 0) {
- return Settings.System.ALARM_ALERT_CACHE_URI;
- } else {
- return null;
+ return ContentProvider.maybeAddUserId(Settings.System.ALARM_ALERT_CACHE_URI, userId);
}
+ return null;
}
/**
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 22cda0a..37e7442 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -58,6 +58,7 @@
import android.os.UserManagerInternal;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -76,6 +77,7 @@
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
@@ -195,6 +197,13 @@
Settings.System.getCloneToManagedProfileSettings(sSystemCloneToManagedSettings);
}
+ // Per user system settings that are cloned from the profile's parent when a dependency
+ // in {@link Settings.Secure} is set to "1".
+ public static final Map<String, String> sSystemCloneFromParentOnDependency = new ArrayMap<>();
+ static {
+ Settings.System.getCloneFromParentOnValueSettings(sSystemCloneFromParentOnDependency);
+ }
+
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -518,19 +527,29 @@
}
uri = ContentProvider.getUriWithoutUserId(uri);
+ final String cacheRingtoneSetting;
final String cacheName;
if (Settings.System.RINGTONE_CACHE_URI.equals(uri)) {
+ cacheRingtoneSetting = Settings.System.RINGTONE;
cacheName = Settings.System.RINGTONE_CACHE;
} else if (Settings.System.NOTIFICATION_SOUND_CACHE_URI.equals(uri)) {
+ cacheRingtoneSetting = Settings.System.NOTIFICATION_SOUND;
cacheName = Settings.System.NOTIFICATION_SOUND_CACHE;
} else if (Settings.System.ALARM_ALERT_CACHE_URI.equals(uri)) {
+ cacheRingtoneSetting = Settings.System.ALARM_ALERT;
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(userId), cacheName);
+ int actualCacheOwner;
+ // Redirect cache to parent if ringtone setting is owned by profile parent
+ synchronized (mLock) {
+ actualCacheOwner = resolveOwningUserIdForSystemSettingLocked(userId,
+ cacheRingtoneSetting);
+ }
+ final File cacheFile = new File(getRingtoneCacheDir(actualCacheOwner), cacheName);
return ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.parseMode(mode));
}
@@ -1102,7 +1121,7 @@
}
if (cacheName != null) {
final File cacheFile = new File(
- getRingtoneCacheDir(UserHandle.getCallingUserId()), cacheName);
+ getRingtoneCacheDir(owningUserId), cacheName);
cacheFile.delete();
}
@@ -1242,6 +1261,16 @@
}
private int resolveOwningUserIdForSystemSettingLocked(int userId, String setting) {
+ final int parentId;
+ // Resolves dependency if setting has a dependency and the calling user has a parent
+ if (sSystemCloneFromParentOnDependency.containsKey(setting)
+ && (parentId = getGroupParentLocked(userId)) != userId) {
+ // The setting has a dependency and the profile has a parent
+ String dependency = sSystemCloneFromParentOnDependency.get(setting);
+ if (getSecureSetting(dependency, userId).getValue().equals("1")) {
+ return parentId;
+ }
+ }
return resolveOwningUserIdLocked(userId, sSystemCloneToManagedSettings, setting);
}