Merge "Undeprecate injector API to allow change summary"
This commit is contained in:
@@ -22866,9 +22866,10 @@ package android.location {
|
||||
ctor public SettingInjectorService(java.lang.String);
|
||||
method public final android.os.IBinder onBind(android.content.Intent);
|
||||
method protected abstract boolean onGetEnabled();
|
||||
method protected abstract deprecated java.lang.String onGetSummary();
|
||||
method protected abstract java.lang.String onGetSummary();
|
||||
method public final void onStart(android.content.Intent, int);
|
||||
method public final int onStartCommand(android.content.Intent, int, int);
|
||||
method public static final void refreshSettings(android.content.Context);
|
||||
field public static final java.lang.String ACTION_INJECTED_SETTING_CHANGED = "android.location.InjectedSettingChanged";
|
||||
field public static final java.lang.String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService";
|
||||
field public static final java.lang.String ATTRIBUTES_NAME = "injected-location-setting";
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.location;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -26,7 +27,7 @@ import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* Dynamically specifies the enabled status of a preference injected into
|
||||
* Dynamically specifies the summary (subtitle) and enabled status of a preference injected into
|
||||
* the list of app settings displayed by the system settings app
|
||||
* <p/>
|
||||
* For use only by apps that are included in the system image, for preferences that affect multiple
|
||||
@@ -71,12 +72,13 @@ import android.util.Log;
|
||||
* </ul>
|
||||
*
|
||||
* To ensure a good user experience, your {@link android.app.Application#onCreate()},
|
||||
* and {@link #onGetEnabled()} methods must all be fast. If either is slow,
|
||||
* it can delay the display of settings values for other apps as well. Note further that these
|
||||
* methods are called on your app's UI thread.
|
||||
* {@link #onGetSummary()}, and {@link #onGetEnabled()} methods must all be fast. If any are slow,
|
||||
* it can delay the display of settings values for other apps as well. Note further that all are
|
||||
* called on your app's UI thread.
|
||||
* <p/>
|
||||
* For compactness, only one copy of a given setting should be injected. If each account has a
|
||||
* distinct value for the setting, then only {@code settingsActivity} should display the value for
|
||||
* distinct value for the setting, then the {@link #onGetSummary()} value should represent a summary
|
||||
* of the state across all of the accounts and {@code settingsActivity} should display the value for
|
||||
* each account.
|
||||
*/
|
||||
public abstract class SettingInjectorService extends Service {
|
||||
@@ -107,6 +109,14 @@ public abstract class SettingInjectorService extends Service {
|
||||
public static final String ACTION_INJECTED_SETTING_CHANGED =
|
||||
"android.location.InjectedSettingChanged";
|
||||
|
||||
/**
|
||||
* Name of the bundle key for the string specifying the summary for the setting (e.g., "ON" or
|
||||
* "OFF").
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String SUMMARY_KEY = "summary";
|
||||
|
||||
/**
|
||||
* Name of the bundle key for the string specifying whether the setting is currently enabled.
|
||||
*
|
||||
@@ -150,36 +160,41 @@ public abstract class SettingInjectorService extends Service {
|
||||
}
|
||||
|
||||
private void onHandleIntent(Intent intent) {
|
||||
|
||||
boolean enabled;
|
||||
String summary = null;
|
||||
boolean enabled = false;
|
||||
try {
|
||||
summary = onGetSummary();
|
||||
enabled = onGetEnabled();
|
||||
} catch (RuntimeException e) {
|
||||
// Exception. Send status anyway, so that settings injector can immediately start
|
||||
// loading the status of the next setting.
|
||||
sendStatus(intent, true);
|
||||
throw e;
|
||||
} finally {
|
||||
// If exception happens, send status anyway, so that settings injector can immediately
|
||||
// start loading the status of the next setting. But leave the exception uncaught to
|
||||
// crash the injector service itself.
|
||||
sendStatus(intent, summary, enabled);
|
||||
}
|
||||
|
||||
sendStatus(intent, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the enabled values back to the caller via the messenger encoded in the
|
||||
* intent.
|
||||
*/
|
||||
private void sendStatus(Intent intent, boolean enabled) {
|
||||
private void sendStatus(Intent intent, String summary, boolean enabled) {
|
||||
Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY);
|
||||
// Bail out to avoid crashing GmsCore with incoming malicious Intent.
|
||||
if (messenger == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Message message = Message.obtain();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(SUMMARY_KEY, summary);
|
||||
bundle.putBoolean(ENABLED_KEY, enabled);
|
||||
message.setData(bundle);
|
||||
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, mName + ": received " + intent
|
||||
Log.d(TAG, mName + ": received " + intent + ", summary=" + summary
|
||||
+ ", enabled=" + enabled + ", sending message: " + message);
|
||||
}
|
||||
|
||||
Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY);
|
||||
try {
|
||||
messenger.send(message);
|
||||
} catch (RemoteException e) {
|
||||
@@ -188,14 +203,12 @@ public abstract class SettingInjectorService extends Service {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is no longer called, because status values are no longer shown for any injected
|
||||
* setting.
|
||||
* Returns the {@link android.preference.Preference#getSummary()} value (allowed to be null or
|
||||
* empty). Should not perform unpredictably-long operations such as network access--see the
|
||||
* running-time comments in the class-level javadoc.
|
||||
*
|
||||
* @return ignored
|
||||
*
|
||||
* @deprecated not called any more
|
||||
* @return the {@link android.preference.Preference#getSummary()} value
|
||||
*/
|
||||
@Deprecated
|
||||
protected abstract String onGetSummary();
|
||||
|
||||
/**
|
||||
@@ -217,4 +230,12 @@ public abstract class SettingInjectorService extends Service {
|
||||
* @return the {@link android.preference.Preference#isEnabled()} value
|
||||
*/
|
||||
protected abstract boolean onGetEnabled();
|
||||
|
||||
/**
|
||||
* Sends a broadcast to refresh the injected settings on location settings page.
|
||||
*/
|
||||
public static final void refreshSettings(Context context) {
|
||||
Intent intent = new Intent(ACTION_INJECTED_SETTING_CHANGED);
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,6 +831,10 @@
|
||||
<!-- Toast message shown when setting a new local backup password fails due to the user not supplying the correct existing password. The phrasing here is deliberately quite general. [CHAR LIMIT=80] -->
|
||||
<string name="local_backup_password_toast_validation_failure">Failure setting backup password</string>
|
||||
|
||||
<!-- [CHAR LIMIT=30] Location mode screen, temporary summary text to show as the status of a location
|
||||
setting injected by an external app while the app is being queried for the actual value -->
|
||||
<string name="loading_injected_setting_summary">Loading\u2026</string>
|
||||
|
||||
<!-- Name of each color mode for the display. [CHAR LIMIT=40] -->
|
||||
<string-array name="color_mode_names">
|
||||
<item>Vibrant (default)</item>
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.os.Messenger;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.Log;
|
||||
@@ -44,11 +45,16 @@ import android.util.Xml;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settingslib.R;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -102,7 +108,7 @@ public class SettingsInjector {
|
||||
public SettingsInjector(Context context) {
|
||||
mContext = context;
|
||||
mSettings = new HashSet<Setting>();
|
||||
mHandler = new StatusLoadingHandler();
|
||||
mHandler = new StatusLoadingHandler(mSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,7 +171,7 @@ public class SettingsInjector {
|
||||
Log.e(TAG, "Can't get ApplicationInfo for " + setting.packageName, e);
|
||||
}
|
||||
preference.setTitle(setting.title);
|
||||
preference.setSummary(null);
|
||||
preference.setSummary(R.string.loading_injected_setting_summary);
|
||||
preference.setIcon(appIcon);
|
||||
preference.setOnPreferenceClickListener(new ServiceSettingClickedListener(setting));
|
||||
}
|
||||
@@ -180,6 +186,7 @@ public class SettingsInjector {
|
||||
final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||
final List<UserHandle> profiles = um.getUserProfiles();
|
||||
ArrayList<Preference> prefs = new ArrayList<>();
|
||||
mSettings.clear();
|
||||
for (UserHandle userHandle : profiles) {
|
||||
if (profileId == UserHandle.USER_CURRENT || profileId == userHandle.getIdentifier()) {
|
||||
Iterable<InjectedSetting> settings = getSettings(userHandle);
|
||||
@@ -363,31 +370,28 @@ public class SettingsInjector {
|
||||
* SettingInjectorService}, so to reduce memory pressure we don't want to load too many at
|
||||
* once.
|
||||
*/
|
||||
private final class StatusLoadingHandler extends Handler {
|
||||
private static final class StatusLoadingHandler extends Handler {
|
||||
/**
|
||||
* References all the injected settings.
|
||||
*/
|
||||
WeakReference<Set<Setting>> mAllSettings;
|
||||
|
||||
/**
|
||||
* Settings whose status values need to be loaded. A set is used to prevent redundant loads.
|
||||
*/
|
||||
private Set<Setting> mSettingsToLoad = new HashSet<Setting>();
|
||||
private Deque<Setting> mSettingsToLoad = new ArrayDeque<Setting>();
|
||||
|
||||
/**
|
||||
* Settings that are being loaded now and haven't timed out. In practice this should have
|
||||
* zero or one elements.
|
||||
*/
|
||||
private Set<Setting> mSettingsBeingLoaded = new HashSet<Setting>();
|
||||
private Set<Setting> mSettingsBeingLoaded = new ArraySet<Setting>();
|
||||
|
||||
/**
|
||||
* Settings that are being loaded but have timed out. If only one setting has timed out, we
|
||||
* will go ahead and start loading the next setting so that one slow load won't delay the
|
||||
* load of the other settings.
|
||||
*/
|
||||
private Set<Setting> mTimedOutSettings = new HashSet<Setting>();
|
||||
|
||||
private boolean mReloadRequested;
|
||||
|
||||
private StatusLoadingHandler() {
|
||||
public StatusLoadingHandler(Set<Setting> allSettings) {
|
||||
super(Looper.getMainLooper());
|
||||
mAllSettings = new WeakReference<>(allSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
@@ -396,20 +400,24 @@ public class SettingsInjector {
|
||||
|
||||
// Update state in response to message
|
||||
switch (msg.what) {
|
||||
case WHAT_RELOAD:
|
||||
mReloadRequested = true;
|
||||
case WHAT_RELOAD: {
|
||||
final Set<Setting> allSettings = mAllSettings.get();
|
||||
if (allSettings != null) {
|
||||
// Reload requested, so must reload all settings
|
||||
mSettingsToLoad.clear();
|
||||
mSettingsToLoad.addAll(allSettings);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WHAT_RECEIVED_STATUS:
|
||||
final Setting receivedSetting = (Setting) msg.obj;
|
||||
receivedSetting.maybeLogElapsedTime();
|
||||
mSettingsBeingLoaded.remove(receivedSetting);
|
||||
mTimedOutSettings.remove(receivedSetting);
|
||||
removeMessages(WHAT_TIMEOUT, receivedSetting);
|
||||
break;
|
||||
case WHAT_TIMEOUT:
|
||||
final Setting timedOutSetting = (Setting) msg.obj;
|
||||
mSettingsBeingLoaded.remove(timedOutSetting);
|
||||
mTimedOutSettings.add(timedOutSetting);
|
||||
if (Log.isLoggable(TAG, Log.WARN)) {
|
||||
Log.w(TAG, "Timed out after " + timedOutSetting.getElapsedTime()
|
||||
+ " millis trying to get status for: " + timedOutSetting);
|
||||
@@ -421,37 +429,22 @@ public class SettingsInjector {
|
||||
|
||||
// Decide whether to load additional settings based on the new state. Start by seeing
|
||||
// if we have headroom to load another setting.
|
||||
if (mSettingsBeingLoaded.size() > 0 || mTimedOutSettings.size() > 1) {
|
||||
if (mSettingsBeingLoaded.size() > 0) {
|
||||
// Don't load any more settings until one of the pending settings has completed.
|
||||
// To reduce memory pressure, we want to be loading at most one setting (plus at
|
||||
// most one timed-out setting) at a time. This means we'll be responsible for
|
||||
// bringing in at most two services.
|
||||
// To reduce memory pressure, we want to be loading at most one setting.
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "too many services already live for " + msg + ", " + this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mReloadRequested && mSettingsToLoad.isEmpty() && mSettingsBeingLoaded.isEmpty()
|
||||
&& mTimedOutSettings.isEmpty()) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "reloading because idle and reload requesteed " + msg + ", " + this);
|
||||
}
|
||||
// Reload requested, so must reload all settings
|
||||
mSettingsToLoad.addAll(mSettings);
|
||||
mReloadRequested = false;
|
||||
}
|
||||
|
||||
// Remove the next setting to load from the queue, if any
|
||||
Iterator<Setting> iter = mSettingsToLoad.iterator();
|
||||
if (!iter.hasNext()) {
|
||||
if (mSettingsToLoad.isEmpty()) {
|
||||
if (Log.isLoggable(TAG, Log.VERBOSE)) {
|
||||
Log.v(TAG, "nothing left to do for " + msg + ", " + this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Setting setting = iter.next();
|
||||
iter.remove();
|
||||
Setting setting = mSettingsToLoad.removeFirst();
|
||||
|
||||
// Request the status value
|
||||
setting.startService();
|
||||
@@ -473,21 +466,48 @@ public class SettingsInjector {
|
||||
return "StatusLoadingHandler{" +
|
||||
"mSettingsToLoad=" + mSettingsToLoad +
|
||||
", mSettingsBeingLoaded=" + mSettingsBeingLoaded +
|
||||
", mTimedOutSettings=" + mTimedOutSettings +
|
||||
", mReloadRequested=" + mReloadRequested +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private static class MessengerHandler extends Handler {
|
||||
private WeakReference<Setting> mSettingRef;
|
||||
private Handler mHandler;
|
||||
|
||||
public MessengerHandler(Setting setting, Handler handler) {
|
||||
mSettingRef = new WeakReference(setting);
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
final Setting setting = mSettingRef.get();
|
||||
if (setting == null) {
|
||||
return;
|
||||
}
|
||||
final Preference preference = setting.preference;
|
||||
Bundle bundle = msg.getData();
|
||||
boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true);
|
||||
String summary = bundle.getString(SettingInjectorService.SUMMARY_KEY, null);
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle);
|
||||
}
|
||||
preference.setSummary(summary);
|
||||
preference.setEnabled(enabled);
|
||||
mHandler.sendMessage(
|
||||
mHandler.obtainMessage(WHAT_RECEIVED_STATUS, setting));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an injected setting and the corresponding preference.
|
||||
*/
|
||||
protected final class Setting {
|
||||
|
||||
public final InjectedSetting setting;
|
||||
public final Preference preference;
|
||||
public long startMillis;
|
||||
|
||||
|
||||
public Setting(InjectedSetting setting, Preference preference) {
|
||||
this.setting = setting;
|
||||
this.preference = preference;
|
||||
@@ -501,20 +521,6 @@ public class SettingsInjector {
|
||||
'}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if they both have the same {@link #setting} value. Ignores mutable
|
||||
* {@link #preference} and {@link #startMillis} so that it's safe to use in sets.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this == o || o instanceof Setting && setting.equals(((Setting) o).setting);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return setting.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the service to fetch for the current status for the setting, and updates the
|
||||
* preference when the service replies.
|
||||
@@ -529,20 +535,7 @@ public class SettingsInjector {
|
||||
}
|
||||
return;
|
||||
}
|
||||
Handler handler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
Bundle bundle = msg.getData();
|
||||
boolean enabled = bundle.getBoolean(SettingInjectorService.ENABLED_KEY, true);
|
||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
||||
Log.d(TAG, setting + ": received " + msg + ", bundle: " + bundle);
|
||||
}
|
||||
preference.setSummary(null);
|
||||
preference.setEnabled(enabled);
|
||||
mHandler.sendMessage(
|
||||
mHandler.obtainMessage(WHAT_RECEIVED_STATUS, Setting.this));
|
||||
}
|
||||
};
|
||||
Handler handler = new MessengerHandler(this, mHandler);
|
||||
Messenger messenger = new Messenger(handler);
|
||||
|
||||
Intent intent = setting.getServiceIntent();
|
||||
|
||||
Reference in New Issue
Block a user