Loading res/values/styles.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading src/com/android/settings/accounts/AccountTypePreferenceLoader.java +55 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ import android.os.UserHandle; 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; Loading @@ -45,6 +49,8 @@ import com.android.settings.utils.LocalClassLoaderContextThemeWrapper; 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. Loading Loading @@ -82,6 +88,7 @@ public class AccountTypePreferenceLoader { 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 Loading @@ -97,6 +104,12 @@ public class AccountTypePreferenceLoader { 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); Loading Loading @@ -181,6 +194,48 @@ public class AccountTypePreferenceLoader { } } // 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 Loading src/com/android/settings/homepage/SettingsHomepageActivity.java +11 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,17 @@ public class SettingsHomepageActivity extends FragmentActivity implements @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); mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this); mIsTwoPaneLastTime = ActivityEmbeddingUtils.isTwoPaneResolution(this); Loading src/com/android/settings/search/SearchFeatureProvider.java +1 −1 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ public interface SearchFeatureProvider { * @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; /** Loading src/com/android/settings/search/SearchFeatureProviderImpl.java +8 −10 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -32,21 +33,18 @@ import com.android.settingslib.search.SearchIndexableResourcesMobile; */ 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; } Loading Loading
res/values/styles.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading
src/com/android/settings/accounts/AccountTypePreferenceLoader.java +55 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ import android.os.UserHandle; 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; Loading @@ -45,6 +49,8 @@ import com.android.settings.utils.LocalClassLoaderContextThemeWrapper; 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. Loading Loading @@ -82,6 +88,7 @@ public class AccountTypePreferenceLoader { 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 Loading @@ -97,6 +104,12 @@ public class AccountTypePreferenceLoader { 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); Loading Loading @@ -181,6 +194,48 @@ public class AccountTypePreferenceLoader { } } // 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 Loading
src/com/android/settings/homepage/SettingsHomepageActivity.java +11 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,17 @@ public class SettingsHomepageActivity extends FragmentActivity implements @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); mIsEmbeddingActivityEnabled = ActivityEmbeddingUtils.isEmbeddingActivityEnabled(this); mIsTwoPaneLastTime = ActivityEmbeddingUtils.isTwoPaneResolution(this); Loading
src/com/android/settings/search/SearchFeatureProvider.java +1 −1 Original line number Diff line number Diff line Loading @@ -56,7 +56,7 @@ public interface SearchFeatureProvider { * @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; /** Loading
src/com/android/settings/search/SearchFeatureProviderImpl.java +8 −10 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -32,21 +33,18 @@ import com.android.settingslib.search.SearchIndexableResourcesMobile; */ 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; } Loading