Merge "RESTRICT AUTOMERGE Stops hiding a11y services with the same package+label as an activity." into sc-dev
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7c368d6..54b1080 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -148,6 +148,7 @@
         <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
         <item name="android:textColorHint">?android:attr/textColorSecondary</item>
         <item name="android:minHeight">@dimen/min_tap_target_size</item>
+        <item name="android:maxLength">500</item>
     </style>
 
     <style name="wifi_section">
diff --git a/src/com/android/settings/DefaultRingtonePreference.java b/src/com/android/settings/DefaultRingtonePreference.java
index 9bf626c..4c65488 100644
--- a/src/com/android/settings/DefaultRingtonePreference.java
+++ b/src/com/android/settings/DefaultRingtonePreference.java
@@ -51,16 +51,9 @@
             return;
         }
 
-        String mimeType = mUserContext.getContentResolver().getType(ringtoneUri);
-        if (mimeType == null) {
+        if (!isValidRingtoneUri(ringtoneUri)) {
             Log.e(TAG, "onSaveRingtone for URI:" + ringtoneUri
-                    + " ignored: failure to find mimeType (no access from this context?)");
-            return;
-        }
-
-        if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg"))) {
-            Log.e(TAG, "onSaveRingtone for URI:" + ringtoneUri
-                    + " ignored: associated mimeType:" + mimeType + " is not an audio type");
+                    + " ignored: invalid ringtone Uri");
             return;
         }
 
diff --git a/src/com/android/settings/RingtonePreference.java b/src/com/android/settings/RingtonePreference.java
index 8f9c618..d283e39 100644
--- a/src/com/android/settings/RingtonePreference.java
+++ b/src/com/android/settings/RingtonePreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settings;
 
+import android.content.ContentProvider;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
@@ -23,9 +25,11 @@
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings.System;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 
 import androidx.preference.Preference;
 import androidx.preference.PreferenceManager;
@@ -239,4 +243,82 @@
         return true;
     }
 
+    public boolean isDefaultRingtone(Uri ringtoneUri) {
+        // null URIs are valid (None/silence)
+        return ringtoneUri == null || RingtoneManager.isDefault(ringtoneUri);
+    }
+
+    protected boolean isValidRingtoneUri(Uri ringtoneUri) {
+        if (isDefaultRingtone(ringtoneUri)) {
+            return true;
+        }
+
+        // Return early for android resource URIs
+        if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(ringtoneUri.getScheme())) {
+            return true;
+        }
+
+        String mimeType = mUserContext.getContentResolver().getType(ringtoneUri);
+        if (mimeType == null) {
+            Log.e(TAG, "isValidRingtoneUri for URI:" + ringtoneUri
+                    + " failed: failure to find mimeType (no access from this context?)");
+            return false;
+        }
+
+        if (!(mimeType.startsWith("audio/") || mimeType.equals("application/ogg")
+                || mimeType.equals("application/x-flac"))) {
+            Log.e(TAG, "isValidRingtoneUri for URI:" + ringtoneUri
+                    + " failed: associated mimeType:" + mimeType + " is not an audio type");
+            return false;
+        }
+
+        // Validate userId from URIs: content://{userId}@...
+        final int userIdFromUri = ContentProvider.getUserIdFromUri(ringtoneUri, mUserId);
+        if (userIdFromUri != mUserId) {
+            final UserManager userManager = mUserContext.getSystemService(UserManager.class);
+
+            if (!userManager.isSameProfileGroup(mUserId, userIdFromUri)) {
+                Log.e(TAG,
+                    "isValidRingtoneUri for URI:" + ringtoneUri + " failed: user " + userIdFromUri
+                        + " and user " + mUserId + " are not in the same profile group");
+                return false;
+            }
+
+            final int parentUserId;
+            final int profileUserId;
+            if (userManager.isProfile()) {
+                profileUserId = mUserId;
+                parentUserId = userIdFromUri;
+            } else {
+                parentUserId = mUserId;
+                profileUserId = userIdFromUri;
+            }
+
+            final UserHandle parent = userManager.getProfileParent(UserHandle.of(profileUserId));
+            if (parent == null || parent.getIdentifier() != parentUserId) {
+                Log.e(TAG,
+                    "isValidRingtoneUri for URI:" + ringtoneUri + " failed: user " + profileUserId
+                        + " is not a profile of user " + parentUserId);
+                return false;
+            }
+
+            // Allow parent <-> managed profile sharing, unless restricted
+            if (userManager.hasUserRestrictionForUser(
+                UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, UserHandle.of(parentUserId))) {
+                Log.e(TAG,
+                    "isValidRingtoneUri for URI:" + ringtoneUri + " failed: user " + parentUserId
+                        + " has restriction: " + UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE);
+                return false;
+            }
+
+            if (!userManager.isManagedProfile(profileUserId)) {
+                Log.e(TAG, "isValidRingtoneUri for URI:" + ringtoneUri
+                    + " failed: user " + profileUserId + " is not a managed profile");
+                return false;
+            }
+        }
+
+        return true;
+    }
+
 }
diff --git a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java
index 42bb34a..73583ea 100644
--- a/src/com/android/settings/accounts/AccountTypePreferenceLoader.java
+++ b/src/com/android/settings/accounts/AccountTypePreferenceLoader.java
@@ -32,6 +32,10 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
 import androidx.preference.Preference;
 import androidx.preference.Preference.OnPreferenceClickListener;
 import androidx.preference.PreferenceFragmentCompat;
@@ -45,6 +49,8 @@
 import com.android.settingslib.accounts.AuthenticatorHelper;
 import com.android.settingslib.core.instrumentation.Instrumentable;
 
+import java.util.Set;
+
 /**
  * Class to load the preference screen to be added to the settings page for the specific account
  * type as specified in the account-authenticator.
@@ -82,6 +88,7 @@
             try {
                 desc = mAuthenticatorHelper.getAccountTypeDescription(accountType);
                 if (desc != null && desc.accountPreferencesId != 0) {
+                    Set<String> fragmentAllowList = generateFragmentAllowlist(parent);
                     // Load the context of the target package, then apply the
                     // base Settings theme (no references to local resources)
                     // and create a context theme wrapper so that we get the
@@ -97,6 +104,12 @@
                     themedCtx.getTheme().setTo(baseTheme);
                     prefs = mFragment.getPreferenceManager().inflateFromResource(themedCtx,
                             desc.accountPreferencesId, parent);
+                    // Ignore Fragments provided dynamically, as these are coming from external
+                    // applications which must not have access to internal Settings' fragments.
+                    // These preferences are rendered into Settings, so they also won't have access
+                    // to their own Fragments, meaning there is no acceptable usage of
+                    // android:fragment here.
+                    filterBlockedFragments(prefs, fragmentAllowList);
                 }
             } catch (PackageManager.NameNotFoundException e) {
                 Log.w(TAG, "Couldn't load preferences.xml file from " + desc.packageName);
@@ -181,6 +194,48 @@
         }
     }
 
+    // Build allowlist from existing Fragments in PreferenceGroup
+    @VisibleForTesting
+    Set<String> generateFragmentAllowlist(@Nullable PreferenceGroup prefs) {
+        Set<String> fragmentAllowList = new ArraySet<>();
+        if (prefs == null) {
+            return fragmentAllowList;
+        }
+
+        for (int i = 0; i < prefs.getPreferenceCount(); i++) {
+            Preference pref = prefs.getPreference(i);
+            if (pref instanceof PreferenceGroup) {
+                fragmentAllowList.addAll(generateFragmentAllowlist((PreferenceGroup) pref));
+            }
+
+            String fragmentName = pref.getFragment();
+            if (!TextUtils.isEmpty(fragmentName)) {
+                fragmentAllowList.add(fragmentName);
+            }
+        }
+        return fragmentAllowList;
+    }
+
+    // Block clicks on any Preference with android:fragment that is not contained in the allowlist
+    @VisibleForTesting
+    void filterBlockedFragments(@Nullable PreferenceGroup prefs,
+            @NonNull Set<String> allowedFragments) {
+        if (prefs == null) {
+            return;
+        }
+        for (int i = 0; i < prefs.getPreferenceCount(); i++) {
+            Preference pref = prefs.getPreference(i);
+            if (pref instanceof PreferenceGroup) {
+                filterBlockedFragments((PreferenceGroup) pref, allowedFragments);
+            }
+
+            String fragmentName = pref.getFragment();
+            if (fragmentName != null && !allowedFragments.contains(fragmentName)) {
+                pref.setOnPreferenceClickListener(preference -> true);
+            }
+        }
+    }
+
     /**
      * Determines if the supplied Intent is safe. A safe intent is one that is
      * will launch a exported=true activity or owned by the same uid as the
diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
index 3319e1b..cea6463 100644
--- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
+++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java
@@ -276,6 +276,11 @@
         }
     }
 
+    @Override
+    protected boolean shouldSkipForInitialSUW() {
+        return true;
+    }
+
     @VisibleForTesting
     void initHeader() {
         final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header);
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 1d7b5dc..f1c46df 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -74,6 +74,17 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+
+        // Ensure device is provisioned in order to access Settings home
+        // TODO(b/331254029): This should later be replaced in favor of an allowlist
+        boolean unprovisioned = android.provider.Settings.Global.getInt(getContentResolver(),
+                android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 0;
+        if (unprovisioned) {
+            Log.e(TAG, "Device is not provisioned, exiting Settings");
+            finish();
+            return;
+        }
+
         setContentView(R.layout.settings_homepage_container);
 
         final View appBar = findViewById(R.id.app_bar_container);
diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java
index 0b75162..cc83c2e 100644
--- a/src/com/android/settings/network/apn/ApnEditor.java
+++ b/src/com/android/settings/network/apn/ApnEditor.java
@@ -25,6 +25,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.PersistableBundle;
+import android.os.UserManager;
 import android.provider.Telephony;
 import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionInfo;
@@ -266,6 +267,11 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+        if (isUserRestricted()) {
+            Log.e(TAG, "This setting isn't available due to user restriction.");
+            finish();
+            return;
+        }
 
         setLifecycleForAllControllers();
 
@@ -1409,6 +1415,23 @@
     }
 
     @VisibleForTesting
+    boolean isUserRestricted() {
+        UserManager userManager = getContext().getSystemService(UserManager.class);
+        if (userManager == null) {
+            return false;
+        }
+        if (!userManager.isAdminUser()) {
+            Log.e(TAG, "User is not an admin");
+            return true;
+        }
+        if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)) {
+            Log.e(TAG, "User is not allowed to configure mobile network");
+            return true;
+        }
+        return false;
+    }
+
+    @VisibleForTesting
     static class ApnData {
         /**
          * The uri correspond to a database row of the apn data. This should be null if the apn
diff --git a/src/com/android/settings/notification/app/NotificationSoundPreference.java b/src/com/android/settings/notification/app/NotificationSoundPreference.java
index 136b21f..b55f9bd 100644
--- a/src/com/android/settings/notification/app/NotificationSoundPreference.java
+++ b/src/com/android/settings/notification/app/NotificationSoundPreference.java
@@ -25,10 +25,13 @@
 import android.os.AsyncTask;
 import android.util.AttributeSet;
 
+import android.util.Log;
 import com.android.settings.R;
 import com.android.settings.RingtonePreference;
 
 public class NotificationSoundPreference extends RingtonePreference {
+    private static final String TAG = "NotificationSoundPreference";
+
     private Uri mRingtone;
 
     public NotificationSoundPreference(Context context, AttributeSet attrs) {
@@ -50,8 +53,13 @@
     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
         if (data != null) {
             Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
-            setRingtone(uri);
-            callChangeListener(uri);
+            if (isValidRingtoneUri(uri)) {
+                setRingtone(uri);
+                callChangeListener(uri);
+            } else {
+                Log.e(TAG, "onActivityResult for URI:" + uri
+                    + " ignored: invalid ringtone Uri");
+            }
         }
 
         return true;
diff --git a/src/com/android/settings/password/ChooseLockPassword.java b/src/com/android/settings/password/ChooseLockPassword.java
index 24bfe46..8577d82 100644
--- a/src/com/android/settings/password/ChooseLockPassword.java
+++ b/src/com/android/settings/password/ChooseLockPassword.java
@@ -1014,8 +1014,13 @@
 
         @Override
         protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
-            final boolean success = mUtils.setLockCredential(
-                    mChosenPassword, mCurrentCredential, mUserId);
+            boolean success;
+            try {
+                success = mUtils.setLockCredential(mChosenPassword, mCurrentCredential, mUserId);
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Failed to set lockscreen credential", e);
+                success = false;
+            }
             if (success) {
                 unifyProfileCredentialIfRequested();
             }
diff --git a/src/com/android/settings/password/ChooseLockPattern.java b/src/com/android/settings/password/ChooseLockPattern.java
index d45bb2e..573aa90 100644
--- a/src/com/android/settings/password/ChooseLockPattern.java
+++ b/src/com/android/settings/password/ChooseLockPattern.java
@@ -900,8 +900,13 @@
         @Override
         protected Pair<Boolean, Intent> saveAndVerifyInBackground() {
             final int userId = mUserId;
-            final boolean success = mUtils.setLockCredential(mChosenPattern, mCurrentCredential,
-                    userId);
+            boolean success;
+            try {
+                success = mUtils.setLockCredential(mChosenPattern, mCurrentCredential, userId);
+            } catch (RuntimeException e) {
+                Log.e(TAG, "Failed to set lockscreen credential", e);
+                success = false;
+            }
             if (success) {
                 unifyProfileCredentialIfRequested();
             }
diff --git a/src/com/android/settings/search/SearchFeatureProvider.java b/src/com/android/settings/search/SearchFeatureProvider.java
index 4c39b9c..5c54878 100644
--- a/src/com/android/settings/search/SearchFeatureProvider.java
+++ b/src/com/android/settings/search/SearchFeatureProvider.java
@@ -51,7 +51,7 @@
      * @throws IllegalArgumentException when caller is null
      * @throws SecurityException        when caller is not allowed to launch search result page
      */
-    void verifyLaunchSearchResultPageCaller(Context context, @NonNull ComponentName caller)
+    void verifyLaunchSearchResultPageCaller(@NonNull Context context, @NonNull String callerPackage)
             throws SecurityException, IllegalArgumentException;
 
     /**
diff --git a/src/com/android/settings/search/SearchFeatureProviderImpl.java b/src/com/android/settings/search/SearchFeatureProviderImpl.java
index 508d37d..5035ef8 100644
--- a/src/com/android/settings/search/SearchFeatureProviderImpl.java
+++ b/src/com/android/settings/search/SearchFeatureProviderImpl.java
@@ -17,13 +17,14 @@
 
 package com.android.settings.search;
 
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Settings;
 import android.text.TextUtils;
 
+import androidx.annotation.NonNull;
+
 import com.android.settingslib.search.SearchIndexableResources;
 import com.android.settingslib.search.SearchIndexableResourcesMobile;
 
@@ -32,21 +33,18 @@
  */
 public class SearchFeatureProviderImpl implements SearchFeatureProvider {
 
-    private static final String TAG = "SearchFeatureProvider";
-
     private SearchIndexableResources mSearchIndexableResources;
 
     @Override
-    public void verifyLaunchSearchResultPageCaller(Context context, ComponentName caller) {
-        if (caller == null) {
+    public void verifyLaunchSearchResultPageCaller(@NonNull Context context,
+            @NonNull String callerPackage) {
+        if (TextUtils.isEmpty(callerPackage)) {
             throw new IllegalArgumentException("ExternalSettingsTrampoline intents "
                     + "must be called with startActivityForResult");
         }
-        final String packageName = caller.getPackageName();
-        final boolean isSettingsPackage = TextUtils.equals(packageName, context.getPackageName())
-                || TextUtils.equals(getSettingsIntelligencePkgName(context), packageName);
-        final boolean isAllowlistedPackage =
-                isSignatureAllowlisted(context, caller.getPackageName());
+        final boolean isSettingsPackage = TextUtils.equals(callerPackage, context.getPackageName())
+                || TextUtils.equals(getSettingsIntelligencePkgName(context), callerPackage);
+        final boolean isAllowlistedPackage = isSignatureAllowlisted(context, callerPackage);
         if (isSettingsPackage || isAllowlistedPackage) {
             return;
         }
diff --git a/src/com/android/settings/search/SearchResultTrampoline.java b/src/com/android/settings/search/SearchResultTrampoline.java
index e930473..28030e8 100644
--- a/src/com/android/settings/search/SearchResultTrampoline.java
+++ b/src/com/android/settings/search/SearchResultTrampoline.java
@@ -39,7 +39,7 @@
         // First make sure caller has privilege to launch a search result page.
         FeatureFactory.getFactory(this)
                 .getSearchFeatureProvider()
-                .verifyLaunchSearchResultPageCaller(this, getCallingActivity());
+                .verifyLaunchSearchResultPageCaller(this, getLaunchedFromPackage());
         // Didn't crash, proceed and launch the result as a subsetting.
         final Intent intent = getIntent();
 
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
index b50e083..d435117 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java
@@ -780,4 +780,9 @@
                     new Pair(ConvertUtils.METRIC_KEY_BATTERY_USAGE, "app label")
                 });
     }
+
+    @Test
+    public void shouldSkipForInitialSUW_returnTrue() {
+        assertThat(mFragment.shouldSkipForInitialSUW()).isTrue();
+    }
 }
diff --git a/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java b/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java
index 0a430cd..b2a30ca 100644
--- a/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java
+++ b/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java
@@ -34,6 +34,7 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.UserManager;
 import android.view.KeyEvent;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -102,6 +103,8 @@
     @Mock
     private FragmentActivity mActivity;
     @Mock
+    private UserManager mUserManager;
+    @Mock
     private ProxySubscriptionManager mProxySubscriptionMgr;
 
     @Captor
@@ -127,6 +130,11 @@
         doReturn(mContext.getTheme()).when(mActivity).getTheme();
         doReturn(mContext.getContentResolver()).when(mActivity).getContentResolver();
 
+        doReturn(mUserManager).when(mContext).getSystemService(UserManager.class);
+        doReturn(true).when(mUserManager).isAdminUser();
+        doReturn(false).when(mUserManager)
+                .hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+
         setMockPreference(mContext);
         mApnEditorUT.mApnData = new FakeApnData(APN_DATA);
         mApnEditorUT.sNotSet = "Not Set";
@@ -453,6 +461,27 @@
 
     @Test
     @Config(shadows = ShadowFragment.class)
+    public void onCreate_notAdminUser_shouldFinish() {
+        doReturn(false).when(mUserManager).isAdminUser();
+
+        mApnEditorUT.onCreate(null);
+
+        verify(mApnEditorUT).finish();
+    }
+
+    @Test
+    @Config(shadows = ShadowFragment.class)
+    public void onCreate_hasUserRestriction_shouldFinish() {
+        doReturn(true).when(mUserManager)
+                .hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
+
+        mApnEditorUT.onCreate(null);
+
+        verify(mApnEditorUT).finish();
+    }
+
+    @Test
+    @Config(shadows = ShadowFragment.class)
     public void onCreate_noAction_shouldFinishAndNoCrash() {
         ProxySubscriptionManager proxySubscriptionMgr = mock(ProxySubscriptionManager.class);
         mApnEditorUT.mProxySubscriptionMgr = proxySubscriptionMgr;
diff --git a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
index 444a813..ebd935d 100644
--- a/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
+++ b/tests/robotests/src/com/android/settings/search/SearchFeatureProviderImplTest.java
@@ -21,7 +21,6 @@
 
 import android.app.Activity;
 import android.app.settings.SettingsEnums;
-import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ResolveInfo;
@@ -126,20 +125,22 @@
 
     @Test(expected = SecurityException.class)
     public void verifyLaunchSearchResultPageCaller_badCaller_shouldCrash() {
-        final ComponentName cn = new ComponentName("pkg", "class");
-        mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
+        final String packageName = "pkg";
+
+        mProvider.verifyLaunchSearchResultPageCaller(mActivity, packageName);
     }
 
     @Test
     public void verifyLaunchSearchResultPageCaller_settingsCaller_shouldNotCrash() {
-        final ComponentName cn = new ComponentName(mActivity.getPackageName(), "class");
-        mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
+        final String packageName = mActivity.getPackageName();
+
+        mProvider.verifyLaunchSearchResultPageCaller(mActivity, packageName);
     }
 
     @Test
     public void verifyLaunchSearchResultPageCaller_settingsIntelligenceCaller_shouldNotCrash() {
         final String packageName = mProvider.getSettingsIntelligencePkgName(mActivity);
-        final ComponentName cn = new ComponentName(packageName, "class");
-        mProvider.verifyLaunchSearchResultPageCaller(mActivity, cn);
+
+        mProvider.verifyLaunchSearchResultPageCaller(mActivity, packageName);
     }
 }
diff --git a/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java b/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java
index 7877684..360a8a5 100644
--- a/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java
+++ b/tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java
@@ -16,16 +16,19 @@
 
 package com.android.settings;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ContentInterface;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.media.RingtoneManager;
 import android.net.Uri;
+import android.os.UserHandle;
+import android.os.UserManager;
 
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -34,17 +37,22 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 /** Unittest for DefaultRingtonePreference. */
 @RunWith(AndroidJUnit4.class)
 public class DefaultRingtonePreferenceTest {
 
+    private static final int OWNER_USER_ID = 1;
+    private static final int OTHER_USER_ID = 10;
+    private static final int INVALID_RINGTONE_TYPE = 0;
     private DefaultRingtonePreference mDefaultRingtonePreference;
 
     @Mock
     private ContentResolver mContentResolver;
     @Mock
+    private UserManager mUserManager;
     private Uri mRingtoneUri;
 
     @Before
@@ -52,14 +60,24 @@
         MockitoAnnotations.initMocks(this);
 
         Context context = spy(ApplicationProvider.getApplicationContext());
-        doReturn(mContentResolver).when(context).getContentResolver();
+        mContentResolver = ContentResolver.wrap(Mockito.mock(ContentInterface.class));
+        when(context.getContentResolver()).thenReturn(mContentResolver);
 
         mDefaultRingtonePreference = spy(new DefaultRingtonePreference(context, null /* attrs */));
         doReturn(context).when(mDefaultRingtonePreference).getContext();
+
+        // Use INVALID_RINGTONE_TYPE to return early in RingtoneManager.setActualDefaultRingtoneUri
         when(mDefaultRingtonePreference.getRingtoneType())
-                .thenReturn(RingtoneManager.TYPE_RINGTONE);
-        mDefaultRingtonePreference.setUserId(1);
+                .thenReturn(INVALID_RINGTONE_TYPE);
+
+        mDefaultRingtonePreference.setUserId(OWNER_USER_ID);
         mDefaultRingtonePreference.mUserContext = context;
+        when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(false);
+
+        when(context.getSystemServiceName(UserManager.class)).thenReturn(Context.USER_SERVICE);
+        when(context.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+
+        mRingtoneUri = Uri.parse("content://none");
     }
 
     @Test
@@ -79,4 +97,53 @@
 
         verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
     }
+
+    @Test
+    public void onSaveRingtone_notManagedProfile_shouldNotSetRingtone() {
+        mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
+        when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
+        when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
+        when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
+                UserHandle.of(OWNER_USER_ID));
+        when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(false);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
+
+    @Test
+    public void onSaveRingtone_notSameUser_shouldNotSetRingtone() {
+        mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
+        when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
+        when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(false);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference, never()).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
+
+    @Test
+    public void onSaveRingtone_isManagedProfile_shouldSetRingtone() {
+        mRingtoneUri = Uri.parse("content://" + OTHER_USER_ID + "@ringtone");
+        when(mContentResolver.getType(mRingtoneUri)).thenReturn("audio/*");
+        when(mUserManager.isSameProfileGroup(OWNER_USER_ID, OTHER_USER_ID)).thenReturn(true);
+        when(mUserManager.getProfileParent(UserHandle.of(OTHER_USER_ID))).thenReturn(
+                UserHandle.of(OWNER_USER_ID));
+        when(mUserManager.isManagedProfile(OTHER_USER_ID)).thenReturn(true);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
+
+    @Test
+    public void onSaveRingtone_defaultUri_shouldSetRingtone() {
+        mRingtoneUri = Uri.parse("default_ringtone");
+        when(mDefaultRingtonePreference.isDefaultRingtone(any(Uri.class))).thenReturn(true);
+
+        mDefaultRingtonePreference.onSaveRingtone(mRingtoneUri);
+
+        verify(mDefaultRingtonePreference).setActualDefaultRingtoneUri(mRingtoneUri);
+    }
 }