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

Commit 421dccd5 authored by Tom O'Neill's avatar Tom O'Neill
Browse files

Restrict Settings injection to system-signed apps

- Also: reload the injected settings status values on mode changes

- b/10461474

Change-Id: I58c817ab8ab253aa19fa02c3cb511f26c807dc2a
parent f1e0c65e
Loading
Loading
Loading
Loading
+40 −22
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.LocationManager;
import android.location.SettingInjectorService;
import android.os.Bundle;
import android.preference.Preference;
@@ -165,9 +166,42 @@ public class LocationSettings extends LocationSettingsBase
            categoryRecentLocationRequests.addPreference(banner);
        }

        addAppSettings(activity, root);

        // Only show the master switch when we're not in multi-pane mode, and not being used as
        // Setup Wizard.
        if (activity.onIsHidingHeaders() || !activity.onIsMultiPane()) {
            final int padding = activity.getResources().getDimensionPixelSize(
                    R.dimen.action_bar_switch_padding);
            mSwitch.setPaddingRelative(0, 0, padding, 0);
            activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
                    ActionBar.DISPLAY_SHOW_CUSTOM);
            activity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams(
                    ActionBar.LayoutParams.WRAP_CONTENT,
                    ActionBar.LayoutParams.WRAP_CONTENT,
                    Gravity.CENTER_VERTICAL | Gravity.END));
        }

        setHasOptionsMenu(true);

        refreshLocationMode();
        return root;
    }

    /**
     * Add the settings injected by external apps into the "App Settings" category. Hides the
     * category if there are no injected settings.
     *
     * Reloads the settings whenever receives
     * {@link SettingInjectorService#ACTION_INJECTED_SETTING_CHANGED}. As a safety measure,
     * also reloads on {@link LocationManager#MODE_CHANGED_ACTION} to ensure the settings are
     * up-to-date after mode changes even if an affected app doesn't send the setting changed
     * broadcast.
     */
    private void addAppSettings(Context context, PreferenceScreen root) {
        PreferenceCategory categoryAppSettings =
                (PreferenceCategory) root.findPreference(KEY_APP_SETTINGS);
        final SettingsInjector injector = new SettingsInjector(activity);
        final SettingsInjector injector = new SettingsInjector(context);
        List<Preference> appSettings = injector.getInjectedSettings();

        mReceiver = new BroadcastReceiver() {
@@ -179,8 +213,11 @@ public class LocationSettings extends LocationSettingsBase
                injector.reloadStatusMessages();
            }
        };
        activity.registerReceiver(mReceiver,
                new IntentFilter(SettingInjectorService.ACTION_INJECTED_SETTING_CHANGED));

        IntentFilter filter = new IntentFilter();
        filter.addAction(SettingInjectorService.ACTION_INJECTED_SETTING_CHANGED);
        filter.addAction(LocationManager.MODE_CHANGED_ACTION);
        context.registerReceiver(mReceiver, filter);

        if (appSettings.size() > 0) {
            addPreferencesSorted(appSettings, categoryAppSettings);
@@ -188,25 +225,6 @@ public class LocationSettings extends LocationSettingsBase
            // If there's no item to display, remove the whole category.
            root.removePreference(categoryAppSettings);
        }

        // Only show the master switch when we're not in multi-pane mode, and not being used as
        // Setup Wizard.
        if (activity.onIsHidingHeaders() || !activity.onIsMultiPane()) {
            final int padding = activity.getResources().getDimensionPixelSize(
                    R.dimen.action_bar_switch_padding);
            mSwitch.setPaddingRelative(0, 0, padding, 0);
            activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
                    ActionBar.DISPLAY_SHOW_CUSTOM);
            activity.getActionBar().setCustomView(mSwitch, new ActionBar.LayoutParams(
                    ActionBar.LayoutParams.WRAP_CONTENT,
                    ActionBar.LayoutParams.WRAP_CONTENT,
                    Gravity.CENTER_VERTICAL | Gravity.END));
        }

        setHasOptionsMenu(true);

        refreshLocationMode();
        return root;
    }

    @Override
+18 −8
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.settings.location;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
@@ -138,8 +139,8 @@ class SettingsInjector {
    }

    /**
     * Parses {@link InjectedSetting} from the attributes of the
     * {@link SettingInjectorService#META_DATA_NAME} tag.
     * Returns the settings parsed from the attributes of the
     * {@link SettingInjectorService#META_DATA_NAME} tag, or null.
     *
     * Duplicates some code from {@link android.content.pm.RegisteredServicesCache}.
     */
@@ -147,6 +148,15 @@ class SettingsInjector {
            throws XmlPullParserException, IOException {

        ServiceInfo si = service.serviceInfo;
        ApplicationInfo ai = si.applicationInfo;

        if ((ai.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
            if (Log.isLoggable(TAG, Log.WARN)) {
                Log.w(TAG, "Ignoring attempt to inject setting from app not in system image: "
                        + service);
                return null;
            }
        }

        XmlResourceParser parser = null;
        try {
@@ -169,7 +179,7 @@ class SettingsInjector {
                        + SettingInjectorService.ATTRIBUTES_NAME + " tag");
            }

            Resources res = pm.getResourcesForApplication(si.applicationInfo);
            Resources res = pm.getResourcesForApplication(ai);
            return parseAttributes(si.packageName, si.name, res, attrs);
        } catch (PackageManager.NameNotFoundException e) {
            throw new XmlPullParserException(
@@ -191,17 +201,17 @@ class SettingsInjector {
        try {
            // Note that to help guard against malicious string injection, we do not allow dynamic
            // specification of the label (setting title)
            final String label = sa.getString(android.R.styleable.SettingInjectorService_title);
            final int iconId = sa.getResourceId(
                    android.R.styleable.SettingInjectorService_icon, 0);
            final String title = sa.getString(android.R.styleable.SettingInjectorService_title);
            final int iconId =
                    sa.getResourceId(android.R.styleable.SettingInjectorService_icon, 0);
            final String settingsActivity =
                    sa.getString(android.R.styleable.SettingInjectorService_settingsActivity);
            if (Log.isLoggable(TAG, Log.DEBUG)) {
                Log.d(TAG, "parsed label: " + label + ", iconId: " + iconId
                Log.d(TAG, "parsed title: " + title + ", iconId: " + iconId
                        + ", settingsActivity: " + settingsActivity);
            }
            return InjectedSetting.newInstance(packageName, className,
                    label, iconId, settingsActivity);
                    title, iconId, settingsActivity);
        } finally {
            sa.recycle();
        }