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

Commit 4320c89b authored by Grant Menke's avatar Grant Menke Committed by Android (Google) Code Review
Browse files

Merge "Resolve cross account user ringtone validation." into main

parents 0155ca00 294327c6
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -3520,7 +3520,8 @@ public class Call implements CreateConnectionResponse, EventManager.Loggable,
        return Contacts.getLookupUri(mCallerInfo.getContactId(), mCallerInfo.lookupKey);
    }

    Uri getRingtone() {
    @VisibleForTesting
    public Uri getRingtone() {
        return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri;
    }

+29 −6
Original line number Diff line number Diff line
@@ -74,13 +74,19 @@ public class RingtoneFactory {
        Ringtone ringtone = null;

        if (ringtoneUri != null && userContext != null) {
            // Ringtone URI is explicitly specified. First, try to create a Ringtone with that.
            if (currentUserOwnsRingtone(ringtoneUri, incomingCall)) {
                // Ringtone URI is explicitly specified and owned by the current user - try to
                // create a Ringtone with that.
                try {
                    ringtone = RingtoneManager.getRingtone(
                            userContext, ringtoneUri, volumeShaperConfig, audioAttrs);
                } catch (Exception e) {
                    Log.e(this, e, "getRingtone: exception while getting ringtone.");
                }
            } else {
                Log.w(this, "getRingtone: Failed to verify that the custom ringtone URI"
                        + " is owned by the current user. Falling back to the default ringtone.");
            }
        }
        if (ringtone == null) {
            // Contact didn't specify ringtone or custom Ringtone creation failed. Get default
@@ -119,6 +125,23 @@ public class RingtoneFactory {
        return new Pair(ringtoneUri, ringtone);
    }

    private boolean currentUserOwnsRingtone(Uri ringtoneUri, Call incomingCall) {
        if (ringtoneUri.getUserInfo() == null) {
            // The current user set this custom ringtone:
            return true;
        }

        UserHandle associatedUser = incomingCall.getAssociatedUser();
        if (associatedUser == null) {
            Log.d(this, "currentUserOwnsRingtone: The incoming call does not"
                    + " have an associated user.");
            return false;
        }

        String currentUserId = String.valueOf(associatedUser.getIdentifier());
        return currentUserId.equals(ringtoneUri.getUserInfo());
    }

    private AudioAttributes getDefaultRingtoneAudioAttributes(boolean hapticChannelsMuted) {
        return new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE)
+111 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.telecom.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;

import android.media.Ringtone;
import android.net.Uri;
import android.os.UserHandle;
import android.util.Pair;

import androidx.test.filters.SmallTest;

import com.android.server.telecom.CallsManager;
import com.android.server.telecom.Call;
import com.android.server.telecom.RingtoneFactory;
import com.android.server.telecom.flags.FeatureFlags;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mock;

@RunWith(JUnit4.class)
public class RingtoneFactoryTest extends TelecomTestCase {
    @Mock private Uri mockCustomRingtoneUri;
    @Mock private CallsManager mockCallsManager;
    @Mock private FeatureFlags mockFeatureFlags;
    @Mock Call mockCall;
    private RingtoneFactory ringtoneFactory;

    @Override
    @Before
    public void setUp() throws Exception {
        super.setUp();
        mContext = spy(mComponentContextFixture.getTestDouble().getApplicationContext());
        ringtoneFactory = new RingtoneFactory(mockCallsManager, mContext, mockFeatureFlags);
    }

    @Override
    @After
    public void tearDown() throws Exception {
        super.tearDown();
    }

    @SmallTest
    @Test
    public void testCustomRingtoneAccessibleWhenUserOwnsCustomRingtone() throws Exception {
        // Current User is User 10:
        when(mockCall.getAssociatedUser()).thenReturn(new UserHandle(10));

        // Custom ringtone is owned by User 10:
        when(mockCall.getRingtone()).thenReturn(mockCustomRingtoneUri);
        when(mockCustomRingtoneUri.getUserInfo()).thenReturn("10");

        // Ensure access to the custom ringtone is allowed:
        Pair<Uri, Ringtone> ringtonePair = ringtoneFactory.getRingtone(mockCall, null, false);
        assertEquals(mockCustomRingtoneUri, ringtonePair.first);
    }

    @SmallTest
    @Test
    public void testCustomRingtoneAccessibleWhenUserInfoNull() throws Exception {
        // Current User is User 0:
        when(mockCall.getAssociatedUser()).thenReturn(new UserHandle(0));

        // Custom ringtone is owned by User 0:
        when(mockCall.getRingtone()).thenReturn(mockCustomRingtoneUri);

        // Mock the behavior of a custom ringtone on User 0:
        when(mockCustomRingtoneUri.getUserInfo()).thenReturn(null);

        // Ensure access to the custom ringtone is allowed:
        Pair<Uri, Ringtone> ringtonePair = ringtoneFactory.getRingtone(mockCall, null, false);
        assertEquals(mockCustomRingtoneUri, ringtonePair.first);
    }

    @SmallTest
    @Test
    public void testCustomRingtoneNotAccessibleByOtherUser() throws Exception {
        // Current User is User 0:
        when(mockCall.getAssociatedUser()).thenReturn(new UserHandle(0));

        // Custom ringtone is owned by User 10:
        when(mockCall.getRingtone()).thenReturn(mockCustomRingtoneUri);
        when(mockCustomRingtoneUri.getUserInfo()).thenReturn("10");

        // Ensure access to the custom ringtone is NOT allowed:
        Pair<Uri, Ringtone> ringtonePair = ringtoneFactory.getRingtone(mockCall, null, false);
        assertNotEquals(mockCustomRingtoneUri, ringtonePair.first);
    }
}
 No newline at end of file