Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3fa139c7 authored by Andre Lago's avatar Andre Lago
Browse files

[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
parent ea35e07e
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -3816,6 +3816,26 @@ public final class Settings {
            outKeySet.addAll(CLONE_TO_MANAGED_PROFILE);
        }

        /**
         * 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
         *
+1 −1
Original line number Diff line number Diff line
@@ -992,7 +992,7 @@ public class MediaPlayer extends PlayerBase
            // 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;
+3 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.media;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
@@ -210,7 +211,7 @@ public class Ringtone {
        String title = null;

        if (uri != null) {
            String authority = uri.getAuthority();
            String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());

            if (Settings.AUTHORITY.equals(authority)) {
                if (followSettingsUri) {
+77 −7
Original line number Diff line number Diff line
@@ -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.io.OutputStream;
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
@@ -83,6 +92,10 @@ public class RingtoneManager {
     */
    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>
    
    /**
@@ -628,6 +641,48 @@ public class RingtoneManager {
        return null;
    }
    
    /**
     * 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
@@ -645,7 +700,16 @@ public class RingtoneManager {
        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 @@ public class RingtoneManager {

        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 @@ public class RingtoneManager {

    /** {@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;
    }

    /**
+31 −2
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import android.os.UserManager;
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.io.PrintWriter;
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 @@ public class SettingsProvider extends ContentProvider {
        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 @@ public class SettingsProvider extends ContentProvider {
        }
        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 @@ public class SettingsProvider extends ContentProvider {
        }
        if (cacheName != null) {
            final File cacheFile = new File(
                    getRingtoneCacheDir(UserHandle.getCallingUserId()), cacheName);
                    getRingtoneCacheDir(owningUserId), cacheName);
            cacheFile.delete();
        }

@@ -1242,6 +1261,16 @@ public class SettingsProvider extends ContentProvider {
    }

    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);
    }