diff --git a/src/com/android/settings/location/LocationSettings.java b/src/com/android/settings/location/LocationSettings.java index 6dceea955b7..def5af87604 100644 --- a/src/com/android/settings/location/LocationSettings.java +++ b/src/com/android/settings/location/LocationSettings.java @@ -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,29 +166,7 @@ public class LocationSettings extends LocationSettingsBase categoryRecentLocationRequests.addPreference(banner); } - PreferenceCategory categoryAppSettings = - (PreferenceCategory) root.findPreference(KEY_APP_SETTINGS); - final SettingsInjector injector = new SettingsInjector(activity); - List appSettings = injector.getInjectedSettings(); - - mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, "Received settings change intent: " + intent); - } - injector.reloadStatusMessages(); - } - }; - activity.registerReceiver(mReceiver, - new IntentFilter(SettingInjectorService.ACTION_INJECTED_SETTING_CHANGED)); - - if (appSettings.size() > 0) { - addPreferencesSorted(appSettings, categoryAppSettings); - } else { - // If there's no item to display, remove the whole category. - root.removePreference(categoryAppSettings); - } + addAppSettings(activity, root); // Only show the master switch when we're not in multi-pane mode, and not being used as // Setup Wizard. @@ -209,6 +188,45 @@ public class LocationSettings extends LocationSettingsBase 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(context); + List appSettings = injector.getInjectedSettings(); + + mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, "Received settings change intent: " + intent); + } + injector.reloadStatusMessages(); + } + }; + + 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); + } else { + // If there's no item to display, remove the whole category. + root.removePreference(categoryAppSettings); + } + } + @Override public int getHelpResource() { return R.string.help_url_location_access; diff --git a/src/com/android/settings/location/SettingsInjector.java b/src/com/android/settings/location/SettingsInjector.java index 5929466537b..12bf38fecd0 100644 --- a/src/com/android/settings/location/SettingsInjector.java +++ b/src/com/android/settings/location/SettingsInjector.java @@ -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(); }