If you are a ROM developer/maintainer, you maybe need to add some setting item to the official settings app for your custom configuration. For example, I need to add switch for user to enable/disable multi-window and BoringdroidSystemUI based on his/her need. If I add those switch to official settings app, I will manage the fork of official settings app, and apply patches when I upgrade based AOSP version, for example, upgrade from Android 10 to Android 11. So if there is a mechanism to plugin custom settings app to official settings app, it will help to release myself from annoying patch work. Fortunately, the official settings app provide a mechanism called for EXTRA_SETTINGS to help us to make it come true. The following first diagram is the official settings app dashboard page, and it loads the BoringdroidSettings dynamically, and the second diagram is the result after clicking the BoringdroidSettings dashboard entry, the shown BoringdroidSettings app page.

`BoringdroidSettings` entry in official settings app dashboard

`BoringdroidSettings` settings app page

Code base

AOSP 10.0

Configure custom settings app with EXTRA_SETTINGS

It is very simple, the following code snippet is the example of BoringdroidSettings.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    coreApp="true"
    package="com.boringdroid.settings"
    android:sharedUserId="android.uid.system">

    <application
        android:name=".BoringdroidSettingsApplication"
        android:allowBackup="false"
        android:defaultToDeviceProtectedStorage="true"
        android:hardwareAccelerated="true"
        android:icon="@drawable/ic_icon"
        android:label="@string/ic_name"
        android:requiredForAllUsers="true"
        android:supportsRtl="true"
        android:taskAffinity=""
        android:theme="@style/Theme.Settings"
        android:usesCleartextTraffic="true">

        <activity
            android:name=".BoringdroidSettings"
            android:exported="true"
            android:icon="@drawable/ic_icon"
            android:label="@string/ic_name">
            <intent-filter>
                <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
            </intent-filter>

            <meta-data
                android:name="com.android.settings.category"
                android:value="com.android.settings.category.ia.homepage" />
            <meta-data
                android:name="com.android.settings.title"
                android:value="@string/boringdorid_dashboard_title" />
            <meta-data
                android:name="com.android.settings.summary"
                android:value="@string/boringdorid_dashboard_summary" />
        </activity>
    </application>
</manifest>

Firstly, we should add EXTRA_SETTINGS intent-filter for dashboard entry activity:

<intent-filter>
    <action android:name="com.android.settings.action.EXTRA_SETTINGS" />
</intent-filter>

And then we should add critical meta-data list for dashboard entry activity:

<meta-data
    android:name="com.android.settings.category"
    android:value="com.android.settings.category.ia.homepage" />
<meta-data
    android:name="com.android.settings.title"
    android:value="@string/boringdorid_dashboard_title" />
<meta-data
    android:name="com.android.settings.summary"
    android:value="@string/boringdorid_dashboard_summary" />

We must set com.android.settings.category with com.android.settings.category.ia.homepage to tell the official settings app, the activity wants to be added to dashboard. From the code, we also can use different category values to add activity to other sub page, but I don’t check it, if you need, you can try it.

We also should set com.android.settings.title and com.android.settings.summary to customize your shown title and summary.

The next step is to set android:icon="@drawable/ic_icon" for the activity that will be shown on dashboard.

The last what we should do is the set coreApp and shareUid, that the official settings will check.

After above steps, our custom activity will shown on official settings app dashboard page, and will be started after the user clicks it.

Develop custom settings app

If we can get system sign key, we can use gradle to develop custom settings app, and use Android Studio to build and install to our emulator or other physical devices without key problem. What we should do is to import androidx or other preference libraries to develop settings app. The BoringdroidSettings uses this way to develop normal functions. If you want to use Android.mk or Android.bp as build tool, we also can use androidx preference libraries to develop custom settings app, because the AOSP has those prebuilt libraries. The BoringdroidSettings also supports Android.bp for Boringdroid system build. Another example is MaruSettings.

How does EXTRA_SETTINGS work?

com.android.settingslib.drawer.TileUtils defines the EXTRA_SETTINGS:

/**
 * Settings will search for system activities of this action and add them as a top level
 * settings tile using the following parameters.
 *
 * <p>A category must be specified in the meta-data for the activity named
 * {@link #EXTRA_CATEGORY_KEY}
 *
 * <p>The title may be defined by meta-data named {@link #META_DATA_PREFERENCE_TITLE}
 * otherwise the label for the activity will be used.
 *
 * <p>The icon may be defined by meta-data named {@link #META_DATA_PREFERENCE_ICON}
 * otherwise the icon for the activity will be used.
 *
 * <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY}
 */
public static final String EXTRA_SETTINGS_ACTION = "com.android.settings.action.EXTRA_SETTINGS";

From the comment, we know it is used to search system activities, that will be added as a top level settings tile. The comment also describe the meta-datas to set title/icon/summary, what we set above.

The TileUtils.getCategories retrieves all tiles with specific actions, including EXTRA_SETTINGS. It calls the TileUtils.getTilesForAction to retrieve the EXTRA_SETTINGS meta-datas.

@VisibleForTesting
static void getTilesForAction(Context context,
        UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,
        String defaultCategory, List<Tile> outTiles, boolean requireSettings) {
    final Intent intent = new Intent(action);
    if (requireSettings) {
        intent.setPackage(SETTING_PKG);
    }
    final PackageManager pm = context.getPackageManager();
    List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
            PackageManager.GET_META_DATA, user.getIdentifier());
    for (ResolveInfo resolved : results) {
        if (!resolved.system) {
            // Do not allow any app to add to settings, only system ones.
            continue;
        }
        ActivityInfo activityInfo = resolved.activityInfo;
        Bundle metaData = activityInfo.metaData;
        String categoryKey = defaultCategory;

        // Load category
        if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))
                && categoryKey == null) {
                Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
                    + intent + " missing metadata "
                    + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
            continue;
        } else {
            categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
        }
        // other code
    }
}

It skips non-system processes:

if (!resolved.system) {
    // Do not allow any app to add to settings, only system ones.
    continue;
}

So we should add shareUid to share system process.

It also skip component without EXTRA_CATEGORY_KEY meta-data, so we also must set it.

In com.android.settings.dashboard.DashboardFragment, it has multi entries to com.android.settings.dashboard.DashboardFeatureProviderImpl.getTilesForCategory to get tiles with specific category key. Different dashboard fragment has different key, for example, the top level dashboard of official settings app use the key com.android.settings.category.ia.homepage, defined by com.android.settingslib.drawer.CategoryKey.CATEGORY_HOMEPAGE. The relationship between key and dashboard fragment instance is defined by com.android.settings.dashboard.DashboardFragmentRegistry:

static {
    PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
    PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
            CategoryKey.CATEGORY_HOMEPAGE);
    PARENT_TO_CATEGORY_KEY_MAP.put(
            NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
    // other code
}

And the above code also tells use that we can change category key value to add custom setting activity to specific sub dashboard, such as system dashboard.

After that, the DashboardFragment will use Tile to show all items with the same category key. The left things are normal clicking processing, if you want to learn it, just reading the code.

Summary

With the EXTRA_SETTINGS, we can let official settings app to plugin our custom settings app into it very simple. And we can focus on develop custom settings content, and get rid of porting work when upgrading based AOSP version. And it supports to add different type setting activity to different dashboard page, such as top level, system, network, and etc. If you want to arrange settings item for official settings app, it is not suitable for you. And it is useful for people only wants to add custom setting items to official settings app.

That’s all. Enjoy it.